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
//! `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_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 Mutex;
use crate;
/// The shared goal-store handle `sm.goals.*` and `sm.delegate` operate over (#1477).
///
/// Why: the goal store (SM-6) is feature-INDEPENDENT — it works over the
/// [`GoalMemory`](crate::core::sm::GoalMemory) seam — so `sm.goals.*` and the
/// delegation loop can operate in EVERY build. Under `sm-memory` the store is
/// backed by the durable SM palace; on the default build it is backed by a no-op
/// seam ([`NoopGoalMemory`]) so goals function in-memory (non-durable) rather than
/// returning "unavailable" (#1477). Keeping ONE handle type across both builds
/// keeps [`SmDispatcher::new`] a single signature.
/// What: an `Arc<Mutex<SmGoalStore>>` (the `&mut self` goal mutators serialise
/// through the mutex).
/// Test: `tests.rs::dispatcher_with` constructs it in both builds.
pub type SmGoalHandle = ;
/// 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 the shared [`SmGoalStore`]
/// for `sm.goals.*` + `sm.delegate`. All four surfaces are present in EVERY build
/// (#1477): the goal store is palace-backed under `sm-memory` and no-op/in-memory
/// on the default build, so no `sm.*` method returns "unavailable" purely for lack
/// of the feature (only a degraded provider makes `sm.chat`/`sm.delegate` degrade).
/// 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 the no-op/in-memory goal store for the default build (#1477).
///
/// Why: without `sm-memory` there is no palace, but the goal store is
/// feature-independent, so `sm.goals.*` and the delegation loop DEGRADE GRACEFULLY
/// to non-durable in-memory operation (backed by [`NoopGoalMemory`]) rather than
/// returning "unavailable". Durable persistence requires `--features sm-memory`.
/// What: wraps an empty [`empty_goal_store`] in the shared mutex handle.
/// Test: `tests.rs` no-feature `sm.goals.*` / `sm.delegate` branches.
async
/// Build an empty goal store over the no-op palace seam ([`NoopGoalMemory`]).
///
/// Why: gives the store a seam that always succeeds with no entries — used both as
/// the default-build backing (#1477) and as the `sm-memory` palace-unavailable
/// fallback, so `sm.goals.*` answer (empty list / non-durable creates) rather than
/// failing.
/// What: constructs an [`SmGoalStore::new`] over a [`NoopGoalMemory`].
/// Test: covered by SM-6 store tests + the no-feature dispatch tests.