@adhdev/mesh-shared 1.0.60-rc.73 → 1.0.60-rc.74
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.
- package/dist/brain-routing.d.ts +4 -6
- package/dist/git-diff.d.ts +21 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +84 -269
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +78 -250
- package/dist/index.mjs.map +1 -1
- package/dist/json.d.ts +4 -0
- package/dist/mesh-tool-names.d.ts +11 -7
- package/dist/mesh-vocabulary.d.ts +1 -2
- package/dist/outbound-message.d.ts +1 -2
- package/dist/protocol/daemon-server.d.ts +8 -31
- package/dist/session-launch.d.ts +0 -8
- package/dist/slot-proposal.d.ts +1 -27
- package/dist/turn-ipc-mission.d.ts +0 -7
- package/dist/turn-ipc-queue.d.ts +10 -35
- package/dist/turn-ipc-stats.d.ts +72 -0
- package/dist/turn-ipc.d.ts +4 -4
- package/package.json +1 -1
- package/src/brain-routing.ts +6 -8
- package/src/git-diff.ts +48 -0
- package/src/index.ts +1 -1
- package/src/json.ts +10 -0
- package/src/mesh-tool-names.ts +20 -25
- package/src/mesh-vocabulary.ts +1 -2
- package/src/outbound-message.ts +1 -2
- package/src/protocol/daemon-server.ts +3 -34
- package/src/session-launch.ts +2 -4
- package/src/slot-proposal.ts +1 -47
- package/src/turn-ipc-mission.ts +7 -21
- package/src/turn-ipc-queue.ts +40 -102
- package/src/turn-ipc-stats.ts +193 -0
- package/src/turn-ipc.ts +5 -11
- package/dist/magi.d.ts +0 -281
- package/dist/turn-ipc-graph.d.ts +0 -195
- package/src/magi.ts +0 -310
- package/src/turn-ipc-graph.ts +0 -477
package/src/turn-ipc-graph.ts
DELETED
|
@@ -1,477 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* turn-ipc — graph gates, task stats and maintenance commands: graph_gate_claim,
|
|
3
|
-
* graph_gate_release, graph_gate_abandon, graph_node_patch, graph_view_query,
|
|
4
|
-
* task_stats_query, prune_stale_direct, orphaned_pin_notify. Part of the turn-ipc
|
|
5
|
-
* wire contract (./turn-ipc.ts).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
TURN_IPC_PROTOCOL_VERSION,
|
|
10
|
-
hasOnlyKeys,
|
|
11
|
-
isFiniteNumber,
|
|
12
|
-
isOptionalBoolean,
|
|
13
|
-
isOptionalRecord,
|
|
14
|
-
isNonNegativeInt,
|
|
15
|
-
isOptionalString,
|
|
16
|
-
isRecordArray,
|
|
17
|
-
} from './turn-ipc-guards';
|
|
18
|
-
import { isRecord } from './protocol/envelope';
|
|
19
|
-
import { isEvidenceIdentifier } from './turn-evidence';
|
|
20
|
-
|
|
21
|
-
// ─── graph_gate_claim / graph_gate_release / graph_gate_abandon / graph_node_patch / graph_view_query ──
|
|
22
|
-
//
|
|
23
|
-
// Wiring-unification Phase C, workstream C-W9c (2026-09-24 19:00 stamp
|
|
24
|
-
// "mcp-server graph gates/plan/patch ... still call daemon-core in-process").
|
|
25
|
-
//
|
|
26
|
-
// `mesh-tools-graph.ts`'s five graph-orchestration tools (the gate claim
|
|
27
|
-
// / release / abandon verbs, now `mesh_graph_gate`, `mesh_graph_node_patch`, `mesh_graph_view`) called
|
|
28
|
-
// daemon-core's MeshRuntimeStore-backed graph module in-process
|
|
29
|
-
// (`claimMeshGraphGate` / `releaseMeshGraphGate` / `abandonMeshGraphGate` /
|
|
30
|
-
// `patchGraphNodeAndRetry` / `buildMeshGraphViews`, all in `mesh-graph-gates.ts` /
|
|
31
|
-
// `mesh-graph-transition-runner.ts` / `mesh-graph-view.ts`), plus the read-only
|
|
32
|
-
// git-based `collectGateConvergenceEvidence` probe. These five commands move
|
|
33
|
-
// every one of those calls into the daemon that owns the graph rows.
|
|
34
|
-
//
|
|
35
|
-
// CONTENT BOUNDARY: a gate/node-patch result is exposed to the CALLER as
|
|
36
|
-
// identifiers, enums, counts and a `result`/`evidence`/`patches` passthrough
|
|
37
|
-
// the coordinator itself supplied on the request (release/patch echo back only
|
|
38
|
-
// what they were given) — never anything the daemon derives from chat content.
|
|
39
|
-
// `MeshGraphGateRow` / `MeshGraphView` are large, evolving daemon-core shapes;
|
|
40
|
-
// rather than duplicate their structure here (the same risk `active_work_query`'s
|
|
41
|
-
// `activeWork` avoided), the gate/graph payload is a JSON passthrough
|
|
42
|
-
// (`Record<string, unknown>`), exactly like `ActiveWorkQueryResponse.activeWork`.
|
|
43
|
-
|
|
44
|
-
export interface GraphGateClaimRequest {
|
|
45
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
46
|
-
meshId: string
|
|
47
|
-
gateId: string
|
|
48
|
-
coordinatorSessionId: string
|
|
49
|
-
leaseSeconds?: number
|
|
50
|
-
extendDeadlineSeconds?: number
|
|
51
|
-
/** Also collect `collectGateConvergenceEvidence` on a successful claim (mesh_graph_gate action=claim's own behavior). */
|
|
52
|
-
probeConvergenceEvidence?: boolean
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface GraphGateClaimResponse {
|
|
56
|
-
claimed: boolean
|
|
57
|
-
/** gate_not_found / gate_not_eligible / gate_not_awaiting / gate_lease_held / gate_claim_race / gate_terminal:<state> / gate_not_claimable, when `claimed: false`. */
|
|
58
|
-
reason?: string
|
|
59
|
-
/** `MeshGraphGateRow`, JSON passthrough; present whenever the core found the gate. */
|
|
60
|
-
gate?: Record<string, unknown>
|
|
61
|
-
leaseGeneration?: number
|
|
62
|
-
fencingToken?: string
|
|
63
|
-
leaseExpiresAt?: string
|
|
64
|
-
deadlineAt?: string
|
|
65
|
-
ambiguousExternalOutcome?: boolean
|
|
66
|
-
previousLeaseOwnerSessionId?: string
|
|
67
|
-
/** `GateConvergenceEvidence`, JSON passthrough; present only when requested and found. */
|
|
68
|
-
convergenceEvidence?: Record<string, unknown>
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function isGraphGateClaimRequest(value: unknown): value is GraphGateClaimRequest {
|
|
72
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'gateId', 'coordinatorSessionId', 'leaseSeconds', 'extendDeadlineSeconds', 'probeConvergenceEvidence'])) return false
|
|
73
|
-
if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId) || !isEvidenceIdentifier(value.gateId) || !isEvidenceIdentifier(value.coordinatorSessionId)) return false
|
|
74
|
-
if (value.leaseSeconds !== undefined && !isFiniteNumber(value.leaseSeconds)) return false
|
|
75
|
-
if (value.extendDeadlineSeconds !== undefined && !isFiniteNumber(value.extendDeadlineSeconds)) return false
|
|
76
|
-
return isOptionalBoolean(value.probeConvergenceEvidence)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function decodeGraphGateClaimRequest(value: unknown): GraphGateClaimRequest | null {
|
|
80
|
-
return isGraphGateClaimRequest(value) ? value : null
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function isGraphGateClaimResponse(value: unknown): value is GraphGateClaimResponse {
|
|
84
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['claimed', 'reason', 'gate', 'leaseGeneration', 'fencingToken', 'leaseExpiresAt', 'deadlineAt', 'ambiguousExternalOutcome', 'previousLeaseOwnerSessionId', 'convergenceEvidence'])) return false
|
|
85
|
-
if (typeof value.claimed !== 'boolean') return false
|
|
86
|
-
return isOptionalRecord(value.gate) && isOptionalRecord(value.convergenceEvidence) && isOptionalBoolean(value.ambiguousExternalOutcome)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function decodeGraphGateClaimResponse(value: unknown): GraphGateClaimResponse | null {
|
|
90
|
-
return isGraphGateClaimResponse(value) ? value : null
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export interface GraphGateReleaseRequest {
|
|
94
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
95
|
-
meshId: string
|
|
96
|
-
gateId: string
|
|
97
|
-
fencingToken: string
|
|
98
|
-
leaseGeneration: number
|
|
99
|
-
idempotencyKey: string
|
|
100
|
-
outcome: string
|
|
101
|
-
/** Action-specific structured result/evidence the CALLER supplied — JSON passthrough, echoed back unmodified. */
|
|
102
|
-
result?: unknown
|
|
103
|
-
evidence?: unknown
|
|
104
|
-
patches?: readonly { node: string, baseSpecPatch: Record<string, unknown> }[]
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// `releaseMeshGraphGate` THROWS for every rejection (its whole transaction must
|
|
108
|
-
// roll back — mesh-graph-gates.ts file header). The daemon handler catches
|
|
109
|
-
// that and reports it as a RESULT, not an IPC-level failure (the same
|
|
110
|
-
// success:true-carries-a-refusal shape `queue_enqueue_graph` already uses) —
|
|
111
|
-
// `mesh_graph_gate` action=release's tool layer classifies `refusalCode` by prefix
|
|
112
|
-
// exactly as it did the in-process thrown message.
|
|
113
|
-
export type GraphGateReleaseResponse =
|
|
114
|
-
| {
|
|
115
|
-
released: true
|
|
116
|
-
duplicate: boolean
|
|
117
|
-
gate?: Record<string, unknown>
|
|
118
|
-
materializedNodeIds: readonly string[]
|
|
119
|
-
downstreamNodeCount?: number
|
|
120
|
-
graphCompleted?: boolean
|
|
121
|
-
}
|
|
122
|
-
| { released: false, refusalCode?: string, message: string }
|
|
123
|
-
|
|
124
|
-
function isGraphGateReleaseRequest(value: unknown): value is GraphGateReleaseRequest {
|
|
125
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'gateId', 'fencingToken', 'leaseGeneration', 'idempotencyKey', 'outcome', 'result', 'evidence', 'patches'])) return false
|
|
126
|
-
if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId) || !isEvidenceIdentifier(value.gateId)) return false
|
|
127
|
-
if (typeof value.fencingToken !== 'string' || !value.fencingToken) return false
|
|
128
|
-
if (!isFiniteNumber(value.leaseGeneration)) return false
|
|
129
|
-
if (typeof value.idempotencyKey !== 'string' || !value.idempotencyKey) return false
|
|
130
|
-
if (typeof value.outcome !== 'string' || !value.outcome) return false
|
|
131
|
-
if (value.patches !== undefined) {
|
|
132
|
-
if (!Array.isArray(value.patches)) return false
|
|
133
|
-
for (const p of value.patches) {
|
|
134
|
-
if (!isRecord(p) || typeof p.node !== 'string' || !p.node || !isRecord(p.baseSpecPatch)) return false
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return true
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function decodeGraphGateReleaseRequest(value: unknown): GraphGateReleaseRequest | null {
|
|
141
|
-
return isGraphGateReleaseRequest(value) ? value : null
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function isGraphGateReleaseResponse(value: unknown): value is GraphGateReleaseResponse {
|
|
145
|
-
if (!isRecord(value)) return false
|
|
146
|
-
if (value.released === true) {
|
|
147
|
-
if (!hasOnlyKeys(value, ['released', 'duplicate', 'gate', 'materializedNodeIds', 'downstreamNodeCount', 'graphCompleted'])) return false
|
|
148
|
-
if (typeof value.duplicate !== 'boolean') return false
|
|
149
|
-
if (!Array.isArray(value.materializedNodeIds) || !value.materializedNodeIds.every((n) => typeof n === 'string')) return false
|
|
150
|
-
if (value.downstreamNodeCount !== undefined && !isNonNegativeInt(value.downstreamNodeCount)) return false
|
|
151
|
-
return isOptionalRecord(value.gate) && isOptionalBoolean(value.graphCompleted)
|
|
152
|
-
}
|
|
153
|
-
if (value.released === false) {
|
|
154
|
-
return hasOnlyKeys(value, ['released', 'refusalCode', 'message']) && typeof value.message === 'string' && isOptionalString(value.refusalCode)
|
|
155
|
-
}
|
|
156
|
-
return false
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function decodeGraphGateReleaseResponse(value: unknown): GraphGateReleaseResponse | null {
|
|
160
|
-
return isGraphGateReleaseResponse(value) ? value : null
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export interface GraphGateAbandonRequest {
|
|
164
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
165
|
-
meshId: string
|
|
166
|
-
gateId: string
|
|
167
|
-
reason: string
|
|
168
|
-
coordinatorSessionId?: string
|
|
169
|
-
force?: boolean
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
export interface GraphGateAbandonResponse {
|
|
173
|
-
abandoned: boolean
|
|
174
|
-
/** gate_not_found / gate_lease_held / gate_abandon_race / gate_terminal:<state> / gate_not_abandonable, when `abandoned: false`. */
|
|
175
|
-
reason?: string
|
|
176
|
-
gate?: Record<string, unknown>
|
|
177
|
-
cancelledNodeIds: readonly string[]
|
|
178
|
-
cancelledTaskIds: readonly string[]
|
|
179
|
-
graphStatus?: string
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function isGraphGateAbandonRequest(value: unknown): value is GraphGateAbandonRequest {
|
|
183
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'gateId', 'reason', 'coordinatorSessionId', 'force'])) return false
|
|
184
|
-
if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId) || !isEvidenceIdentifier(value.gateId)) return false
|
|
185
|
-
if (typeof value.reason !== 'string' || !value.reason) return false
|
|
186
|
-
if (value.coordinatorSessionId !== undefined && !isEvidenceIdentifier(value.coordinatorSessionId)) return false
|
|
187
|
-
return isOptionalBoolean(value.force)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export function decodeGraphGateAbandonRequest(value: unknown): GraphGateAbandonRequest | null {
|
|
191
|
-
return isGraphGateAbandonRequest(value) ? value : null
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function isGraphGateAbandonResponse(value: unknown): value is GraphGateAbandonResponse {
|
|
195
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['abandoned', 'reason', 'gate', 'cancelledNodeIds', 'cancelledTaskIds', 'graphStatus'])) return false
|
|
196
|
-
if (typeof value.abandoned !== 'boolean') return false
|
|
197
|
-
if (!Array.isArray(value.cancelledNodeIds) || !value.cancelledNodeIds.every((n) => typeof n === 'string')) return false
|
|
198
|
-
if (!Array.isArray(value.cancelledTaskIds) || !value.cancelledTaskIds.every((n) => typeof n === 'string')) return false
|
|
199
|
-
return isOptionalRecord(value.gate)
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export function decodeGraphGateAbandonResponse(value: unknown): GraphGateAbandonResponse | null {
|
|
203
|
-
return isGraphGateAbandonResponse(value) ? value : null
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export interface GraphNodePatchRequest {
|
|
207
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
208
|
-
meshId: string
|
|
209
|
-
node: string
|
|
210
|
-
graphId?: string
|
|
211
|
-
/** Only {@link MESH_NODE_PATCH_KEYS}-shaped keys are permitted; the daemon enforces the allow-list. */
|
|
212
|
-
baseSpecPatch: Record<string, unknown>
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// `patchGraphNodeAndRetry` THROWS for every rejection (mesh-graph-transition-runner.ts) —
|
|
216
|
-
// same released:false treatment as `GraphGateReleaseResponse` above.
|
|
217
|
-
export type GraphNodePatchResponse =
|
|
218
|
-
| {
|
|
219
|
-
patched: true
|
|
220
|
-
graphId: string
|
|
221
|
-
nodeId: string
|
|
222
|
-
ref?: string
|
|
223
|
-
queueTaskId?: string
|
|
224
|
-
materializationVersion: number
|
|
225
|
-
state: string
|
|
226
|
-
/** `SettleOutcome['kind']` — materialized / deferred / skipped / error. */
|
|
227
|
-
outcomeKind: string
|
|
228
|
-
/** Present when `outcomeKind === 'skipped'`. */
|
|
229
|
-
skippedReason?: string
|
|
230
|
-
blockedReason?: string
|
|
231
|
-
}
|
|
232
|
-
| { patched: false, refusalCode?: string, message: string }
|
|
233
|
-
|
|
234
|
-
function isGraphNodePatchRequest(value: unknown): value is GraphNodePatchRequest {
|
|
235
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'node', 'graphId', 'baseSpecPatch'])) return false
|
|
236
|
-
if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId)) return false
|
|
237
|
-
if (typeof value.node !== 'string' || !value.node) return false
|
|
238
|
-
if (value.graphId !== undefined && !isEvidenceIdentifier(value.graphId)) return false
|
|
239
|
-
return isRecord(value.baseSpecPatch) && Object.keys(value.baseSpecPatch).length > 0
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
export function decodeGraphNodePatchRequest(value: unknown): GraphNodePatchRequest | null {
|
|
243
|
-
return isGraphNodePatchRequest(value) ? value : null
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function isGraphNodePatchResponse(value: unknown): value is GraphNodePatchResponse {
|
|
247
|
-
if (!isRecord(value)) return false
|
|
248
|
-
if (value.patched === true) {
|
|
249
|
-
if (!hasOnlyKeys(value, ['patched', 'graphId', 'nodeId', 'ref', 'queueTaskId', 'materializationVersion', 'state', 'outcomeKind', 'skippedReason', 'blockedReason'])) return false
|
|
250
|
-
if (!isEvidenceIdentifier(value.graphId) || !isEvidenceIdentifier(value.nodeId)) return false
|
|
251
|
-
if (!isNonNegativeInt(value.materializationVersion)) return false
|
|
252
|
-
return typeof value.state === 'string' && typeof value.outcomeKind === 'string'
|
|
253
|
-
}
|
|
254
|
-
if (value.patched === false) {
|
|
255
|
-
return hasOnlyKeys(value, ['patched', 'refusalCode', 'message']) && typeof value.message === 'string' && isOptionalString(value.refusalCode)
|
|
256
|
-
}
|
|
257
|
-
return false
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export function decodeGraphNodePatchResponse(value: unknown): GraphNodePatchResponse | null {
|
|
261
|
-
return isGraphNodePatchResponse(value) ? value : null
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export interface GraphViewQueryRequest {
|
|
265
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
266
|
-
meshId: string
|
|
267
|
-
graphId?: string
|
|
268
|
-
batchId?: string
|
|
269
|
-
/** Default true: only graphs that still need attention. */
|
|
270
|
-
activeOnly?: boolean
|
|
271
|
-
limit?: number
|
|
272
|
-
/** Attach `collectGateConvergenceEvidence` to the first few waiting gates (mesh_graph_view's G4 opt-in probe). */
|
|
273
|
-
probeGateEvidence?: boolean
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
export interface GraphViewQueryResponse {
|
|
277
|
-
/** `MeshGraphView[]`, JSON passthrough — see this section's content-boundary note. */
|
|
278
|
-
graphs: readonly Record<string, unknown>[]
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export function isGraphViewQueryRequest(value: unknown): value is GraphViewQueryRequest {
|
|
282
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'graphId', 'batchId', 'activeOnly', 'limit', 'probeGateEvidence'])) return false
|
|
283
|
-
if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId)) return false
|
|
284
|
-
if (value.graphId !== undefined && !isEvidenceIdentifier(value.graphId)) return false
|
|
285
|
-
if (value.batchId !== undefined && !isEvidenceIdentifier(value.batchId)) return false
|
|
286
|
-
if (value.limit !== undefined && !isNonNegativeInt(value.limit)) return false
|
|
287
|
-
return isOptionalBoolean(value.activeOnly) && isOptionalBoolean(value.probeGateEvidence)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
export function decodeGraphViewQueryRequest(value: unknown): GraphViewQueryRequest | null {
|
|
291
|
-
return isGraphViewQueryRequest(value) ? value : null
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function isGraphViewQueryResponse(value: unknown): value is GraphViewQueryResponse {
|
|
295
|
-
return isRecord(value) && hasOnlyKeys(value, ['graphs']) && isRecordArray(value.graphs)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
export function decodeGraphViewQueryResponse(value: unknown): GraphViewQueryResponse | null {
|
|
299
|
-
return isGraphViewQueryResponse(value) ? value : null
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// ─── task_stats_query ───────────────────────────────────────────────────────
|
|
303
|
-
//
|
|
304
|
-
// C-W9c: replaces the in-process `computeMeshTaskStats` / `computeMeshMissionStats`
|
|
305
|
-
// (daemon-core `mesh-task-stats.ts`) — both scan the daemon's queue + local
|
|
306
|
-
// records, so both move behind one command. Exactly one of `taskIds` /
|
|
307
|
-
// `missionId` selects the per-task mode; `missionId` alone with `rollup: true`
|
|
308
|
-
// additionally returns the mission-level aggregate.
|
|
309
|
-
|
|
310
|
-
export interface TaskStatsQueryRequest {
|
|
311
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
312
|
-
meshId: string
|
|
313
|
-
taskIds?: readonly string[]
|
|
314
|
-
missionId?: string
|
|
315
|
-
tail?: number
|
|
316
|
-
/** Also compute `computeMeshMissionStats` — requires `missionId`. */
|
|
317
|
-
rollup?: boolean
|
|
318
|
-
/**
|
|
319
|
-
* Batch rollups: `computeMeshMissionStats` for every id, over ONE queue read and
|
|
320
|
-
* ONE record read (answers `missions`, `tasks` empty). Exclusive with
|
|
321
|
-
* `taskIds`/`missionId`. An older daemon rejects the key.
|
|
322
|
-
*/
|
|
323
|
-
missionIds?: readonly string[]
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
export interface TaskStatsQueryResponse {
|
|
327
|
-
/** `MeshTaskStats[]`, JSON passthrough. */
|
|
328
|
-
tasks: readonly Record<string, unknown>[]
|
|
329
|
-
/** `MeshMissionStats`, JSON passthrough; present only when `rollup: true` was requested and satisfiable. */
|
|
330
|
-
mission?: Record<string, unknown>
|
|
331
|
-
/** Batch mode: `MeshMissionStats` per requested mission id, JSON passthrough. */
|
|
332
|
-
missions?: Record<string, Record<string, unknown>>
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
function isTaskStatsQueryRequest(value: unknown): value is TaskStatsQueryRequest {
|
|
336
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'taskIds', 'missionId', 'tail', 'rollup', 'missionIds'])) return false
|
|
337
|
-
if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId)) return false
|
|
338
|
-
if (value.taskIds !== undefined) {
|
|
339
|
-
if (!Array.isArray(value.taskIds) || value.taskIds.length === 0 || !value.taskIds.every(isEvidenceIdentifier)) return false
|
|
340
|
-
}
|
|
341
|
-
if (value.missionId !== undefined && !isEvidenceIdentifier(value.missionId)) return false
|
|
342
|
-
if (value.tail !== undefined && !isNonNegativeInt(value.tail)) return false
|
|
343
|
-
if (value.missionIds !== undefined) {
|
|
344
|
-
if (!Array.isArray(value.missionIds) || value.missionIds.length === 0 || !value.missionIds.every(isEvidenceIdentifier)) return false
|
|
345
|
-
if (value.taskIds !== undefined || value.missionId !== undefined) return false
|
|
346
|
-
}
|
|
347
|
-
if (value.rollup === true && value.missionId === undefined) return false
|
|
348
|
-
return isOptionalBoolean(value.rollup)
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
export function decodeTaskStatsQueryRequest(value: unknown): TaskStatsQueryRequest | null {
|
|
352
|
-
return isTaskStatsQueryRequest(value) ? value : null
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function isTaskStatsQueryResponse(value: unknown): value is TaskStatsQueryResponse {
|
|
356
|
-
return isRecord(value) && hasOnlyKeys(value, ['tasks', 'mission', 'missions']) && isRecordArray(value.tasks) && isOptionalRecord(value.mission)
|
|
357
|
-
&& (value.missions === undefined || (isRecord(value.missions) && Object.values(value.missions).every(isRecord)))
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
export function decodeTaskStatsQueryResponse(value: unknown): TaskStatsQueryResponse | null {
|
|
361
|
-
return isTaskStatsQueryResponse(value) ? value : null
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// ─── prune_stale_direct ─────────────────────────────────────────────────────
|
|
365
|
-
//
|
|
366
|
-
// C-W9c: replaces the in-process `pruneStaleDirectDispatches` call
|
|
367
|
-
// (`mesh_cleanup_sessions` mode=prune_stale_direct, `mesh-tools-session.ts`). Unlike the other
|
|
368
|
-
// C-W9 commands this one does NOT take its inputs (queue/records/direct
|
|
369
|
-
// dispatches) on the wire — the daemon already reads them itself
|
|
370
|
-
// (`getQueue`/`readLocalRecords`/`getActiveDirectDispatches`), so the request
|
|
371
|
-
// carries only the decision knobs. `closeDispatches` (the C-W8 `turn_cancel`
|
|
372
|
-
// callback) also runs daemon-side now, since it is the same process.
|
|
373
|
-
|
|
374
|
-
export interface PruneStaleDirectRequest {
|
|
375
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
376
|
-
meshId: string
|
|
377
|
-
execute?: boolean
|
|
378
|
-
includeTerminal?: boolean
|
|
379
|
-
source?: string
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
export interface PruneStaleDirectResponse {
|
|
383
|
-
mode: 'execute' | 'dry_run'
|
|
384
|
-
includeTerminal: boolean
|
|
385
|
-
candidateCount: number
|
|
386
|
-
/** `MeshActiveWorkRecord[]`, JSON passthrough. */
|
|
387
|
-
prunable: readonly Record<string, unknown>[]
|
|
388
|
-
prunedCount: number
|
|
389
|
-
preservedUnacknowledged: readonly Record<string, unknown>[]
|
|
390
|
-
preservedLedgerOnly: readonly Record<string, unknown>[]
|
|
391
|
-
preservedNotOrphan: readonly Record<string, unknown>[]
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function isPruneStaleDirectRequest(value: unknown): value is PruneStaleDirectRequest {
|
|
395
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'execute', 'includeTerminal', 'source'])) return false
|
|
396
|
-
if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId)) return false
|
|
397
|
-
if (value.source !== undefined && typeof value.source !== 'string') return false
|
|
398
|
-
return isOptionalBoolean(value.execute) && isOptionalBoolean(value.includeTerminal)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
export function decodePruneStaleDirectRequest(value: unknown): PruneStaleDirectRequest | null {
|
|
402
|
-
return isPruneStaleDirectRequest(value) ? value : null
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function isPruneStaleDirectResponse(value: unknown): value is PruneStaleDirectResponse {
|
|
406
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['mode', 'includeTerminal', 'candidateCount', 'prunable', 'prunedCount', 'preservedUnacknowledged', 'preservedLedgerOnly', 'preservedNotOrphan'])) return false
|
|
407
|
-
if (value.mode !== 'execute' && value.mode !== 'dry_run') return false
|
|
408
|
-
if (typeof value.includeTerminal !== 'boolean') return false
|
|
409
|
-
if (!isNonNegativeInt(value.candidateCount) || !isNonNegativeInt(value.prunedCount)) return false
|
|
410
|
-
return isRecordArray(value.prunable) && isRecordArray(value.preservedUnacknowledged)
|
|
411
|
-
&& isRecordArray(value.preservedLedgerOnly) && isRecordArray(value.preservedNotOrphan)
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
export function decodePruneStaleDirectResponse(value: unknown): PruneStaleDirectResponse | null {
|
|
415
|
-
return isPruneStaleDirectResponse(value) ? value : null
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// ─── orphaned_pin_notify ────────────────────────────────────────────────────
|
|
419
|
-
//
|
|
420
|
-
// C-W9c: replaces the in-process `notifyCoordinatorOfOrphanedPins` call
|
|
421
|
-
// (`mesh_queue_cancel` tool, `mesh-tools-queue.ts`, CANCEL-ORPHANS-PINNED-TASK).
|
|
422
|
-
// The core reads the daemon's live queue (`getQueue`) and, when it finds
|
|
423
|
-
// orphans, calls `notifyMeshCoordinator` to page the coordinator — both are
|
|
424
|
-
// daemon-only concerns, so the whole call moves, not just its queue read.
|
|
425
|
-
// `title` is a task-message-derived label (first line, truncated) — free text,
|
|
426
|
-
// hence local IPC only, same rationale as `record_local`'s payload.
|
|
427
|
-
|
|
428
|
-
export interface OrphanedPinNotifyRequest {
|
|
429
|
-
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
430
|
-
meshId: string
|
|
431
|
-
stoppedSessionId: string
|
|
432
|
-
excludeTaskId?: string
|
|
433
|
-
cause?: string
|
|
434
|
-
nodeId?: string
|
|
435
|
-
coordinatorSessionId?: string
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
export interface OrphanedPinnedTaskWire {
|
|
439
|
-
taskId: string
|
|
440
|
-
title: string
|
|
441
|
-
targetSessionId: string
|
|
442
|
-
targetNodeId?: string
|
|
443
|
-
missionId?: string
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
export interface OrphanedPinNotifyResponse {
|
|
447
|
-
orphans: readonly OrphanedPinnedTaskWire[]
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
function isOrphanedPinnedTaskWire(value: unknown): value is OrphanedPinnedTaskWire {
|
|
451
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['taskId', 'title', 'targetSessionId', 'targetNodeId', 'missionId'])) return false
|
|
452
|
-
if (!isEvidenceIdentifier(value.taskId) || !isEvidenceIdentifier(value.targetSessionId)) return false
|
|
453
|
-
if (typeof value.title !== 'string') return false
|
|
454
|
-
if (value.targetNodeId !== undefined && !isEvidenceIdentifier(value.targetNodeId)) return false
|
|
455
|
-
return value.missionId === undefined || isEvidenceIdentifier(value.missionId)
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
function isOrphanedPinNotifyRequest(value: unknown): value is OrphanedPinNotifyRequest {
|
|
459
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'stoppedSessionId', 'excludeTaskId', 'cause', 'nodeId', 'coordinatorSessionId'])) return false
|
|
460
|
-
if (value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId) || !isEvidenceIdentifier(value.stoppedSessionId)) return false
|
|
461
|
-
if (value.excludeTaskId !== undefined && !isEvidenceIdentifier(value.excludeTaskId)) return false
|
|
462
|
-
if (value.cause !== undefined && typeof value.cause !== 'string') return false
|
|
463
|
-
if (value.nodeId !== undefined && !isEvidenceIdentifier(value.nodeId)) return false
|
|
464
|
-
return value.coordinatorSessionId === undefined || isEvidenceIdentifier(value.coordinatorSessionId)
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
export function decodeOrphanedPinNotifyRequest(value: unknown): OrphanedPinNotifyRequest | null {
|
|
468
|
-
return isOrphanedPinNotifyRequest(value) ? value : null
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function isOrphanedPinNotifyResponse(value: unknown): value is OrphanedPinNotifyResponse {
|
|
472
|
-
return isRecord(value) && hasOnlyKeys(value, ['orphans']) && Array.isArray(value.orphans) && value.orphans.every(isOrphanedPinnedTaskWire)
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
export function decodeOrphanedPinNotifyResponse(value: unknown): OrphanedPinNotifyResponse | null {
|
|
476
|
-
return isOrphanedPinNotifyResponse(value) ? value : null
|
|
477
|
-
}
|