@adhdev/mesh-shared 1.0.82-rc.4 → 1.0.82-rc.40
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/assistant-protocol.d.ts +161 -6
- package/dist/decision-push-relay.d.ts +20 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1197 -118
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1107 -118
- package/dist/index.mjs.map +1 -1
- package/dist/launch-defaults.d.ts +196 -0
- package/dist/mesh-tool-names.d.ts +2 -2
- package/dist/mesh-vocabulary.d.ts +13 -4
- package/dist/mission-git-state.d.ts +83 -0
- package/dist/mission-items.d.ts +202 -0
- package/dist/node-facts.d.ts +75 -1
- package/dist/outbound-message.d.ts +9 -2
- package/dist/path-ownership.d.ts +1 -1
- package/dist/protocol/daemon-server.d.ts +42 -2
- package/dist/protocol/dashboard-daemon-p2p.d.ts +1 -1
- package/dist/protocol/dashboard-server.d.ts +20 -2
- package/dist/provider-approval.d.ts +36 -0
- package/dist/provider-question.d.ts +19 -0
- package/dist/quota-freshness.d.ts +65 -0
- package/dist/relay-reprobe.d.ts +52 -0
- package/dist/release.d.ts +90 -0
- package/dist/role-launch.d.ts +11 -0
- package/dist/session-launch.d.ts +10 -22
- package/dist/slot-proposal.d.ts +14 -0
- package/dist/task-source-phase.d.ts +75 -0
- package/dist/turn-evidence.d.ts +29 -13
- package/dist/turn-ipc-mission.d.ts +68 -1
- package/dist/turn-ipc-queue.d.ts +19 -0
- package/dist/turn-ipc.d.ts +2 -2
- package/dist/types.d.ts +1 -0
- package/dist/worker-protocol.d.ts +1 -1
- package/package.json +1 -1
- package/src/assistant-protocol.ts +302 -6
- package/src/daemon-normalize.ts +7 -5
- package/src/decision-push-relay.ts +23 -0
- package/src/index.ts +12 -0
- package/src/keyed-doc-delta.ts +9 -1
- package/src/launch-defaults.ts +170 -0
- package/src/mesh-tool-names.ts +16 -0
- package/src/mesh-vocabulary.ts +12 -2
- package/src/mission-git-state.ts +112 -0
- package/src/mission-items.ts +421 -0
- package/src/node-facts.ts +70 -1
- package/src/outbound-message.ts +19 -0
- package/src/path-ownership.ts +4 -6
- package/src/protocol/daemon-server.ts +37 -2
- package/src/protocol/dashboard-server.ts +13 -2
- package/src/provider-approval.ts +59 -0
- package/src/provider-question.ts +31 -0
- package/src/quota-freshness.ts +139 -0
- package/src/relay-reprobe.ts +117 -0
- package/src/release.ts +121 -0
- package/src/role-launch.ts +51 -0
- package/src/session-launch.ts +17 -1
- package/src/slot-proposal.ts +61 -2
- package/src/task-source-phase.ts +61 -0
- package/src/turn-evidence.ts +27 -20
- package/src/turn-ipc-mission.ts +121 -11
- package/src/turn-ipc-queue.ts +29 -1
- package/src/turn-ipc-turn.ts +1 -1
- package/src/turn-ipc.ts +1 -0
- package/src/types.ts +1 -0
- package/src/worker-protocol.ts +15 -0
package/src/turn-ipc-mission.ts
CHANGED
|
@@ -85,9 +85,46 @@ export interface MissionUpsertRequest {
|
|
|
85
85
|
* this wire guard only checks SHAPE, not that decision.
|
|
86
86
|
*/
|
|
87
87
|
brief?: MissionBriefWire | null
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Structured-missions bundle A (design 2026-10-28 §5.1). Every key below is optional
|
|
90
|
+
* and ABSENT on a legacy call. `structured: true` is the caller's capability flag: the
|
|
91
|
+
* daemon only puts the new keys on the response (mission summary/outcome/version,
|
|
92
|
+
* `result`) when it is set, so an older mcp-server that never sends it keeps decoding
|
|
93
|
+
* byte-identical responses (mixed-version fleets, design §13.1).
|
|
94
|
+
*/
|
|
95
|
+
structured?: boolean
|
|
96
|
+
summary?: string
|
|
97
|
+
/** One user/operator-visible sentence for the release CHANGELOG draft (design 2026-11-01-release-objects L5). `''` clears. */
|
|
98
|
+
releaseNote?: string
|
|
99
|
+
outcome?: string
|
|
100
|
+
outcomeReason?: string
|
|
101
|
+
/** Mission id open items move to when this call closes the mission (default: the carry-over mission). */
|
|
102
|
+
carryOverTo?: string
|
|
103
|
+
/** Route to the mesh's auto-created carry-over mission instead of `id`/`title` (decision requests without a mission). `title` may then be empty. */
|
|
104
|
+
toCarryOver?: boolean
|
|
105
|
+
itemsAdd?: readonly Record<string, unknown>[]
|
|
106
|
+
itemsUpdate?: readonly Record<string, unknown>[]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface MeshMissionOpenItemCountsWire {
|
|
110
|
+
remaining: number
|
|
111
|
+
live_check: number
|
|
112
|
+
decision: number
|
|
113
|
+
evidence: number
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Structured-missions fields (only with `structured: true` on the request). */
|
|
117
|
+
export interface MeshMissionStructuredWire {
|
|
118
|
+
summary?: string
|
|
119
|
+
releaseNote?: string
|
|
120
|
+
outcome?: string
|
|
121
|
+
outcomeReason?: string
|
|
122
|
+
version?: number
|
|
123
|
+
carryOver?: boolean
|
|
124
|
+
openItems?: MeshMissionOpenItemCountsWire
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface MeshMissionRecordWire extends MeshMissionStructuredWire {
|
|
91
128
|
id: string
|
|
92
129
|
meshId: string
|
|
93
130
|
title: string
|
|
@@ -99,10 +136,30 @@ export interface MeshMissionRecordWire {
|
|
|
99
136
|
|
|
100
137
|
export interface MissionUpsertResponse {
|
|
101
138
|
mission: MeshMissionRecordWire
|
|
139
|
+
/** Structured calls only: per-item results, movedItems/movedTo, restoredItems, ownerConfirmationItemId, warnings. */
|
|
140
|
+
result?: Record<string, unknown>
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const STRUCTURED_MISSION_KEYS = ['summary', 'releaseNote', 'outcome', 'outcomeReason', 'version', 'carryOver', 'openItems'] as const
|
|
144
|
+
|
|
145
|
+
function isOpenItemCountsWire(value: unknown): value is MeshMissionOpenItemCountsWire {
|
|
146
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ['remaining', 'live_check', 'decision', 'evidence'])) return false
|
|
147
|
+
return (['remaining', 'live_check', 'decision', 'evidence'] as const).every((k) => isNonNegativeInt(value[k]))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function isStructuredMissionFields(value: Record<string, unknown>): boolean {
|
|
151
|
+
for (const k of ['summary', 'releaseNote', 'outcome', 'outcomeReason'] as const) {
|
|
152
|
+
if (value[k] !== undefined && typeof value[k] !== 'string') return false
|
|
153
|
+
}
|
|
154
|
+
if (value.version !== undefined && !isNonNegativeInt(value.version)) return false
|
|
155
|
+
if (value.carryOver !== undefined && typeof value.carryOver !== 'boolean') return false
|
|
156
|
+
if (value.openItems !== undefined && !isOpenItemCountsWire(value.openItems)) return false
|
|
157
|
+
return true
|
|
102
158
|
}
|
|
103
159
|
|
|
104
160
|
function isMeshMissionRecordWire(value: unknown): value is MeshMissionRecordWire {
|
|
105
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['id', 'meshId', 'title', 'goal', 'status', 'brief'])) return false
|
|
161
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ['id', 'meshId', 'title', 'goal', 'status', 'brief', ...STRUCTURED_MISSION_KEYS])) return false
|
|
162
|
+
if (!isStructuredMissionFields(value)) return false
|
|
106
163
|
if (!isEvidenceIdentifier(value.id) || !isEvidenceIdentifier(value.meshId)) return false
|
|
107
164
|
if (typeof value.title !== 'string' || typeof value.goal !== 'string') return false
|
|
108
165
|
if (!isMeshMissionStatusValue(value.status)) return false
|
|
@@ -111,11 +168,24 @@ function isMeshMissionRecordWire(value: unknown): value is MeshMissionRecordWire
|
|
|
111
168
|
}
|
|
112
169
|
|
|
113
170
|
function isMissionUpsertRequest(value: unknown): value is MissionUpsertRequest {
|
|
114
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'id', 'title', 'goal', 'status', 'brief'])) return false
|
|
171
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'id', 'title', 'goal', 'status', 'brief', 'structured', 'summary', 'releaseNote', 'outcome', 'outcomeReason', 'carryOverTo', 'toCarryOver', 'itemsAdd', 'itemsUpdate'])) return false
|
|
115
172
|
if (value.v !== TURN_IPC_PROTOCOL_VERSION) return false
|
|
116
173
|
if (!isEvidenceIdentifier(value.meshId)) return false
|
|
117
174
|
if (value.id !== undefined && !isEvidenceIdentifier(value.id)) return false
|
|
118
|
-
if (typeof value.title !== 'string'
|
|
175
|
+
if (typeof value.title !== 'string') return false
|
|
176
|
+
// An empty title is allowed only when the daemon can supply one: an existing mission (id) or the carry-over mission.
|
|
177
|
+
if (value.title.trim().length === 0 && value.toCarryOver !== true && value.id === undefined) return false
|
|
178
|
+
if (value.structured !== undefined && typeof value.structured !== 'boolean') return false
|
|
179
|
+
if (value.toCarryOver !== undefined && typeof value.toCarryOver !== 'boolean') return false
|
|
180
|
+
for (const k of ['summary', 'releaseNote', 'outcome', 'outcomeReason'] as const) {
|
|
181
|
+
if (value[k] !== undefined && typeof value[k] !== 'string') return false
|
|
182
|
+
}
|
|
183
|
+
if (value.carryOverTo !== undefined && !isEvidenceIdentifier(value.carryOverTo)) return false
|
|
184
|
+
for (const k of ['itemsAdd', 'itemsUpdate'] as const) {
|
|
185
|
+
const list = value[k]
|
|
186
|
+
// Bounded only to keep the IPC message sane; per-entry shape/size is clamped (never rejected) by the daemon.
|
|
187
|
+
if (list !== undefined && (!Array.isArray(list) || list.length > 500 || !list.every(isRecord))) return false
|
|
188
|
+
}
|
|
119
189
|
if (value.goal !== undefined && typeof value.goal !== 'string') return false
|
|
120
190
|
if (value.status !== undefined && !isMeshMissionStatusValue(value.status)) return false
|
|
121
191
|
if (value.brief !== undefined && value.brief !== null && !isMissionBriefWire(value.brief)) return false
|
|
@@ -127,7 +197,8 @@ export function decodeMissionUpsertRequest(value: unknown): MissionUpsertRequest
|
|
|
127
197
|
}
|
|
128
198
|
|
|
129
199
|
export function isMissionUpsertResponse(value: unknown): value is MissionUpsertResponse {
|
|
130
|
-
return isRecord(value) && hasOnlyKeys(value, ['mission']) && isMeshMissionRecordWire(value.mission)
|
|
200
|
+
return isRecord(value) && hasOnlyKeys(value, ['mission', 'result']) && isMeshMissionRecordWire(value.mission)
|
|
201
|
+
&& (value.result === undefined || isRecord(value.result))
|
|
131
202
|
}
|
|
132
203
|
|
|
133
204
|
export function decodeMissionUpsertResponse(value: unknown): MissionUpsertResponse | null {
|
|
@@ -207,6 +278,12 @@ export interface MissionListQueryRequest {
|
|
|
207
278
|
* option except `historyIdLimit` is ignored. An older daemon rejects the key.
|
|
208
279
|
*/
|
|
209
280
|
meshStatusView?: 'compact' | 'verbose'
|
|
281
|
+
/** Structured-missions capability flag (see MissionUpsertRequest.structured): rows then carry summary/outcome/openItems/carryOver/version. */
|
|
282
|
+
structured?: boolean
|
|
283
|
+
/** Structured only: attach each mission's items. */
|
|
284
|
+
items?: boolean
|
|
285
|
+
/** Structured only: attach each mission's change history (bounded tail). */
|
|
286
|
+
history?: boolean
|
|
210
287
|
}
|
|
211
288
|
|
|
212
289
|
export interface MeshMissionTaskAggregateWire {
|
|
@@ -221,6 +298,8 @@ export interface MeshMissionTaskAggregateWire {
|
|
|
221
298
|
}
|
|
222
299
|
|
|
223
300
|
export interface MeshMissionStatsWire {
|
|
301
|
+
/** Present on peers that project proven blocked outcomes separately. */
|
|
302
|
+
blocked?: number
|
|
224
303
|
missionId: string
|
|
225
304
|
taskCount: number
|
|
226
305
|
completed: number
|
|
@@ -246,6 +325,16 @@ export interface MissionListSummaryVerboseWire {
|
|
|
246
325
|
createdAt?: string
|
|
247
326
|
updatedAt?: string
|
|
248
327
|
closeCandidateEmittedAt?: string | null
|
|
328
|
+
/** Structured lists only. */
|
|
329
|
+
summary?: string
|
|
330
|
+
releaseNote?: string
|
|
331
|
+
outcome?: string
|
|
332
|
+
outcomeReason?: string
|
|
333
|
+
version?: number
|
|
334
|
+
carryOver?: boolean
|
|
335
|
+
openItems?: MeshMissionOpenItemCountsWire
|
|
336
|
+
items?: readonly Record<string, unknown>[]
|
|
337
|
+
history?: readonly Record<string, unknown>[]
|
|
249
338
|
}
|
|
250
339
|
|
|
251
340
|
/** Compact (default) mission row. `goalPreview`/`goalTruncated` present, `goal` absent. */
|
|
@@ -264,6 +353,16 @@ export interface MissionListSummarySlimWire {
|
|
|
264
353
|
createdAt?: string
|
|
265
354
|
updatedAt?: string
|
|
266
355
|
closeCandidateEmittedAt?: string | null
|
|
356
|
+
/** Structured lists only. */
|
|
357
|
+
summary?: string
|
|
358
|
+
releaseNote?: string
|
|
359
|
+
outcome?: string
|
|
360
|
+
outcomeReason?: string
|
|
361
|
+
version?: number
|
|
362
|
+
carryOver?: boolean
|
|
363
|
+
openItems?: MeshMissionOpenItemCountsWire
|
|
364
|
+
items?: readonly Record<string, unknown>[]
|
|
365
|
+
history?: readonly Record<string, unknown>[]
|
|
267
366
|
}
|
|
268
367
|
|
|
269
368
|
export type MissionListSummaryWire = MissionListSummaryVerboseWire | MissionListSummarySlimWire
|
|
@@ -284,7 +383,10 @@ export interface MissionListQueryResponse {
|
|
|
284
383
|
}
|
|
285
384
|
|
|
286
385
|
function isMissionListQueryRequest(value: unknown): value is MissionListQueryRequest {
|
|
287
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'statuses', 'verbose', 'withStats', 'limit', 'historyIdLimit', 'meshStatusView'])) return false
|
|
386
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'statuses', 'verbose', 'withStats', 'limit', 'historyIdLimit', 'meshStatusView', 'structured', 'items', 'history'])) return false
|
|
387
|
+
for (const k of ['structured', 'items', 'history'] as const) {
|
|
388
|
+
if (value[k] !== undefined && typeof value[k] !== 'boolean') return false
|
|
389
|
+
}
|
|
288
390
|
if (value.meshStatusView !== undefined && value.meshStatusView !== 'compact' && value.meshStatusView !== 'verbose') return false
|
|
289
391
|
if (value.v !== TURN_IPC_PROTOCOL_VERSION) return false
|
|
290
392
|
if (!isEvidenceIdentifier(value.meshId)) return false
|
|
@@ -313,12 +415,13 @@ function isMeshMissionTaskAggregateWire(value: unknown): value is MeshMissionTas
|
|
|
313
415
|
}
|
|
314
416
|
|
|
315
417
|
function isMeshMissionStatsWire(value: unknown): value is MeshMissionStatsWire {
|
|
316
|
-
const keys = ['missionId', 'taskCount', 'completed', 'failed', 'totalDurationMs', 'wallClockMs', 'retries', 'incompleteTaskIds'] as const
|
|
418
|
+
const keys = ['missionId', 'taskCount', 'completed', 'failed', 'blocked', 'totalDurationMs', 'wallClockMs', 'retries', 'incompleteTaskIds'] as const
|
|
317
419
|
if (!isRecord(value) || !hasOnlyKeys(value, keys)) return false
|
|
318
420
|
if (!isEvidenceIdentifier(value.missionId)) return false
|
|
319
421
|
for (const k of ['taskCount', 'completed', 'failed', 'totalDurationMs', 'retries'] as const) {
|
|
320
422
|
if (!isNonNegativeInt(value[k])) return false
|
|
321
423
|
}
|
|
424
|
+
if (value.blocked !== undefined && !isNonNegativeInt(value.blocked)) return false
|
|
322
425
|
if (value.wallClockMs !== null && !isNonNegativeInt(value.wallClockMs)) return false
|
|
323
426
|
if (!Array.isArray(value.incompleteTaskIds) || !value.incompleteTaskIds.every(isEvidenceIdentifier)) return false
|
|
324
427
|
return true
|
|
@@ -329,7 +432,7 @@ function isMissionListSummaryWire(value: unknown): value is MissionListSummaryWi
|
|
|
329
432
|
const hasGoal = 'goal' in value
|
|
330
433
|
const hasPreview = 'goalPreview' in value && 'goalTruncated' in value
|
|
331
434
|
if (hasGoal === hasPreview) return false // exactly one of the two shapes
|
|
332
|
-
const baseKeys = ['id', 'meshId', 'title', 'status', 'tasks', 'stats', 'brief', 'createdAt', 'updatedAt', 'closeCandidateEmittedAt'] as const
|
|
435
|
+
const baseKeys = ['id', 'meshId', 'title', 'status', 'tasks', 'stats', 'brief', 'createdAt', 'updatedAt', 'closeCandidateEmittedAt', ...STRUCTURED_MISSION_KEYS, 'items', 'history'] as const
|
|
333
436
|
const allowed = hasGoal ? [...baseKeys, 'goal'] : [...baseKeys, 'goalPreview', 'goalTruncated']
|
|
334
437
|
if (!hasOnlyKeys(value, allowed)) return false
|
|
335
438
|
if (!isEvidenceIdentifier(value.id) || !isEvidenceIdentifier(value.meshId)) return false
|
|
@@ -343,6 +446,10 @@ function isMissionListSummaryWire(value: unknown): value is MissionListSummaryWi
|
|
|
343
446
|
if (value.createdAt !== undefined && typeof value.createdAt !== 'string') return false
|
|
344
447
|
if (value.updatedAt !== undefined && typeof value.updatedAt !== 'string') return false
|
|
345
448
|
if (value.closeCandidateEmittedAt !== undefined && value.closeCandidateEmittedAt !== null && typeof value.closeCandidateEmittedAt !== 'string') return false
|
|
449
|
+
if (!isStructuredMissionFields(value)) return false
|
|
450
|
+
for (const k of ['items', 'history'] as const) {
|
|
451
|
+
if (value[k] !== undefined && (!Array.isArray(value[k]) || !(value[k] as unknown[]).every(isRecord))) return false
|
|
452
|
+
}
|
|
346
453
|
return true
|
|
347
454
|
}
|
|
348
455
|
|
|
@@ -580,6 +687,8 @@ export interface LedgerQueryEntryWire {
|
|
|
580
687
|
}
|
|
581
688
|
|
|
582
689
|
export interface LedgerQuerySummaryWire {
|
|
690
|
+
/** Absent on older peers; never folded into failed by the decoder. */
|
|
691
|
+
taskBlocked?: number
|
|
583
692
|
meshId: string
|
|
584
693
|
totalEntries: number
|
|
585
694
|
taskDispatched: number
|
|
@@ -625,12 +734,13 @@ function isLedgerQueryEntryWire(value: unknown): value is LedgerQueryEntryWire {
|
|
|
625
734
|
}
|
|
626
735
|
|
|
627
736
|
function isLedgerQuerySummaryWire(value: unknown): value is LedgerQuerySummaryWire {
|
|
628
|
-
const keys = ['meshId', 'totalEntries', 'taskDispatched', 'taskCompleted', 'taskFailed', 'taskStalled', 'sessionLaunched', 'checkpointCreated', 'lastActivityAt', 'recentFailures'] as const
|
|
737
|
+
const keys = ['meshId', 'totalEntries', 'taskDispatched', 'taskCompleted', 'taskFailed', 'taskBlocked', 'taskStalled', 'sessionLaunched', 'checkpointCreated', 'lastActivityAt', 'recentFailures'] as const
|
|
629
738
|
if (!isRecord(value) || !hasOnlyKeys(value, keys)) return false
|
|
630
739
|
if (!isEvidenceIdentifier(value.meshId)) return false
|
|
631
740
|
for (const k of ['totalEntries', 'taskDispatched', 'taskCompleted', 'taskFailed', 'taskStalled', 'sessionLaunched', 'checkpointCreated', 'recentFailures'] as const) {
|
|
632
741
|
if (!isNonNegativeInt(value[k])) return false
|
|
633
742
|
}
|
|
743
|
+
if (value.taskBlocked !== undefined && !isNonNegativeInt(value.taskBlocked)) return false
|
|
634
744
|
if (value.lastActivityAt !== null && typeof value.lastActivityAt !== 'string') return false
|
|
635
745
|
return true
|
|
636
746
|
}
|
package/src/turn-ipc-queue.ts
CHANGED
|
@@ -135,6 +135,8 @@ export interface QueueQueryResponse {
|
|
|
135
135
|
counts?: Record<string, number>
|
|
136
136
|
/** `withCounts` + `historicalOlderThanMs`: terminal rows older than that age. */
|
|
137
137
|
oldHistoricalCount?: number
|
|
138
|
+
/** Included in counts.blocked, derived from proven worker reports; stored failed rows stay unchanged. */
|
|
139
|
+
legacyReportedBlockedCount?: number
|
|
138
140
|
/** `withDependencyHeads`. */
|
|
139
141
|
dependencyHeads?: readonly QueueDependencyHeadWire[]
|
|
140
142
|
}
|
|
@@ -159,9 +161,10 @@ function isQueueDependencyHeadWire(value: unknown): value is QueueDependencyHead
|
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
function isQueueQueryResponse(value: unknown): value is QueueQueryResponse {
|
|
162
|
-
if (!isRecord(value) || !hasOnlyKeys(value, ['entries', 'counts', 'oldHistoricalCount', 'dependencyHeads'])) return false
|
|
164
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ['entries', 'counts', 'oldHistoricalCount', 'legacyReportedBlockedCount', 'dependencyHeads'])) return false
|
|
163
165
|
if (!Array.isArray(value.entries) || !value.entries.every(isQueueEntryWire)) return false
|
|
164
166
|
if (value.counts !== undefined && (!isRecord(value.counts) || !Object.values(value.counts).every(isNonNegativeInt))) return false
|
|
167
|
+
if (value.legacyReportedBlockedCount !== undefined && !isNonNegativeInt(value.legacyReportedBlockedCount)) return false
|
|
165
168
|
if (value.oldHistoricalCount !== undefined && !isNonNegativeInt(value.oldHistoricalCount)) return false
|
|
166
169
|
if (value.dependencyHeads !== undefined && (!Array.isArray(value.dependencyHeads) || !value.dependencyHeads.every(isQueueDependencyHeadWire))) return false
|
|
167
170
|
return true
|
|
@@ -491,3 +494,28 @@ function isRecoveryContextQueryResponse(value: unknown): value is RecoveryContex
|
|
|
491
494
|
export function decodeRecoveryContextQueryResponse(value: unknown): RecoveryContextQueryResponse | null {
|
|
492
495
|
return isRecoveryContextQueryResponse(value) ? value : null
|
|
493
496
|
}
|
|
497
|
+
|
|
498
|
+
// ── queue_revise: host-authenticated pending CAS, never a force requeue ──
|
|
499
|
+
export interface QueueReviseRequest {
|
|
500
|
+
v: typeof TURN_IPC_PROTOCOL_VERSION
|
|
501
|
+
meshId: string
|
|
502
|
+
taskId: string
|
|
503
|
+
expectedRevision: number
|
|
504
|
+
patch: { message?: string; ownedPaths?: string[]; phase?: 'source' | 'integration'; dependsOn?: string[]; routingPreferences?: import('./task-source-phase').MeshRoutingPreferences; review?: import('./task-source-phase').MeshReviewConstraint }
|
|
505
|
+
}
|
|
506
|
+
export type QueueReviseResponse = QueueRequeueResponse
|
|
507
|
+
export function decodeQueueReviseRequest(value: unknown): QueueReviseRequest | null {
|
|
508
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ['v', 'meshId', 'taskId', 'expectedRevision', 'patch'])
|
|
509
|
+
|| value.v !== TURN_IPC_PROTOCOL_VERSION || !isEvidenceIdentifier(value.meshId) || !isEvidenceIdentifier(value.taskId)
|
|
510
|
+
|| !Number.isSafeInteger(value.expectedRevision) || (value.expectedRevision as number) < 0
|
|
511
|
+
|| !isRecord(value.patch) || !Object.keys(value.patch).length
|
|
512
|
+
|| !hasOnlyKeys(value.patch, ['message', 'ownedPaths', 'phase', 'dependsOn', 'routingPreferences', 'review'])) return null
|
|
513
|
+
const p = value.patch
|
|
514
|
+
if (p.message !== undefined && (typeof p.message !== 'string' || !p.message.trim())) return null
|
|
515
|
+
if (p.phase !== undefined && p.phase !== 'source' && p.phase !== 'integration') return null
|
|
516
|
+
for (const key of ['ownedPaths', 'dependsOn']) {
|
|
517
|
+
if (p[key] !== undefined && (!Array.isArray(p[key]) || !(p[key] as unknown[]).every(x => typeof x === 'string' && !!x.trim()))) return null
|
|
518
|
+
}
|
|
519
|
+
return value as unknown as QueueReviseRequest
|
|
520
|
+
}
|
|
521
|
+
export const decodeQueueReviseResponse = decodeQueueRequeueResponse
|
package/src/turn-ipc-turn.ts
CHANGED
|
@@ -66,7 +66,7 @@ function isTurnObserveResponse(value: unknown): value is TurnObserveResponse {
|
|
|
66
66
|
&& (value.outcome === undefined || isTurnOutcomeValue(value.outcome))
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const TURN_OUTCOMES_LOCAL = ['completed', 'failed', 'cancelled'] as const
|
|
69
|
+
const TURN_OUTCOMES_LOCAL = ['completed', 'blocked', 'failed', 'cancelled'] as const
|
|
70
70
|
function isTurnOutcomeValue(value: unknown): value is TurnOutcome {
|
|
71
71
|
return typeof value === 'string' && (TURN_OUTCOMES_LOCAL as readonly string[]).includes(value)
|
|
72
72
|
}
|
package/src/turn-ipc.ts
CHANGED
package/src/types.ts
CHANGED
package/src/worker-protocol.ts
CHANGED
|
@@ -40,6 +40,21 @@ export const WORKER_TOOLS = [
|
|
|
40
40
|
'git_status',
|
|
41
41
|
'git_log',
|
|
42
42
|
'git_diff',
|
|
43
|
+
'driver_list',
|
|
44
|
+
'driver_describe',
|
|
45
|
+
'driver_invoke',
|
|
46
|
+
'driver_scaffold',
|
|
47
|
+
'driver_validate',
|
|
48
|
+
'driver_export',
|
|
49
|
+
'driver_install',
|
|
50
|
+
'artifact_register',
|
|
51
|
+
'artifact_list',
|
|
52
|
+
'artifact_read',
|
|
53
|
+
// Contract versioning (Cut A): exact pinned-unit reads (E2), the explicit
|
|
54
|
+
// read claim (C1) and the authoritative head/notice pull.
|
|
55
|
+
'contract_read',
|
|
56
|
+
'contract_observe',
|
|
57
|
+
'contract_current',
|
|
43
58
|
] as const
|
|
44
59
|
|
|
45
60
|
export type WorkerTool = typeof WORKER_TOOLS[number]
|