1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
//! `tm sm serve --stdio` — the SM JSON-RPC 2.0 over STDIO adapter (DOC-14 §1A.1).
//!
//! Why: the SM's PRIMARY, API-first interface (epic #1283, SM-STDIO #1291). A
//! parent `claude-mpm`/PM drives the SM headlessly over newline-delimited JSON-RPC
//! to exercise EVERY capability — chat, goals, session launch/observe/verify,
//! context, health — with no UI (the §1A.2 test topology
//! `claude-mpm ⟷ SM ⟷ t-mpm`). The adapter is a THIN mapping: each method maps
//! onto an existing surface — `SessionManagerAgent::chat` (SM-7), the goal store
//! (SM-6), the rolling-context engine (SM-5), the provider/health surface
//! (SM-2/§5.3), and the managed-session control surface (§2.6). No business logic
//! lives in the transport.
//!
//! STDOUT DISCIPLINE (CRITICAL): stdout is reserved EXCLUSIVELY for JSON-RPC
//! framing (one response object per line). EVERY diagnostic goes to stderr via
//! `tracing`. There is ZERO `println!`/`print!` in this module tree or the SM
//! code path it touches — `tests::no_stdout_writes_in_sm_paths` greps the source
//! and asserts that mechanically.
//!
//! What: [`SmDispatcher`] owns the SM surfaces and exposes the transport-neutral
//! [`SmDispatcher::dispatch`] (request → response). [`run_sm_stdio`] builds the
//! dispatcher from the daemon state and drives the shared
//! [`trusty_common::mcp::run_stdio_loop`] (line framing + stderr-only logging),
//! so the wire framing is the SAME proven loop trusty-memory/trusty-search use.
//! Test: `tests.rs` — each of the 14 methods round-trips with correct JSON-RPC
//! framing, plus parse-error / method-not-found / stdout-cleanliness / scripted
//! sequence coverage.
use PathBuf;
use Arc;
use ;
use crate;
pub use ;
use Arc as StdArc;
use Mutex;
use crateSmGoalStore;
/// The shared goal-store handle `sm.goals.*` operate over (feature-gated type).
///
/// Why: [`SmDispatcher::new`] takes ONE signature across both `sm-memory` and
/// the default build (a fragile dual-arity API was the prior shape). The goal
/// store only exists under `sm-memory`, so the constructor accepts this handle
/// as an `Option` that is simply always `None` in the no-memory build — gating
/// the TYPE, never the arity. Under `sm-memory` it is the live store behind a
/// mutex (the `&mut self` goal mutators serialise through it).
/// What: an `Arc<Mutex<SmGoalStore>>` under the feature; an uninhabited
/// `std::convert::Infallible` placeholder without it (never constructed —
/// callers pass `None`).
/// Test: `tests.rs::dispatcher_with` constructs `Some(..)`/`None` via this alias.
pub type SmGoalHandle = ;
/// Placeholder goal-store handle for the no-memory build (never constructed).
///
/// Why: lets [`SmDispatcher::new`] keep ONE signature without referencing the
/// `sm-memory`-only [`SmGoalStore`]. Callers in the default build always pass
/// `None`, so no value of this type is ever created.
/// What: a zero-sized uninhabitable-by-convention marker.
/// Test: compile-only; the no-memory `new` ignores its `Option<SmGoalHandle>`.
pub type SmGoalHandle = Infallible;
/// The transport-neutral SM dispatcher the stdio adapter drives (§1A.1).
///
/// Why: separating the dispatcher from the stdio loop is what makes the 14
/// methods HERMETICALLY testable — the round-trip tests construct an
/// `SmDispatcher` over mocks and call [`SmDispatcher::dispatch`] with constructed
/// JSON requests, with no real stdin/stdout, no network, and no tmux. The
/// dispatcher owns one handle per SM surface and does pure translation.
/// What: the SM [`SessionManagerAgent`] (chat + health), a config snapshot + the
/// `data_root` for opening the per-`conv_id` context engine (`sm.context.get`),
/// the [`SessionControl`] seam for `sm.sessions.*`, and — under `sm-memory` — the
/// shared [`SmGoalStore`] for `sm.goals.*`. Without the feature the goal store is
/// absent and `sm.goals.*`/`sm.context.get` return a graceful JSON-RPC error.
/// Test: `tests.rs` builds this over the mock resolver + mock session control.
/// Run the SM JSON-RPC stdio adapter to EOF (the `tm sm serve --stdio` entry).
///
/// Why: the process entry point. It builds the [`SmDispatcher`] from the daemon
/// state (reusing the SM-7 agent construction and the managed-session surface),
/// then drives the shared newline-delimited JSON-RPC loop on stdin/stdout. Logs
/// go to stderr only (the loop never writes anything but framed responses to
/// stdout), so a parent driver gets a clean channel.
/// What: constructs the agent + control + (feature-gated) goal store rooted under
/// the daemon's SM data root, then runs [`run_stdio_loop`] forwarding each request
/// to [`SmDispatcher::dispatch`]. Returns `Ok(())` on stdin EOF.
/// Test: the dispatch logic is covered by `tests.rs`; this thin wiring is
/// exercised at runtime via `tm sm serve --stdio`.
pub async
/// Build the production [`SmDispatcher`] from the daemon state.
///
/// Why: isolates the wiring (which differs by the `sm-memory` feature) from the
/// loop so [`run_sm_stdio`] stays readable. The agent and control reuse the
/// daemon's already-constructed handles so the stdio surface and the HTTP/chat
/// surfaces share one core.
/// What: clones the daemon's SM agent, reads the SM config + data root, builds a
/// [`DaemonSessionControl`], and — under `sm-memory` — loads the goal store from
/// the dedicated SM palace (falling back gracefully on a palace-open failure).
/// Test: covered indirectly; the dispatch behaviour is unit-tested in `tests.rs`.
async
/// Load the SM goal store from the dedicated SM palace (`sm-memory` build).
///
/// Why: `sm.goals.*` are backed by the SM-6 dual-persistence store over the SM
/// palace. Loading rebuilds the goal map from the palace (truth) with a cache
/// fallback, so the stdio surface sees the same goals the chat surface does.
/// What: opens the SM palace under `<data_root>/palace`, wraps it as the goal
/// store's [`GoalMemory`], and `SmGoalStore::load`s it. A palace-open or load
/// failure degrades to an empty in-memory store (logged to stderr) rather than
/// failing the whole stdio surface.
/// Test: covered by the SM-6 store tests; this wiring is runtime-only.
async
/// Build an empty goal store over a no-op palace seam (degraded fallback).
///
/// Why: when the real palace is unavailable, `sm.goals.*` must still answer
/// (returning an empty list / accepting creates that simply do not durably
/// persist) rather than the whole stdio surface failing. A no-op [`GoalMemory`]
/// gives the store a seam that always succeeds with no entries.
/// What: constructs an [`SmGoalStore::new`] over a [`NoopGoalMemory`].
/// Test: runtime fallback; the store behaviour is covered by SM-6 tests.
/// A [`GoalMemory`] that persists nothing and lists nothing (degraded seam).
///
/// Why: lets the goal store operate (in-memory only) when the real SM palace is
/// unavailable, so `sm.goals.*` degrade gracefully instead of erroring out.
/// What: `remember_goal` is a no-op success; `list_goals` returns an empty vec.
/// Test: runtime fallback only.
;