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
300
301
//! Daemon summary, config, the palace roster, one palace, and drawer CRUD
//! (#6286).
//!
//! Why: these were `/api/v1/status`, `/config`, `GET /palaces`,
//! `GET /palaces/{id}` and the three drawer routes. `palace_create`,
//! `palace_update` and `palace_delete` are NOT here — they were already tool
//! methods the dispatcher routes, so folding them again would be a second
//! implementation of the same call.
//!
//! [`palaces_list`] is the one that is not a straight fold. `palace_list`, the
//! tool, answers bare ids; `GET /palaces` answered rows but with `peek`-based
//! placeholder zeros for any palace not already resident (#4640). Neither is
//! what a roster with counts needs, which is why the monitor was fanning out one
//! [`get_palace`] per id — see [`palaces_list`] and [`PalaceListRow`]. #6155
//! adds [`PalacesListParams`]`::counts`, because a caller that wants only NAMES
//! should not pay for the opens: see that type.
//!
//! What: each handler delegates to `MemoryService`, which is where the
//! behaviour lived all along; the axum extractors become one params struct.
//! Test: `super::super::uds::tests` — `rpc_status_*`, `rpc_drawer_*`,
//! `rpc_palace_get_*`, `rpc_palaces_list_*`.
use ;
use ;
use crateApiError;
use crate::;
use ;
pub use crateStatusPayload;
/// `memory.status` — daemon and palace summary.
///
/// The console header and external health tooling read this for the palace,
/// drawer and triple counts.
pub async
/// What `memory.config` reports.
///
/// The OpenRouter key itself is never included — only whether one is set.
/// `memory.config` — the current daemon configuration, minus the secret.
pub async
/// `memory.palace_get` — one palace by id.
pub async
/// One palace's row in [`palaces_list`], or why its counts are missing.
///
/// Why the error is a FIELD rather than an omitted row (#6286): the monitor's
/// predecessor — an N-call `memory.palace_get` fan-out — dropped a palace whose
/// call failed at `debug!`, so the panel could report "12 palaces" above 9 rows
/// with nothing to say which three were missing or why. A row that can carry
/// its own failure makes that impossible to reintroduce: every palace the
/// registry lists appears, and one that could not be read says so.
///
/// What: `id` is always present. Exactly one of `palace` and `error` is
/// non-null. `error` is serialised even when null so a consumer reads it
/// directly rather than testing for the key.
///
/// `palace` is nested rather than flattened because [`crate::service::PalaceInfo`]
/// carries its own `id`, and a flatten would put two spellings of the same
/// field in one object.
/// Test: `rpc_palaces_list_reports_counts_per_palace`,
/// `rpc_palaces_list_reports_an_unreadable_palace_rather_than_dropping_it`.
/// Params for [`palaces_list`] — whether the roster is worth its counts.
///
/// Why (#6155): counting is the whole cost of this method. On the operator's
/// 93-palace install one call took 8.5-11 s because every palace is opened, and
/// the console's 30 s bridge budget was exceeded often enough that
/// `/tools/memory` sat on "Loading palaces…" and the KG palace selector stayed
/// empty. A roster of NAMES needs none of that work, and `list_palaces` has
/// answered it from `PalaceRegistry::peek` — zero disk I/O — since #4637.
///
/// What: `counts` defaults to `true`, so `{}` and `null` both keep the #6286
/// behaviour. `null` is accepted because the method took [`NoParams`] before
/// this field existed.
///
/// The default stays `true` because this is a published UDS/MCP method and an
/// external caller sending `{}` asked for the #6286 contract. What changed in
/// #7125 is who sends `{}`: the two PERIODIC pollers — `trusty_common`'s
/// monitor client and trusty-mpm's health TUI — now send `counts: false`,
/// because a poll on a 2- or 5-second timer was opening every palace on disk
/// and pinning the registry's LRU at its ceiling for as long as it ran.
/// Test: `rpc_palaces_list_without_counts_does_not_open_a_cold_palace`,
/// `rpc_palaces_list_defaults_to_counting`,
/// `monitor_client_poll_leaves_closed_palaces_closed`.
/// `memory.palaces_list` — one row per palace, counted or named (#6286, #6155).
///
/// Why: this is the contract the retired `GET /api/v1/palaces` carried, with
/// the counts it could not. That route used `PalaceRegistry::peek` since #4640,
/// so it reported `cached: false` and placeholder zeros for any palace not
/// already resident — which is why the monitor fanned out one
/// `memory.palace_get` per id instead, and why the panel could disagree with
/// itself about how many palaces there are.
///
/// What: with `counts: true` (the default) it delegates to
/// `MemoryService::list_palaces_with_counts`, which opens each palace and keeps
/// per-palace failures. See that method for why opening every palace is the
/// same cost as the fan-out it replaces, not a new one. With `counts: false`
/// (#6155) it delegates to `MemoryService::list_palaces`, which peeks: a palace
/// that is not already resident is never opened, so the call is bounded by the
/// registry walk rather than by cold disk I/O. Those rows carry `cached: false`
/// and zeroed counts, which #4637 defines as UNKNOWN rather than empty; a
/// caller that wants one palace's real counts asks `memory.palace_get` for it.
/// Answers `{"palaces": [PalaceListRow, …]}` either way — an object rather than
/// a bare array so a later addition (a total, a truncation flag) is not a
/// breaking shape change.
///
/// Test: `rpc_palaces_list_reports_counts_per_palace`,
/// `rpc_palaces_list_reports_an_unreadable_palace_rather_than_dropping_it`,
/// `rpc_palaces_list_without_counts_does_not_open_a_cold_palace`,
/// `rpc_palaces_list_defaults_to_counting`.
pub async
/// Params for `memory.drawers_list`.
///
/// Flattens the former `{id}` path segment and the tag/search/pagination query
/// string into one object.
/// `memory.drawers_list` — drawers in one palace, filtered and paged.
pub async
/// Params for `memory.drawer_create`.
/// `memory.drawer_create` — write one drawer, attributed to its caller.
///
/// Test: `rpc_drawer_create_attributes_the_caller_it_was_given`.
pub async
/// Params for `memory.drawer_delete`.
/// `memory.drawer_delete` — remove one drawer.
///
/// Answers `{"deleted": true}` rather than the former `204 No Content`: a
/// JSON-RPC success frame always carries a result, and a `null` would be
/// indistinguishable from a method that forgot to return one. A drawer id that
/// does not exist is still `-32004` (#5231).
pub async