@adhdev/mesh-shared 1.0.60-rc.8 → 1.0.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/brain-routing.d.ts +6 -8
  2. package/dist/git-diff.d.ts +21 -0
  3. package/dist/index.d.ts +12 -1
  4. package/dist/index.js +2876 -77
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +2625 -72
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/interpolation.d.ts +0 -9
  9. package/dist/json.d.ts +4 -0
  10. package/dist/keyed-doc-delta.d.ts +105 -0
  11. package/dist/mesh-tool-names.d.ts +31 -2
  12. package/dist/mesh-vocabulary.d.ts +66 -0
  13. package/dist/mission-brief.d.ts +62 -0
  14. package/dist/outbound-message.d.ts +201 -0
  15. package/dist/path-ownership.d.ts +101 -0
  16. package/dist/protocol/daemon-server.d.ts +356 -0
  17. package/dist/protocol/dashboard-daemon-p2p.d.ts +213 -0
  18. package/dist/protocol/dashboard-server.d.ts +240 -0
  19. package/dist/protocol/dashboard-wire-version.d.ts +21 -0
  20. package/dist/protocol/envelope.d.ts +55 -0
  21. package/dist/protocol/index.d.ts +16 -0
  22. package/dist/session-launch.d.ts +105 -0
  23. package/dist/session-status.d.ts +75 -0
  24. package/dist/slot-proposal.d.ts +2 -28
  25. package/dist/turn-evidence.d.ts +434 -0
  26. package/dist/turn-ipc-guards.d.ts +18 -0
  27. package/dist/turn-ipc-mission.d.ts +250 -0
  28. package/dist/turn-ipc-queue.d.ts +196 -0
  29. package/dist/turn-ipc-stats.d.ts +72 -0
  30. package/dist/turn-ipc-turn.d.ts +176 -0
  31. package/dist/turn-ipc.d.ts +90 -0
  32. package/dist/types.d.ts +11 -0
  33. package/dist/worker-protocol.d.ts +69 -0
  34. package/dist/ws-protocol.d.ts +69 -41
  35. package/package.json +1 -1
  36. package/src/brain-routing.ts +9 -11
  37. package/src/git-diff.ts +48 -0
  38. package/src/index.ts +12 -1
  39. package/src/interpolation.ts +0 -18
  40. package/src/json.ts +10 -0
  41. package/src/keyed-doc-delta.ts +413 -0
  42. package/src/mesh-tool-names.ts +66 -25
  43. package/src/mesh-vocabulary.ts +94 -0
  44. package/src/mission-brief.ts +153 -0
  45. package/src/outbound-message.ts +319 -0
  46. package/src/path-ownership.ts +271 -0
  47. package/src/protocol/daemon-server.ts +510 -0
  48. package/src/protocol/dashboard-daemon-p2p.ts +273 -0
  49. package/src/protocol/dashboard-server.ts +340 -0
  50. package/src/protocol/dashboard-wire-version.ts +27 -0
  51. package/src/protocol/envelope.ts +75 -0
  52. package/src/protocol/index.ts +17 -0
  53. package/src/session-launch.ts +245 -0
  54. package/src/session-status.ts +164 -0
  55. package/src/slot-proposal.ts +2 -48
  56. package/src/turn-evidence.ts +596 -0
  57. package/src/turn-ipc-guards.ts +61 -0
  58. package/src/turn-ipc-mission.ts +647 -0
  59. package/src/turn-ipc-queue.ts +493 -0
  60. package/src/turn-ipc-stats.ts +193 -0
  61. package/src/turn-ipc-turn.ts +453 -0
  62. package/src/turn-ipc.ts +137 -0
  63. package/src/types.ts +8 -0
  64. package/src/worker-protocol.ts +164 -0
  65. package/src/ws-protocol.ts +98 -93
  66. package/dist/magi.d.ts +0 -281
  67. package/src/magi.ts +0 -310
@@ -0,0 +1,493 @@
1
+ /**
2
+ * turn-ipc — local records, queue composites and active work (C-W9a):
3
+ * record_local, queue_query, queue_enqueue, queue_enqueue_batch, queue_cancel,
4
+ * queue_requeue, direct_dispatch_record, active_work_query,
5
+ * recovery_context_query. Part of the turn-ipc wire contract (./turn-ipc.ts).
6
+ */
7
+
8
+ // ─── C-W9a: local records, queue composites, active work ────────────────────
9
+ //
10
+ // Wiring-unification Phase C, workstream C-W9a (the 2026-09-24 14:00 stamp
11
+ // "Left for C-W9"): the event ledger and its JSONL mirror are gone, and every
12
+ // mcp-server call that still reached the daemon's `mesh-runtime.db` in-process
13
+ // — record appends, queue mutations, the queue/active-work reads that fed
14
+ // `buildMeshActiveWork`, recovery hints — moves behind the commands below,
15
+ // executed in the daemon that owns the store.
16
+ //
17
+ // CONTENT BOUNDARY: like `mission_upsert`'s `goal`, `note_upsert`'s `text` and
18
+ // `ledger_query`'s entry payloads, several fields here are free text or
19
+ // unbounded JSON (a task message, a queue row with its
20
+ // message, an active-work record with its task summary). This is fine for the
21
+ // same reason: local IPC between the mcp-server and the daemon on the
22
+ // operator's own machine — never a cross-machine or server hop. Those fields
23
+ // are documented as passthroughs and validated only for shape (object/array),
24
+ // exactly like `LedgerQueryEntryWire.payload`. Nothing here is ever published
25
+ // to a topic: `record_local`'s topic leg is the daemon's `mesh.record`
26
+ // allow-list projection, which drops free text by construction.
27
+
28
+ import { isRecord } from './protocol/envelope';
29
+ import { isEvidenceIdentifier } from './turn-evidence';
30
+ import {
31
+ TURN_IPC_PROTOCOL_VERSION,
32
+ hasOnlyKeys,
33
+ isOptionalId,
34
+ isNonNegativeInt,
35
+ isOptionalBoolean,
36
+ isOptionalRecord,
37
+ isRecordArray,
38
+ isOptionalString,
39
+ } from './turn-ipc-guards';
40
+
41
+ /** A queue row as the daemon's `getQueue` returns it — a JSON passthrough (see the section note). */
42
+ export type QueueEntryWire = Record<string, unknown> & { id: string; status: string }
43
+
44
+ function isQueueEntryWire(value: unknown): value is QueueEntryWire {
45
+ return isRecord(value) && isEvidenceIdentifier(value.id) && typeof value.status === 'string'
46
+ }
47
+
48
+ // ── record_local ──
49
+ //
50
+ // `meshRecord(meshId, kind, scalars, { local: true })` over IPC: the scalar
51
+ // projection goes to `mesh.<id>.events` (content-free), the FULL payload lands
52
+ // in the daemon's `mesh_local_records` (local-only). Replaces every mcp-server
53
+ // `appendLedgerEntry` (dispatch / checkpoint message /
54
+ // reconcile records). `payload` is free-form JSON (see the section note).
55
+
56
+ export interface RecordLocalRequest {
57
+ v: typeof TURN_IPC_PROTOCOL_VERSION
58
+ meshId: string
59
+ /** A `MeshLedgerKind` (daemon-core); an identifier here (mesh-shared cannot import that union). */
60
+ kind: string
61
+ nodeId?: string
62
+ sessionId?: string
63
+ providerType?: string
64
+ taskId?: string
65
+ payload: Record<string, unknown>
66
+ }
67
+
68
+ export interface RecordLocalResponse {
69
+ eventId: string
70
+ timestamp: string
71
+ storedLocally: boolean
72
+ published: boolean
73
+ }
74
+
75
+ function isRecordLocalRequest(value: unknown): value is RecordLocalRequest {
76
+ return isRecord(value) && hasOnlyKeys(value, ['v', 'meshId', 'kind', 'nodeId', 'sessionId', 'providerType', 'taskId', 'payload'])
77
+ && value.v === TURN_IPC_PROTOCOL_VERSION
78
+ && isEvidenceIdentifier(value.meshId)
79
+ && isEvidenceIdentifier(value.kind)
80
+ && isOptionalId(value.nodeId) && isOptionalId(value.sessionId) && isOptionalId(value.providerType) && isOptionalId(value.taskId)
81
+ && isRecord(value.payload)
82
+ }
83
+
84
+ export function decodeRecordLocalRequest(value: unknown): RecordLocalRequest | null {
85
+ return isRecordLocalRequest(value) ? value : null
86
+ }
87
+
88
+ function isRecordLocalResponse(value: unknown): value is RecordLocalResponse {
89
+ return isRecord(value) && hasOnlyKeys(value, ['eventId', 'timestamp', 'storedLocally', 'published'])
90
+ && isEvidenceIdentifier(value.eventId) && typeof value.timestamp === 'string'
91
+ && typeof value.storedLocally === 'boolean' && typeof value.published === 'boolean'
92
+ }
93
+
94
+ export function decodeRecordLocalResponse(value: unknown): RecordLocalResponse | null {
95
+ return isRecordLocalResponse(value) ? value : null
96
+ }
97
+
98
+ // ── queue_query ──
99
+
100
+ export interface QueueQueryRequest {
101
+ v: typeof TURN_IPC_PROTOCOL_VERSION
102
+ meshId: string
103
+ /** Status filter (queue statuses); omitted = every row. */
104
+ statuses?: readonly string[]
105
+ /** One row by id (returns 0 or 1 entries). */
106
+ taskId?: string
107
+ /** Project for a VIEW surface: the persisted input envelope → `inputSummary` (summarizeQueueEntryInputForView). */
108
+ view?: boolean
109
+ /**
110
+ * Also return the per-status row counts of the WHOLE mesh queue (columns only,
111
+ * no payload parse) — lets a view read only the rows it shows yet report
112
+ * mesh-wide counts. An older daemon rejects the key.
113
+ */
114
+ withCounts?: boolean
115
+ /** With `withCounts`: also count terminal rows whose `updated_at` is older than this age. */
116
+ historicalOlderThanMs?: number
117
+ /**
118
+ * Also return `dependencyHeads`: id/status/cancelReason of every
119
+ * row the returned entries list in `dependsOn` that is not itself returned —
120
+ * enough to annotate dependency state without reading the whole queue.
121
+ */
122
+ withDependencyHeads?: boolean
123
+ }
124
+
125
+ /** A dependency row reduced to the fields dependency-state annotation reads. */
126
+ export interface QueueDependencyHeadWire {
127
+ id: string
128
+ status: string
129
+ cancelReason?: string
130
+ }
131
+
132
+ export interface QueueQueryResponse {
133
+ entries: readonly QueueEntryWire[]
134
+ /** `withCounts`: row count per status over the whole mesh queue. */
135
+ counts?: Record<string, number>
136
+ /** `withCounts` + `historicalOlderThanMs`: terminal rows older than that age. */
137
+ oldHistoricalCount?: number
138
+ /** `withDependencyHeads`. */
139
+ dependencyHeads?: readonly QueueDependencyHeadWire[]
140
+ }
141
+
142
+ function isQueueQueryRequest(value: unknown): value is QueueQueryRequest {
143
+ if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'statuses', 'taskId', 'view', 'withCounts', 'historicalOlderThanMs', 'withDependencyHeads'])) return false
144
+ if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId)) return false
145
+ if (value.statuses !== undefined && (!Array.isArray(value.statuses) || !value.statuses.every(isEvidenceIdentifier))) return false
146
+ if (value.historicalOlderThanMs !== undefined && !isNonNegativeInt(value.historicalOlderThanMs)) return false
147
+ return isOptionalId(value.taskId) && isOptionalBoolean(value.view)
148
+ && isOptionalBoolean(value.withCounts) && isOptionalBoolean(value.withDependencyHeads)
149
+ }
150
+
151
+ export function decodeQueueQueryRequest(value: unknown): QueueQueryRequest | null {
152
+ return isQueueQueryRequest(value) ? value : null
153
+ }
154
+
155
+ function isQueueDependencyHeadWire(value: unknown): value is QueueDependencyHeadWire {
156
+ return isRecord(value) && hasOnlyKeys(value, ['id', 'status', 'cancelReason'])
157
+ && typeof value.id === 'string' && typeof value.status === 'string'
158
+ && (value.cancelReason === undefined || typeof value.cancelReason === 'string')
159
+ }
160
+
161
+ function isQueueQueryResponse(value: unknown): value is QueueQueryResponse {
162
+ if (!isRecord(value) || !hasOnlyKeys(value, ['entries', 'counts', 'oldHistoricalCount', 'dependencyHeads'])) return false
163
+ if (!Array.isArray(value.entries) || !value.entries.every(isQueueEntryWire)) return false
164
+ if (value.counts !== undefined && (!isRecord(value.counts) || !Object.values(value.counts).every(isNonNegativeInt))) return false
165
+ if (value.oldHistoricalCount !== undefined && !isNonNegativeInt(value.oldHistoricalCount)) return false
166
+ if (value.dependencyHeads !== undefined && (!Array.isArray(value.dependencyHeads) || !value.dependencyHeads.every(isQueueDependencyHeadWire))) return false
167
+ return true
168
+ }
169
+
170
+ export function decodeQueueQueryResponse(value: unknown): QueueQueryResponse | null {
171
+ return isQueueQueryResponse(value) ? value : null
172
+ }
173
+
174
+ // ── queue_enqueue ──
175
+ //
176
+ // `enqueueTask(meshId, message, options)` in the daemon. `options` is
177
+ // `MeshEnqueueTaskOptions` (daemon-core) as a JSON
178
+ // passthrough; the daemon's enqueue guards (message, task-mode, difficulty,
179
+ // dependency cycle) run unchanged and a refusal comes back as the error.
180
+
181
+ export interface QueueEnqueueRequest {
182
+ v: typeof TURN_IPC_PROTOCOL_VERSION
183
+ meshId: string
184
+ /** Free text (the task message — see the section note). */
185
+ message: string
186
+ options?: Record<string, unknown>
187
+ }
188
+
189
+ export interface QueueEnqueueResponse {
190
+ entry: QueueEntryWire
191
+ }
192
+
193
+ function isQueueEnqueueRequest(value: unknown): value is QueueEnqueueRequest {
194
+ return isRecord(value) && hasOnlyKeys(value, ['v', 'meshId', 'message', 'options'])
195
+ && value.v === TURN_IPC_PROTOCOL_VERSION && isEvidenceIdentifier(value.meshId)
196
+ && typeof value.message === 'string'
197
+ && isOptionalRecord(value.options)
198
+ }
199
+
200
+ export function decodeQueueEnqueueRequest(value: unknown): QueueEnqueueRequest | null {
201
+ return isQueueEnqueueRequest(value) ? value : null
202
+ }
203
+
204
+ function isQueueEnqueueResponse(value: unknown): value is QueueEnqueueResponse {
205
+ return isRecord(value) && hasOnlyKeys(value, ['entry']) && isQueueEntryWire(value.entry)
206
+ }
207
+
208
+ export function decodeQueueEnqueueResponse(value: unknown): QueueEnqueueResponse | null {
209
+ return isQueueEnqueueResponse(value) ? value : null
210
+ }
211
+
212
+ // ── queue_enqueue_batch ──
213
+ //
214
+ // The atomic multi-task enqueue in the daemon: `enqueueTaskBatch(meshId, specs)`
215
+ // — every task inserts or none does. A refusal answers
216
+ // `{ ok: false, refusalCode?, message, extra? }` (a domain refusal is a RESULT
217
+ // here, not a transport error, so the caller keeps its code/extra for the tool
218
+ // response). NOT `code`/`error`: those two keys belong to the command envelope
219
+ // (`{ success, error, code }`) the client unwraps, so a result must never
220
+ // reuse them.
221
+
222
+ export interface QueueEnqueueBatchRequest {
223
+ v: typeof TURN_IPC_PROTOCOL_VERSION
224
+ meshId: string
225
+ /** `MeshTaskBatchEntrySpec[]` (daemon-core, JSON passthrough). */
226
+ specs: readonly Record<string, unknown>[]
227
+ }
228
+
229
+ export type QueueEnqueueBatchResponse =
230
+ | { ok: true; tasks: readonly QueueEntryWire[] }
231
+ | { ok: false; refusalCode?: string; message: string; extra?: Record<string, unknown> }
232
+
233
+ function isQueueEnqueueBatchRequest(value: unknown): value is QueueEnqueueBatchRequest {
234
+ return isRecord(value) && hasOnlyKeys(value, ['v', 'meshId', 'specs'])
235
+ && value.v === TURN_IPC_PROTOCOL_VERSION && isEvidenceIdentifier(value.meshId)
236
+ && isRecordArray(value.specs)
237
+ }
238
+
239
+ export function decodeQueueEnqueueBatchRequest(value: unknown): QueueEnqueueBatchRequest | null {
240
+ return isQueueEnqueueBatchRequest(value) ? value : null
241
+ }
242
+
243
+ function isQueueEnqueueBatchResponse(value: unknown): value is QueueEnqueueBatchResponse {
244
+ if (!isRecord(value)) return false
245
+ if (value.ok === true) {
246
+ return hasOnlyKeys(value, ['ok', 'tasks']) && Array.isArray(value.tasks) && value.tasks.every(isQueueEntryWire)
247
+ }
248
+ if (value.ok === false) {
249
+ return hasOnlyKeys(value, ['ok', 'refusalCode', 'message', 'extra']) && typeof value.message === 'string'
250
+ && isOptionalString(value.refusalCode) && isOptionalRecord(value.extra)
251
+ }
252
+ return false
253
+ }
254
+
255
+ export function decodeQueueEnqueueBatchResponse(value: unknown): QueueEnqueueBatchResponse | null {
256
+ return isQueueEnqueueBatchResponse(value) ? value : null
257
+ }
258
+
259
+ // ── queue_cancel ──
260
+
261
+ export interface QueueCancelRequest {
262
+ v: typeof TURN_IPC_PROTOCOL_VERSION
263
+ meshId: string
264
+ taskId: string
265
+ /** Operator reason (free text, local IPC). */
266
+ reason?: string
267
+ }
268
+
269
+ export interface QueueCancelResponse {
270
+ /** The cancelled row, or null when no such task. */
271
+ task: QueueEntryWire | null
272
+ /** The row as it was BEFORE the cancel (its assignment is what a caller stops). */
273
+ before: QueueEntryWire | null
274
+ }
275
+
276
+ function isQueueCancelRequest(value: unknown): value is QueueCancelRequest {
277
+ return isRecord(value) && hasOnlyKeys(value, ['v', 'meshId', 'taskId', 'reason'])
278
+ && value.v === TURN_IPC_PROTOCOL_VERSION && isEvidenceIdentifier(value.meshId) && isEvidenceIdentifier(value.taskId)
279
+ && isOptionalString(value.reason)
280
+ }
281
+
282
+ export function decodeQueueCancelRequest(value: unknown): QueueCancelRequest | null {
283
+ return isQueueCancelRequest(value) ? value : null
284
+ }
285
+
286
+ function isQueueCancelResponse(value: unknown): value is QueueCancelResponse {
287
+ return isRecord(value) && hasOnlyKeys(value, ['task', 'before'])
288
+ && (value.task === null || isQueueEntryWire(value.task))
289
+ && (value.before === null || isQueueEntryWire(value.before))
290
+ }
291
+
292
+ export function decodeQueueCancelResponse(value: unknown): QueueCancelResponse | null {
293
+ return isQueueCancelResponse(value) ? value : null
294
+ }
295
+
296
+ // ── queue_requeue ──
297
+
298
+ export interface QueueRequeueRequest {
299
+ v: typeof TURN_IPC_PROTOCOL_VERSION
300
+ meshId: string
301
+ taskId: string
302
+ /** `requeueTask` options (reason / targetNodeId / targetSessionId / clear* / force / message / maxRetries / notBefore), JSON passthrough. */
303
+ options?: Record<string, unknown>
304
+ }
305
+
306
+ export interface QueueRequeueResponse {
307
+ task: QueueEntryWire | null
308
+ }
309
+
310
+ function isQueueRequeueRequest(value: unknown): value is QueueRequeueRequest {
311
+ return isRecord(value) && hasOnlyKeys(value, ['v', 'meshId', 'taskId', 'options'])
312
+ && value.v === TURN_IPC_PROTOCOL_VERSION && isEvidenceIdentifier(value.meshId) && isEvidenceIdentifier(value.taskId)
313
+ && isOptionalRecord(value.options)
314
+ }
315
+
316
+ export function decodeQueueRequeueRequest(value: unknown): QueueRequeueRequest | null {
317
+ return isQueueRequeueRequest(value) ? value : null
318
+ }
319
+
320
+ function isQueueRequeueResponse(value: unknown): value is QueueRequeueResponse {
321
+ return isRecord(value) && hasOnlyKeys(value, ['task']) && (value.task === null || isQueueEntryWire(value.task))
322
+ }
323
+
324
+ export function decodeQueueRequeueResponse(value: unknown): QueueRequeueResponse | null {
325
+ return isQueueRequeueResponse(value) ? value : null
326
+ }
327
+
328
+ // ── direct_dispatch_record ──
329
+ //
330
+ // The post-dispatch bookkeeping of `mesh_send_task`'s direct arms, in the
331
+ // daemon: `recordDirectDispatchTask` (materialise the queue row, stamped with
332
+ // the already-open `mesh_direct` attempt). Best-effort and reported — a
333
+ // dispatch that already happened must never fail on its bookkeeping.
334
+
335
+ export interface DirectDispatchRecordRequest {
336
+ v: typeof TURN_IPC_PROTOCOL_VERSION
337
+ meshId: string
338
+ taskId: string
339
+ /** Free text (the authored task message — the materialised row keeps it). */
340
+ message: string
341
+ /** `recordDirectDispatchTask` options minus `id` (JSON passthrough). */
342
+ task?: Record<string, unknown>
343
+ }
344
+
345
+ export interface DirectDispatchRecordResponse {
346
+ taskRecorded: boolean
347
+ }
348
+
349
+ function isDirectDispatchRecordRequest(value: unknown): value is DirectDispatchRecordRequest {
350
+ return isRecord(value) && hasOnlyKeys(value, ['v', 'meshId', 'taskId', 'message', 'task'])
351
+ && value.v === TURN_IPC_PROTOCOL_VERSION && isEvidenceIdentifier(value.meshId) && isEvidenceIdentifier(value.taskId)
352
+ && typeof value.message === 'string' && isOptionalRecord(value.task)
353
+ }
354
+
355
+ export function decodeDirectDispatchRecordRequest(value: unknown): DirectDispatchRecordRequest | null {
356
+ return isDirectDispatchRecordRequest(value) ? value : null
357
+ }
358
+
359
+ function isDirectDispatchRecordResponse(value: unknown): value is DirectDispatchRecordResponse {
360
+ return isRecord(value) && hasOnlyKeys(value, ['taskRecorded'])
361
+ && typeof value.taskRecorded === 'boolean'
362
+ }
363
+
364
+ export function decodeDirectDispatchRecordResponse(value: unknown): DirectDispatchRecordResponse | null {
365
+ return isDirectDispatchRecordResponse(value) ? value : null
366
+ }
367
+
368
+ // ── active_work_query ──
369
+ //
370
+ // The active-work view, COMPUTED in the daemon: it reads the queue, the open
371
+ // direct dispatches (`mesh_direct` attempts) and its local records (+ turn
372
+ // outcomes), and runs `buildMeshActiveWork` over them with the caller's live
373
+ // node probe results. `includeInputs` also returns the records + dispatches it
374
+ // used (the transcript-reconcile pass and the stale-direct prune read them);
375
+ // `compute: false` returns only those inputs. `includeSchedulingRuntime` adds
376
+ // `buildMeshSchedulingRuntime(mesh, queue)` for the caller's `mesh` snapshot.
377
+ // `queue` lets a caller substitute its own annotated queue view.
378
+
379
+ export interface ActiveWorkQueryRequest {
380
+ v: typeof TURN_IPC_PROTOCOL_VERSION
381
+ meshId: string
382
+ /** Live node probe results (`buildMeshActiveWork`'s `nodes`), JSON passthrough. */
383
+ nodes?: readonly Record<string, unknown>[]
384
+ /** Substitute queue rows (default: the daemon's `getQueue`). */
385
+ queue?: readonly Record<string, unknown>[]
386
+ /** Record tail fed to active work (default 200). */
387
+ recordTail?: number
388
+ includeTerminalDirect?: boolean
389
+ /** Default true. */
390
+ compute?: boolean
391
+ includeInputs?: boolean
392
+ includeSummary?: boolean
393
+ includeSchedulingRuntime?: boolean
394
+ /**
395
+ * The caller's mesh snapshot for the scheduling runtime, JSON passthrough.
396
+ * Optional since the daemon resolves its own mesh record when it is absent;
397
+ * an older daemon rejects `includeSchedulingRuntime` without it (the caller's
398
+ * feature-detect signal to resend with the mesh).
399
+ */
400
+ mesh?: Record<string, unknown>
401
+ }
402
+
403
+ export interface ActiveWorkQueryResponse {
404
+ /** `MeshActiveWorkEvidence` (daemon-core), JSON passthrough; absent when `compute: false`. */
405
+ activeWork?: Record<string, unknown>
406
+ records?: readonly Record<string, unknown>[]
407
+ directDispatches?: readonly Record<string, unknown>[]
408
+ /** `MeshLedgerSummary`-shaped record summary. */
409
+ summary?: Record<string, unknown>
410
+ schedulingRuntime?: Record<string, unknown>
411
+ }
412
+
413
+ function isActiveWorkQueryRequest(value: unknown): value is ActiveWorkQueryRequest {
414
+ if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'nodes', 'queue', 'recordTail', 'includeTerminalDirect', 'compute', 'includeInputs', 'includeSummary', 'includeSchedulingRuntime', 'mesh'])) return false
415
+ if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId)) return false
416
+ if (value.nodes !== undefined && !isRecordArray(value.nodes)) return false
417
+ if (value.queue !== undefined && !isRecordArray(value.queue)) return false
418
+ if (value.recordTail !== undefined && !isNonNegativeInt(value.recordTail)) return false
419
+ return isOptionalBoolean(value.includeTerminalDirect) && isOptionalBoolean(value.compute) && isOptionalBoolean(value.includeInputs)
420
+ && isOptionalBoolean(value.includeSummary) && isOptionalBoolean(value.includeSchedulingRuntime) && isOptionalRecord(value.mesh)
421
+ }
422
+
423
+ export function decodeActiveWorkQueryRequest(value: unknown): ActiveWorkQueryRequest | null {
424
+ return isActiveWorkQueryRequest(value) ? value : null
425
+ }
426
+
427
+ function isActiveWorkQueryResponse(value: unknown): value is ActiveWorkQueryResponse {
428
+ if (!isRecord(value) || !hasOnlyKeys(value, ['activeWork', 'records', 'directDispatches', 'summary', 'schedulingRuntime'])) return false
429
+ if (value.records !== undefined && !isRecordArray(value.records)) return false
430
+ if (value.directDispatches !== undefined && !isRecordArray(value.directDispatches)) return false
431
+ return isOptionalRecord(value.activeWork) && isOptionalRecord(value.summary) && isOptionalRecord(value.schedulingRuntime)
432
+ }
433
+
434
+ export function decodeActiveWorkQueryResponse(value: unknown): ActiveWorkQueryResponse | null {
435
+ return isActiveWorkQueryResponse(value) ? value : null
436
+ }
437
+
438
+ // ── recovery_context_query ──
439
+ //
440
+ // `getSessionRecoveryContext` (daemon-core `mesh-local-records.ts`) in the
441
+ // daemon. `lastTaskMessage` / `advice` are free text (local IPC).
442
+
443
+ export interface RecoveryContextQueryRequest {
444
+ v: typeof TURN_IPC_PROTOCOL_VERSION
445
+ meshId: string
446
+ nodeId?: string
447
+ sessionId?: string
448
+ maxRetries?: number
449
+ /**
450
+ * Batch: one context per node id, computed over ONE record read (answers
451
+ * `contexts`). Exclusive with `nodeId`/`sessionId`. An older daemon rejects it.
452
+ */
453
+ nodeIds?: readonly string[]
454
+ }
455
+
456
+ export interface RecoveryContextQueryResponse {
457
+ /** Single mode (nodeId/sessionId). */
458
+ context?: Record<string, unknown>
459
+ /** Batch mode (`nodeIds`): context per node id. */
460
+ contexts?: Record<string, Record<string, unknown>>
461
+ }
462
+
463
+ function isRecoveryContextQueryRequest(value: unknown): value is RecoveryContextQueryRequest {
464
+ if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'nodeId', 'sessionId', 'maxRetries', 'nodeIds'])) return false
465
+ if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId)) return false
466
+ if (!isOptionalId(value.nodeId) || !isOptionalId(value.sessionId)) return false
467
+ if (value.maxRetries !== undefined && !isNonNegativeInt(value.maxRetries)) return false
468
+ if (value.nodeIds !== undefined) {
469
+ if (!Array.isArray(value.nodeIds) || value.nodeIds.length === 0 || !value.nodeIds.every(isEvidenceIdentifier)) return false
470
+ return value.nodeId === undefined && value.sessionId === undefined
471
+ }
472
+ return value.nodeId !== undefined || value.sessionId !== undefined
473
+ }
474
+
475
+ export function decodeRecoveryContextQueryRequest(value: unknown): RecoveryContextQueryRequest | null {
476
+ return isRecoveryContextQueryRequest(value) ? value : null
477
+ }
478
+
479
+ function isRecoveryContextWire(value: unknown): boolean {
480
+ return isRecord(value) && typeof value.consecutiveNodeFailures === 'number'
481
+ }
482
+
483
+ function isRecoveryContextQueryResponse(value: unknown): value is RecoveryContextQueryResponse {
484
+ if (!isRecord(value) || !hasOnlyKeys(value, ['context', 'contexts'])) return false
485
+ if (value.context === undefined && value.contexts === undefined) return false
486
+ if (value.context !== undefined && !isRecoveryContextWire(value.context)) return false
487
+ if (value.contexts !== undefined && (!isRecord(value.contexts) || !Object.values(value.contexts).every(isRecoveryContextWire))) return false
488
+ return true
489
+ }
490
+
491
+ export function decodeRecoveryContextQueryResponse(value: unknown): RecoveryContextQueryResponse | null {
492
+ return isRecoveryContextQueryResponse(value) ? value : null
493
+ }