@fitlab-ai/agent-infra 0.11.11 → 0.11.13

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 (154) hide show
  1. package/dist/bin/internal-cli.js +2 -2
  2. package/dist/lib/agent-clients/adapter.js +7 -1
  3. package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
  9. package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
  12. package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
  13. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
  14. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
  15. package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
  16. package/dist/lib/agent-clients/adapters/codex.js +40 -1
  17. package/dist/lib/agent-clients/adapters/index.js +2 -10
  18. package/dist/lib/agent-clients/tokens.js +3 -2
  19. package/dist/lib/internal/agent-client.js +132 -72
  20. package/dist/lib/internal/cli-route-inventory.js +2 -2
  21. package/dist/lib/internal/sandbox-control.js +1 -81
  22. package/dist/lib/internal/task-finalization.js +26 -19
  23. package/dist/lib/internal/task-lifecycle.js +1 -1
  24. package/dist/lib/internal/task-operation-registry.js +1 -1
  25. package/dist/lib/internal/task-validate.js +1 -1
  26. package/dist/lib/platform/github-client.js +33 -3
  27. package/dist/lib/platform/issues.js +18 -6
  28. package/dist/lib/runtime/agent-runtime.js +6 -29
  29. package/dist/lib/sandbox/commands/control-status.js +1 -0
  30. package/dist/lib/sandbox/commands/create.js +7 -2
  31. package/dist/lib/sandbox/commands/ls.js +4 -5
  32. package/dist/lib/sandbox/commands/rm.js +1 -1
  33. package/dist/lib/sandbox/control/agent-operation.js +34 -0
  34. package/dist/lib/sandbox/control/client.js +14 -201
  35. package/dist/lib/sandbox/control/executor.js +12 -125
  36. package/dist/lib/sandbox/control/lifecycle.js +66 -4
  37. package/dist/lib/sandbox/control/protocol.js +15 -54
  38. package/dist/lib/sandbox/control/server.js +216 -281
  39. package/dist/lib/sandbox/control/task-view.js +0 -13
  40. package/dist/lib/sandbox/mounts.js +9 -1
  41. package/dist/lib/sandbox/recovery.js +31 -10
  42. package/dist/lib/sandbox/removal.js +28 -5
  43. package/dist/lib/sandbox/workspace-identity.js +18 -6
  44. package/dist/lib/sandbox/workspace-view.js +56 -19
  45. package/dist/lib/task/commands/ls.js +3 -3
  46. package/dist/lib/task/control-authority.js +56 -264
  47. package/dist/lib/task/control-recovery.js +2 -3
  48. package/dist/lib/task/create.js +2 -2
  49. package/dist/lib/task/delegation-receipts.js +74 -161
  50. package/dist/lib/task/events.js +88 -44
  51. package/dist/lib/task/finalization.js +35 -6
  52. package/dist/lib/task/lifecycle.js +274 -115
  53. package/dist/lib/task/orchestration.js +201 -194
  54. package/dist/lib/task/runtime-paths.js +10 -0
  55. package/dist/lib/task/short-id.js +37 -27
  56. package/dist/lib/task/verification-engine.js +32 -26
  57. package/dist/lib/task/workspace-snapshot.js +1 -1
  58. package/dist/lifecycle-build-manifest.json +3 -3
  59. package/lib/agent-clients/adapter.ts +39 -0
  60. package/lib/agent-clients/adapters/claude-code.ts +5 -1
  61. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
  62. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
  63. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
  64. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
  65. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
  66. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
  67. package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
  68. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
  69. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
  70. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
  71. package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
  72. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
  73. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
  74. package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
  75. package/lib/agent-clients/adapters/codex.ts +44 -1
  76. package/lib/agent-clients/adapters/index.ts +7 -20
  77. package/lib/agent-clients/tokens.ts +4 -3
  78. package/lib/internal/agent-client.ts +142 -73
  79. package/lib/internal/cli-route-inventory.ts +2 -2
  80. package/lib/internal/sandbox-control.ts +0 -97
  81. package/lib/internal/task-finalization.ts +30 -18
  82. package/lib/internal/task-lifecycle.ts +1 -1
  83. package/lib/internal/task-operation-registry.ts +1 -1
  84. package/lib/internal/task-validate.ts +1 -1
  85. package/lib/platform/github-client.ts +34 -3
  86. package/lib/platform/issues.ts +18 -6
  87. package/lib/runtime/agent-runtime.ts +6 -41
  88. package/lib/sandbox/commands/control-status.ts +1 -0
  89. package/lib/sandbox/commands/create.ts +7 -1
  90. package/lib/sandbox/commands/ls.ts +4 -5
  91. package/lib/sandbox/commands/rm.ts +1 -1
  92. package/lib/sandbox/control/agent-operation.ts +49 -0
  93. package/lib/sandbox/control/client.ts +20 -254
  94. package/lib/sandbox/control/executor.ts +13 -136
  95. package/lib/sandbox/control/lifecycle.ts +67 -4
  96. package/lib/sandbox/control/protocol.ts +22 -66
  97. package/lib/sandbox/control/server.ts +21 -95
  98. package/lib/sandbox/control/task-view.ts +0 -19
  99. package/lib/sandbox/mounts.ts +10 -1
  100. package/lib/sandbox/recovery.ts +38 -17
  101. package/lib/sandbox/removal.ts +33 -7
  102. package/lib/sandbox/workspace-identity.ts +16 -6
  103. package/lib/sandbox/workspace-view.ts +50 -18
  104. package/lib/task/commands/ls.ts +3 -3
  105. package/lib/task/control-authority.ts +66 -378
  106. package/lib/task/control-recovery.ts +3 -4
  107. package/lib/task/create.ts +3 -3
  108. package/lib/task/delegation-receipts.ts +87 -231
  109. package/lib/task/events.ts +100 -42
  110. package/lib/task/finalization.ts +37 -7
  111. package/lib/task/lifecycle.ts +257 -109
  112. package/lib/task/orchestration.ts +260 -237
  113. package/lib/task/runtime-paths.ts +12 -0
  114. package/lib/task/short-id.ts +33 -40
  115. package/lib/task/verification-engine.ts +30 -25
  116. package/lib/task/workspace-snapshot.ts +1 -1
  117. package/package.json +1 -1
  118. package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
  119. package/templates/.agents/rules/README.en.md +2 -1
  120. package/templates/.agents/rules/README.zh-CN.md +2 -1
  121. package/templates/.agents/rules/next-step-output.en.md +6 -19
  122. package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
  123. package/templates/.agents/rules/strategy-pattern.en.md +25 -0
  124. package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
  125. package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
  126. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
  127. package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
  128. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
  129. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  130. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
  131. package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
  132. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
  133. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
  134. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
  135. package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
  136. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
  137. package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
  138. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
  139. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  140. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
  141. package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
  142. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
  143. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  144. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
  145. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  146. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  147. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  148. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  149. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
  150. package/dist/lib/internal/codex-lifecycle.js +0 -385
  151. package/dist/lib/task/codex-orchestration.js +0 -206
  152. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
  153. package/lib/internal/codex-lifecycle.ts +0 -390
  154. package/lib/task/codex-orchestration.ts +0 -276
@@ -6,16 +6,14 @@ import { parseTypedTaskFrontmatter } from './frontmatter.js';
6
6
  import { validateCurrentTaskContract } from './current-contract.js';
7
7
  import { parseReviewSummary } from './review-artifacts.js';
8
8
  import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
9
- import { resolveTaskRef } from './resolve-ref.js';
9
+ import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
10
10
  import { activateDelegation, abortActivatedDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
11
11
  import { normalizeAgentClients } from '../agent-clients/config.js';
12
- import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.js';
13
- import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
12
+ import { getAgentClientAdapter, getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
14
13
  import { isAgentClientId } from '../agent-clients/types.js';
15
14
  import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
16
15
  import { assertGitRepositoryBinding } from '../git/worktree-identity.js';
17
16
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
18
- import { hasActiveCodexLifecycleEvidence } from '../agent-clients/adapters/codex-lifecycle/store.js';
19
17
  import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead } from './review-fingerprint.js';
20
18
  import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './delivery-target.js';
21
19
  import { buildLifecycleFacts, canStart, recommendNext } from './capabilities.js';
@@ -29,7 +27,7 @@ function supportsLifecycleDelegation(client) {
29
27
  && getAgentClientDelegationEvidence(client).actualReasoningEffort !== 'unavailable';
30
28
  }
31
29
  function orchestrationPath(taskDir) {
32
- return path.join(taskDir, 'orchestration.json');
30
+ return path.join(taskDir, '.runtime', 'orchestration.json');
33
31
  }
34
32
  const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match the current runtime structure; the file was left unchanged; rebuild the sandbox or manually repair the state before retrying';
35
33
  class OrchestrationStateError extends Error {
@@ -79,7 +77,7 @@ function isModelPolicy(value) {
79
77
  }
80
78
  const RECOVERY_GUARD_KEYS = [
81
79
  'stepCount', 'nextStage', 'baselineEmpty', 'receiptCount', 'pendingDelegation',
82
- 'commitAuthorizationUnused', 'completionEvidenceAbsent'
80
+ 'completionEvidenceAbsent'
83
81
  ];
84
82
  function isRecovery(value) {
85
83
  if (!hasExactKeys(value, [
@@ -92,14 +90,13 @@ function isRecovery(value) {
92
90
  && value.previousStatus === 'paused'
93
91
  && isPause(value.previousPause)
94
92
  && value.previousPause.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
95
- && value.client === 'claude-code'
93
+ && isAgentClientId(value.client)
96
94
  && hasExactKeys(guards, RECOVERY_GUARD_KEYS)
97
95
  && guards.stepCount === 0
98
96
  && guards.nextStage === null
99
97
  && guards.baselineEmpty === true
100
98
  && guards.receiptCount === 0
101
99
  && guards.pendingDelegation === false
102
- && guards.commitAuthorizationUnused === true
103
100
  && guards.completionEvidenceAbsent === true
104
101
  && value.resultingStatus === 'running';
105
102
  }
@@ -119,17 +116,15 @@ export function isCompletionEvidence(value) {
119
116
  const ORCHESTRATION_RUN_KEYS = [
120
117
  'taskId', 'runId', 'status', 'nextStage', 'stepCount', 'maxSteps', 'modelPolicy',
121
118
  'modelPolicySource', 'recoveryHistory', 'baseline', 'pendingDelegation', 'receipts',
122
- 'pause', 'commitAuthorization', 'completionEvidence', 'createdAt', 'updatedAt'
119
+ 'pause', 'completionEvidence', 'createdAt', 'updatedAt'
123
120
  ];
124
- function orchestrationStateReasonCodes(value, expectedTaskId) {
121
+ function orchestrationStateReasonCodes(value) {
125
122
  if (!hasExactKeys(value, ORCHESTRATION_RUN_KEYS))
126
123
  return ['top-level-keys'];
127
124
  const record = value;
128
125
  const reasons = [];
129
126
  if (!exactText(record.taskId))
130
127
  reasons.push('taskId');
131
- else if (expectedTaskId !== undefined && record.taskId !== expectedTaskId)
132
- reasons.push('taskId-mismatch');
133
128
  if (!exactText(record.runId))
134
129
  reasons.push('runId');
135
130
  if (!['running', 'paused', 'completed'].includes(record.status))
@@ -155,15 +150,6 @@ function orchestrationStateReasonCodes(value, expectedTaskId) {
155
150
  reasons.push('receipts');
156
151
  if (!(record.pause === null || isPause(record.pause)))
157
152
  reasons.push('pause');
158
- if (!hasExactKeys(record.commitAuthorization, ['issuedAt', 'consumedAt'])) {
159
- reasons.push('commitAuthorization');
160
- }
161
- else {
162
- if (!nullableText(record.commitAuthorization.issuedAt))
163
- reasons.push('commitAuthorization.issuedAt');
164
- if (!nullableText(record.commitAuthorization.consumedAt))
165
- reasons.push('commitAuthorization.consumedAt');
166
- }
167
153
  if (!(record.completionEvidence === null || isCompletionEvidence(record.completionEvidence)))
168
154
  reasons.push('completionEvidence');
169
155
  if (!exactText(record.createdAt))
@@ -173,7 +159,7 @@ function orchestrationStateReasonCodes(value, expectedTaskId) {
173
159
  if (Array.isArray(record.receipts) && record.receipts.every(isDelegationReceipt)
174
160
  && (record.pendingDelegation === null || isDelegationReceipt(record.pendingDelegation))) {
175
161
  const receipts = [...record.receipts, ...(record.pendingDelegation ? [record.pendingDelegation] : [])];
176
- if (receipts.some((receipt) => receipt.taskId !== record.taskId || receipt.runId !== record.runId)) {
162
+ if (receipts.some((receipt) => receipt.runId !== record.runId)) {
177
163
  reasons.push('receipt-identity');
178
164
  }
179
165
  }
@@ -223,7 +209,7 @@ function fileObservation(file) {
223
209
  }
224
210
  }
225
211
  function parseOrchestrationRun(value, expectedTaskId) {
226
- const reasonCodes = orchestrationStateReasonCodes(value, expectedTaskId);
212
+ const reasonCodes = orchestrationStateReasonCodes(value);
227
213
  if (reasonCodes.length > 0)
228
214
  throw new OrchestrationStateError(expectedTaskId ?? null, reasonCodes);
229
215
  return value;
@@ -268,7 +254,77 @@ function readRun(taskDir, options = {}) {
268
254
  throw new OrchestrationStateError(taskId, reasonCodes);
269
255
  }
270
256
  }
257
+ function findDelegation(run, receiptId) {
258
+ if (run.pendingDelegation?.id === receiptId)
259
+ return { receipt: run.pendingDelegation, location: 'pending' };
260
+ const receipt = run.receipts.find((entry) => entry.id === receiptId);
261
+ return receipt ? { receipt, location: 'history' } : null;
262
+ }
263
+ function readDelegationAdapterEvidence(taskRef, receiptId, adapterId, options = {}) {
264
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
265
+ if (!resolved.ok)
266
+ throw new Error(`${resolved.code}: ${resolved.message}`);
267
+ const run = readRun(resolved.taskDir, options);
268
+ const found = run && findDelegation(run, receiptId);
269
+ if (!found || found.receipt.client !== adapterId)
270
+ return null;
271
+ return found.receipt.adapterEvidence?.[adapterId] ?? null;
272
+ }
273
+ function mutateDelegationAdapterEvidence(taskRef, receiptId, adapterId, mutate, options = {}) {
274
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
275
+ if (!resolved.ok)
276
+ throw new Error(`${resolved.code}: ${resolved.message}`);
277
+ return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options, false);
278
+ }
279
+ function mutateDelegationAdapterEvidenceWithinTaskLock(taskRef, receiptId, adapterId, mutate, options = {}) {
280
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
281
+ if (!resolved.ok)
282
+ throw new Error(`${resolved.code}: ${resolved.message}`);
283
+ return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options, true);
284
+ }
285
+ function mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options, taskLockHeld) {
286
+ const update = () => {
287
+ try {
288
+ const run = readRun(resolved.taskDir, options);
289
+ const found = run && findDelegation(run, receiptId);
290
+ if (!run || !found || found.receipt.client !== adapterId) {
291
+ throw new Error('ORCHESTRATION_DELEGATION_MISSING: adapter evidence has no matching task receipt');
292
+ }
293
+ const current = found.receipt.adapterEvidence?.[adapterId] ?? null;
294
+ const nextState = mutate(current);
295
+ const serialized = JSON.stringify(nextState);
296
+ if (serialized === undefined)
297
+ throw new Error('Agent adapter evidence must be JSON serializable');
298
+ if (serialized === JSON.stringify(current))
299
+ return JSON.parse(serialized);
300
+ const adapterEvidence = Object.freeze({
301
+ ...(found.receipt.adapterEvidence ?? {}),
302
+ [adapterId]: JSON.parse(serialized)
303
+ });
304
+ const nextReceipt = Object.freeze({ ...found.receipt, adapterEvidence });
305
+ const updated = found.location === 'pending'
306
+ ? withUpdatedRun(run, { pendingDelegation: nextReceipt }, options.now)
307
+ : withUpdatedRun(run, {
308
+ receipts: Object.freeze(run.receipts.map((receipt) => receipt.id === receiptId ? nextReceipt : receipt))
309
+ }, options.now);
310
+ saveRun(resolved.taskDir, updated);
311
+ return JSON.parse(serialized);
312
+ }
313
+ catch (error) {
314
+ mutationError = error;
315
+ return null;
316
+ }
317
+ };
318
+ let mutationError;
319
+ const result = taskLockHeld
320
+ ? update()
321
+ : withTaskExecutionLock(resolved.repoRoot, resolved.taskId, `agent-adapter.${adapterId}.evidence`, update);
322
+ if (mutationError)
323
+ throw mutationError;
324
+ return result;
325
+ }
271
326
  function atomicWrite(file, value) {
327
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
272
328
  const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
273
329
  fs.writeFileSync(temp, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
274
330
  fs.renameSync(temp, file);
@@ -316,17 +372,16 @@ function resolveProjectPolicy(repoRoot, client) {
316
372
  const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
317
373
  return normalizeAgentClients(raw).state[client].orchestration;
318
374
  }
319
- function canRecoverClaudeCodeUnsupportedPause(run, taskDir) {
375
+ function canRecoverUnsupportedClientPause(run) {
320
376
  return run.status === 'paused'
321
377
  && run.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
322
- && run.modelPolicySource?.client === 'claude-code'
378
+ && isAgentClientId(run.modelPolicySource?.client)
379
+ && supportsLifecycleDelegation(run.modelPolicySource.client)
323
380
  && run.stepCount === 0
324
381
  && run.nextStage === null
325
382
  && run.baseline === ''
326
383
  && run.pendingDelegation === null
327
384
  && run.receipts.length === 0
328
- && run.commitAuthorization?.issuedAt === null
329
- && run.commitAuthorization?.consumedAt === null
330
385
  && run.completionEvidence == null
331
386
  && run.recoveryHistory.length === 0;
332
387
  }
@@ -360,8 +415,7 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
360
415
  }
361
416
  }
362
417
  if (existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED') {
363
- if (existing.modelPolicySource.client !== 'claude-code'
364
- || !canRecoverClaudeCodeUnsupportedPause(existing, resolved.taskDir)) {
418
+ if (!canRecoverUnsupportedClientPause(existing)) {
365
419
  return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
366
420
  }
367
421
  const now = (options.now ?? (() => new Date().toISOString()))();
@@ -370,14 +424,13 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
370
424
  recoveredAt: now,
371
425
  previousStatus: 'paused',
372
426
  previousPause: existing.pause,
373
- client: 'claude-code',
427
+ client: existing.modelPolicySource.client,
374
428
  guards: {
375
429
  stepCount: 0,
376
430
  nextStage: null,
377
431
  baselineEmpty: true,
378
432
  receiptCount: 0,
379
433
  pendingDelegation: false,
380
- commitAuthorizationUnused: true,
381
434
  completionEvidenceAbsent: true
382
435
  },
383
436
  resultingStatus: 'running'
@@ -431,7 +484,6 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
431
484
  pendingDelegation: null,
432
485
  receipts: [],
433
486
  pause: null,
434
- commitAuthorization: { issuedAt: null, consumedAt: null },
435
487
  completionEvidence: null,
436
488
  createdAt: now,
437
489
  updatedAt: now
@@ -657,6 +709,40 @@ function statusOrchestration(taskRef, options = {}) {
657
709
  return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
658
710
  return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
659
711
  }
712
+ function validatePreparedModelPolicy(taskId, run, next, input) {
713
+ if (!validModel(input.requestedModel)) {
714
+ return failed('ORCHESTRATION_REQUESTED_MODEL_REQUIRED', 'prepare requires the exact requested model identity', taskId);
715
+ }
716
+ if (!validModel(input.requestedReasoningEffort)) {
717
+ return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', taskId);
718
+ }
719
+ const expectedPolicy = run.modelPolicy[next.role];
720
+ if (input.requestedModel !== expectedPolicy.model) {
721
+ return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, taskId);
722
+ }
723
+ if (input.requestedReasoningEffort !== expectedPolicy.reasoningEffort) {
724
+ return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_MISMATCH', `requested reasoning effort does not match the persisted ${next.role} policy`, taskId);
725
+ }
726
+ return null;
727
+ }
728
+ function captureTaskSnapshot(taskId, repoRoot, options) {
729
+ return (options.captureWorkspace ?? captureWorkspaceSnapshot)({
730
+ gitRoot: gitRootFor(repoRoot, options),
731
+ stateRoot: repoRoot,
732
+ taskId
733
+ });
734
+ }
735
+ function validateDelegationRun(taskRef, taskId, run, options) {
736
+ if (!run || run.status !== 'running') {
737
+ return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', taskId);
738
+ }
739
+ if (run.pendingDelegation)
740
+ return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', taskId);
741
+ if (run.stepCount >= run.maxSteps) {
742
+ return pauseOrchestration(taskRef, 'ORCHESTRATION_MAX_STEPS', 'maximum orchestration steps reached', true, options);
743
+ }
744
+ return null;
745
+ }
660
746
  function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
661
747
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
662
748
  if (!resolved.ok)
@@ -673,47 +759,27 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
673
759
  requestedReasoningEffort: input.requestedReasoningEffort ?? null
674
760
  });
675
761
  const run = readRun(resolved.taskDir, options);
676
- if (!run || run.status !== 'running')
677
- return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
678
- if (run.pendingDelegation)
679
- return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', resolved.taskId);
680
- const repositoryPending = matchingDelegations(() => true, options);
681
- if (repositoryPending.length > 0) {
682
- return failed('ORCHESTRATION_DELEGATION_BUSY', 'the repository already has a pending lifecycle delegation', resolved.taskId);
683
- }
684
- if (run.stepCount >= run.maxSteps)
685
- return pauseOrchestration(taskRef, 'ORCHESTRATION_MAX_STEPS', 'maximum orchestration steps reached', true, options);
762
+ const runFailure = validateDelegationRun(taskRef, resolved.taskId, run, options);
763
+ if (runFailure)
764
+ return runFailure;
765
+ const activeRun = run;
686
766
  const routed = routeOrchestration(taskRef, options);
687
767
  if (!routed.next)
688
768
  return routed;
689
769
  const next = routed.next;
690
- if (!validModel(input.requestedModel)) {
691
- return failed('ORCHESTRATION_REQUESTED_MODEL_REQUIRED', 'prepare requires the exact requested model identity', resolved.taskId);
692
- }
693
- if (!validModel(input.requestedReasoningEffort)) {
694
- return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', resolved.taskId);
695
- }
696
- const expectedPolicy = run.modelPolicy[next.role];
697
- if (input.requestedModel !== expectedPolicy.model) {
698
- return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
699
- }
700
- if (input.requestedReasoningEffort !== expectedPolicy.reasoningEffort) {
701
- return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_MISMATCH', `requested reasoning effort does not match the persisted ${next.role} policy`, resolved.taskId);
702
- }
770
+ const policyFailure = validatePreparedModelPolicy(resolved.taskId, activeRun, next, input);
771
+ if (policyFailure)
772
+ return policyFailure;
703
773
  let beforeFingerprint;
704
774
  try {
705
- beforeFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
706
- gitRoot: gitRootFor(resolved.repoRoot, options),
707
- stateRoot: resolved.repoRoot,
708
- taskId: resolved.taskId
709
- });
775
+ beforeFingerprint = captureTaskSnapshot(resolved.taskId, resolved.repoRoot, options);
710
776
  }
711
777
  catch (error) {
712
778
  return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
713
779
  }
714
780
  const receipt = prepareDelegation({
715
781
  taskId: resolved.taskId,
716
- runId: run.runId,
782
+ runId: activeRun.runId,
717
783
  role: next.role,
718
784
  stage: next.stage,
719
785
  round: next.round,
@@ -722,10 +788,9 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
722
788
  requestedModel: input.requestedModel,
723
789
  requestedReasoningEffort: input.requestedReasoningEffort,
724
790
  workspaceSnapshotScope: 'task',
725
- lifecycleProvenance: input.lifecycleProvenance ?? null,
726
791
  beforeFingerprint
727
792
  }, { id: options.id, now: options.now, monotonicNow: options.monotonicNow });
728
- const updated = withUpdatedRun(run, {
793
+ const updated = withUpdatedRun(activeRun, {
729
794
  nextStage: next.stage,
730
795
  pendingDelegation: receipt
731
796
  });
@@ -737,7 +802,7 @@ function prepareOrchestrationDelegation(taskRef, input, options = {}) {
737
802
  if (!resolved.ok)
738
803
  return failed(resolved.code, resolved.message, resolved.taskId);
739
804
  try {
740
- return withTaskExecutionLock(resolved.repoRoot, '__repository__', 'task-orchestration.prepare.repository', () => withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.prepare.task', () => prepareOrchestrationDelegationUnlocked(taskRef, input, options)));
805
+ return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.prepare.task', () => prepareOrchestrationDelegationUnlocked(taskRef, input, options));
741
806
  }
742
807
  catch (error) {
743
808
  if (error instanceof TaskExecutionLockError)
@@ -770,7 +835,7 @@ function dispatchOrchestrationDelegation(taskRef, options = {}) {
770
835
  if (!resolved.ok)
771
836
  return failed(resolved.code, resolved.message, resolved.taskId);
772
837
  try {
773
- return withTaskExecutionLock(resolved.repoRoot, '__repository__', 'task-orchestration.dispatch.repository', () => withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.dispatch.task', () => dispatchOrchestrationDelegationUnlocked(taskRef, options)));
838
+ return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.dispatch.task', () => dispatchOrchestrationDelegationUnlocked(taskRef, options));
774
839
  }
775
840
  catch (error) {
776
841
  if (error instanceof TaskExecutionLockError)
@@ -779,65 +844,40 @@ function dispatchOrchestrationDelegation(taskRef, options = {}) {
779
844
  }
780
845
  }
781
846
  function matchingDelegations(predicate, options) {
847
+ const taskId = options.taskId;
848
+ if (taskId === undefined || taskId === '') {
849
+ return { ok: false, error: failed('ORCHESTRATION_TASK_ID_REQUIRED', 'taskId is required for lifecycle matching') };
850
+ }
851
+ if (typeof taskId !== 'string' || !TASK_ID_RE.test(taskId)) {
852
+ return { ok: false, error: failed('ORCHESTRATION_TASK_ID_INVALID', 'taskId must be a canonical TASK-id') };
853
+ }
782
854
  const repoRoot = options.repoRoot ?? process.cwd();
783
- const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
784
- if (!fs.existsSync(activeRoot))
785
- return [];
786
- emitDiagnostic(options, 'orchestration-active-scan-start', {
787
- activeRoot
788
- });
789
- const matches = fs.readdirSync(activeRoot, { withFileTypes: true })
790
- .filter((entry) => entry.isDirectory())
791
- .flatMap((entry) => {
792
- const candidateTaskId = entry.name;
793
- const candidateTaskDir = path.join(activeRoot, candidateTaskId);
794
- emitDiagnostic(options, 'orchestration-active-scan-candidate', {
795
- candidateTaskId,
796
- candidateTaskDir,
797
- orchestrationPath: orchestrationPath(candidateTaskDir)
798
- });
799
- let run;
800
- try {
801
- run = readRun(candidateTaskDir, options);
802
- }
803
- catch (error) {
804
- emitDiagnostic(options, 'orchestration-active-scan-candidate-failed', {
805
- candidateTaskId,
806
- candidateTaskDir,
807
- errorType: error instanceof Error ? error.name : typeof error,
808
- reasonCodes: error instanceof OrchestrationStateError ? error.reasonCodes.join(',') : null
809
- });
810
- throw error;
811
- }
812
- if (!run || run.status !== 'running' || !run.pendingDelegation)
813
- return [];
814
- const receipt = run.pendingDelegation;
815
- return predicate(receipt)
816
- ? [{ taskId: candidateTaskId, run }]
817
- : [];
818
- });
819
- emitDiagnostic(options, 'orchestration-active-scan-finished', {
820
- activeRoot,
821
- matchingDelegations: matches.length
822
- });
823
- return matches;
855
+ const taskDir = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
856
+ const run = readRun(taskDir, options);
857
+ const matches = run && run.status === 'running' && run.pendingDelegation && predicate(run.pendingDelegation)
858
+ ? [{ taskId, run }]
859
+ : [];
860
+ return { ok: true, matches };
824
861
  }
825
862
  function uniqueMatchingDelegation(client, predicate, options) {
826
- const matches = matchingDelegations((receipt) => receipt.client === client && predicate(receipt), options);
863
+ const lookup = matchingDelegations((receipt) => receipt.client === client && predicate(receipt), options);
864
+ if (!lookup.ok)
865
+ return lookup.error;
866
+ const matches = lookup.matches;
827
867
  if (matches.length === 0)
828
868
  return failed('ORCHESTRATION_DELEGATION_MISSING', 'no matching lifecycle delegation exists');
829
- if (matches.length > 1)
830
- return failed('ORCHESTRATION_DELEGATION_AMBIGUOUS', 'multiple lifecycle delegations match the native hook event');
831
869
  return matches[0];
832
870
  }
833
871
  function hasActivatableOrchestrationDelegation(client, childId, options = {}) {
834
- return matchingDelegations((receipt) => receipt.client === client && (receipt.status === 'prepared'
835
- || (receipt.childId === childId && ['activated', 'stage-completed'].includes(receipt.status))), options).length > 0;
872
+ const lookup = matchingDelegations((receipt) => receipt.client === client && (receipt.status === 'prepared'
873
+ || (receipt.childId === childId && ['activated', 'stage-completed'].includes(receipt.status))), options);
874
+ return lookup.ok && lookup.matches.length > 0;
836
875
  }
837
876
  function hasSealableOrchestrationDelegation(client, childId, options = {}) {
838
- return matchingDelegations((receipt) => receipt.client === client
877
+ const lookup = matchingDelegations((receipt) => receipt.client === client
839
878
  && receipt.childId === childId
840
- && ['stage-completed', 'sealed'].includes(receipt.status), options).length > 0;
879
+ && ['stage-completed', 'sealed'].includes(receipt.status), options);
880
+ return lookup.ok && lookup.matches.length > 0;
841
881
  }
842
882
  function activateMatchingOrchestrationDelegation(client, event, options = {}) {
843
883
  const role = managedDelegationRole(event.nativeAgent);
@@ -857,7 +897,8 @@ function activateMatchingOrchestrationDelegation(client, event, options = {}) {
857
897
  && (receipt.actualReasoningEffort ?? null) === foldBlankToNull(event.actualReasoningEffort)
858
898
  && receipt.modelFallbackReason === (event.modelFallbackReason ?? null)
859
899
  && receipt.reasoningEffortFallbackReason === (event.reasoningEffortFallbackReason ?? null)
860
- && receipt.hostEvidence?.hookDefinitionHash === event.hostEvidence?.hookDefinitionHash;
900
+ && (event.clientEvidence === undefined
901
+ || JSON.stringify(receipt.adapterEvidence?.[client]) === JSON.stringify(event.clientEvidence));
861
902
  if (!sameEvidence) {
862
903
  return pauseOrchestration(replay.taskId, 'DELEGATION_REPLAY_CONFLICT', 'replayed native start evidence conflicts with the active receipt', true, options);
863
904
  }
@@ -874,7 +915,7 @@ function pauseMatchingOrchestrationDelegation(client, code, message, options = {
874
915
  return matched;
875
916
  return pauseOrchestration(matched.taskId, code, message, true, options);
876
917
  }
877
- function sealMatchingOrchestrationDelegation(client, event, options = {}) {
918
+ function sealMatchingOrchestrationDelegation(client, event, options = {}, beforeSeal) {
878
919
  const role = managedDelegationRole(event.nativeAgent);
879
920
  if (!role)
880
921
  return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
@@ -904,66 +945,19 @@ function sealMatchingOrchestrationDelegation(client, event, options = {}) {
904
945
  actualReasoningEffort: event.actualReasoningEffort,
905
946
  modelFallbackReason: event.modelFallbackReason,
906
947
  reasoningEffortFallbackReason: event.reasoningEffortFallbackReason
907
- }, options);
948
+ }, { runId: receipt.runId, receiptId: receipt.id }, options, beforeSeal);
908
949
  }
909
950
  catch (error) {
910
951
  return pauseOrchestration(matched.taskId, 'ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), true, options);
911
952
  }
912
953
  }
913
- function sealMatchingOrchestrationDelegationWithHostEvidence(client, event, consumeEvidence, options = {}) {
914
- const role = managedDelegationRole(event.nativeAgent);
915
- if (!role)
916
- return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
917
- const matched = uniqueMatchingDelegation(client, (receipt) => ((receipt.status === 'stage-completed' || receipt.status === 'sealed')
918
- && receipt.childId === event.childId), options);
919
- if ('status' in matched)
920
- return matched;
921
- const receipt = matched.run.pendingDelegation;
922
- if (receipt.status === 'sealed') {
923
- return reconcileMatchingOrchestrationDelegation(client, event.childId, options);
924
- }
925
- if (receipt.role !== role) {
926
- return pauseOrchestration(matched.taskId, 'DELEGATION_ROLE_MISMATCH', `managed role ${role} does not match ${receipt.role}`, true, options);
927
- }
928
- const repoRoot = options.repoRoot ?? process.cwd();
929
- try {
930
- const gitRoot = gitRootFor(repoRoot, options);
931
- const snapshotTaskId = receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null;
932
- const afterFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
933
- gitRoot,
934
- stateRoot: repoRoot,
935
- taskId: snapshotTaskId
936
- });
937
- const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(gitRoot, receipt.beforeFingerprint, afterFingerprint);
938
- const baseEvent = { childId: event.childId, exitCode: 0, afterFingerprint, changedPaths };
939
- const validated = sealDelegation(receipt, baseEvent, { now: options.now, requireHostEvidence: false });
940
- if (!validated.ok) {
941
- return pauseOrchestration(matched.taskId, validated.code, validated.message, true, options);
942
- }
943
- const hostEvidence = consumeEvidence(receipt);
944
- const sealed = sealDelegation(receipt, { ...baseEvent, hostEvidence }, { now: options.now });
945
- if (!sealed.ok)
946
- return pauseOrchestration(matched.taskId, sealed.code, sealed.message, true, options);
947
- const resolved = resolveTaskRef(matched.taskId, { repoRoot });
948
- if (!resolved.ok)
949
- return failed(resolved.code, resolved.message, resolved.taskId);
950
- const updated = withUpdatedRun(matched.run, { pendingDelegation: sealed.receipt }, options.now);
951
- saveRun(resolved.taskDir, updated);
952
- return { status: 'running', changed: true, taskId: matched.taskId, run: updated, next: null, error: null };
953
- }
954
- catch (error) {
955
- return pauseOrchestration(matched.taskId, 'ORCHESTRATION_CODEX_EVIDENCE_FAILED', error instanceof Error ? error.message : String(error), true, options);
956
- }
957
- }
958
954
  function reconcileMatchingOrchestrationDelegation(client, childId, options = {}) {
959
955
  const matched = uniqueMatchingDelegation(client, (receipt) => receipt.childId === childId, options);
960
956
  if ('status' in matched)
961
957
  return matched;
962
958
  const receipt = matched.run.pendingDelegation;
963
- if (receipt.status !== 'sealed'
964
- || receipt.hostEvidence?.consumer !== receipt.id
965
- || receipt.hostEvidence.consumedAt === null) {
966
- return pauseOrchestration(matched.taskId, 'ORCHESTRATION_CODEX_RECONCILIATION_FAILED', 'Codex native spawn did not produce one sealed receipt with consumed host evidence', true, options);
959
+ if (receipt.status !== 'sealed' || receipt.childId !== childId) {
960
+ return pauseOrchestration(matched.taskId, 'ORCHESTRATION_DELEGATION_RECONCILIATION_FAILED', 'native child did not produce one sealed delegation receipt', true, options);
967
961
  }
968
962
  return { status: matched.run.status, changed: false, taskId: matched.taskId, run: matched.run, next: null, error: null };
969
963
  }
@@ -977,9 +971,15 @@ function activateOrchestrationDelegation(taskRef, event, options = {}) {
977
971
  if (run.status !== 'running') {
978
972
  return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
979
973
  }
974
+ const validateAdapterEvidence = getAgentClientAdapter(run.pendingDelegation.client)
975
+ .orchestrationAdapter?.validateActivationEvidence;
976
+ if (validateAdapterEvidence && !validateAdapterEvidence(event.clientEvidence, event.parentId)) {
977
+ return pauseOrchestration(taskRef, 'DELEGATION_ADAPTER_EVIDENCE_REQUIRED', 'native activation lacks valid evidence from the agent adapter', true, options);
978
+ }
980
979
  const result = activateDelegation(run.pendingDelegation, event, {
981
980
  now: options.now,
982
- monotonicNow: options.monotonicNow
981
+ monotonicNow: options.monotonicNow,
982
+ evidencePolicy: getAgentClientDelegationEvidence(run.pendingDelegation.client)
983
983
  });
984
984
  if (!result.ok) {
985
985
  if (result.code === 'DELEGATION_IGNORED')
@@ -1048,15 +1048,10 @@ function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
1048
1048
  && wallNow <= Date.parse(receipt.activationDeadlineAt)) {
1049
1049
  return failed('ORCHESTRATION_PREPARED_RECOVERY_EARLY', 'prepared receipt activation deadline has not elapsed', resolved.taskId);
1050
1050
  }
1051
- const activeLifecycleEvidence = options.hasActiveLifecycleEvidence?.(receipt)
1052
- ?? (receipt.client === 'codex'
1053
- && receipt.lifecycleProvenance
1054
- && hasActiveCodexLifecycleEvidence(resolveAgentRuntimeStoreRoot({ repoRoot: resolved.repoRoot, store: 'lifecycle' }), {
1055
- nativeAgent: `agent-infra-lifecycle-${receipt.role}`,
1056
- hookDefinitionHash: receipt.lifecycleProvenance.hookDefinitionHash
1057
- }));
1058
- if (activeLifecycleEvidence) {
1059
- return failed('ORCHESTRATION_PREPARED_RECOVERY_ACTIVE_EVIDENCE', 'active Codex lifecycle evidence blocks prepared receipt recovery', resolved.taskId);
1051
+ const activeAdapterEvidence = options.hasActiveLifecycleEvidence?.(receipt)
1052
+ ?? Object.keys(receipt.adapterEvidence ?? {}).length > 0;
1053
+ if (activeAdapterEvidence) {
1054
+ return failed('ORCHESTRATION_PREPARED_RECOVERY_ACTIVE_EVIDENCE', 'active agent adapter evidence blocks prepared receipt recovery', resolved.taskId);
1060
1055
  }
1061
1056
  const current = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
1062
1057
  gitRoot: gitRootFor(resolved.repoRoot, options),
@@ -1086,9 +1081,8 @@ function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
1086
1081
  return failed('ORCHESTRATION_PREPARED_RECOVERY_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
1087
1082
  }
1088
1083
  }
1089
- function recoveryReceiptMatches(receipt, taskId, event) {
1090
- return receipt.taskId === taskId
1091
- && receipt.stage === event.stage
1084
+ function recoveryReceiptMatches(receipt, event) {
1085
+ return receipt.stage === event.stage
1092
1086
  && receipt.round === event.round
1093
1087
  && receipt.artifact === event.artifact
1094
1088
  && receipt.childId === event.childId
@@ -1108,7 +1102,7 @@ function recoverActivatedOrchestrationDelegationUnderLock(taskRef, event, option
1108
1102
  const receipt = matches.find((candidate) => candidate.id === event.receiptId);
1109
1103
  if (!receipt)
1110
1104
  return failed('ORCHESTRATION_DELEGATION_MISSING', 'recovery receipt does not exist', resolved.taskId);
1111
- if (!recoveryReceiptMatches(receipt, resolved.taskId, event)) {
1105
+ if (!recoveryReceiptMatches(receipt, event)) {
1112
1106
  return failed('ORCHESTRATION_PROVENANCE_MISMATCH', 'recovery selector does not match the delegation receipt', resolved.taskId);
1113
1107
  }
1114
1108
  if (receipt.status === 'aborted' && run.pendingDelegation === null) {
@@ -1125,9 +1119,7 @@ function recoverActivatedOrchestrationDelegationUnderLock(taskRef, event, option
1125
1119
  }
1126
1120
  const aborted = abortActivatedDelegation(receipt, {
1127
1121
  childId: event.childId,
1128
- stopRevision: event.stopRevision,
1129
- consumer: event.consumer,
1130
- consumedAt: event.consumedAt
1122
+ clientEvidence: event.clientEvidence
1131
1123
  });
1132
1124
  if (!aborted.ok)
1133
1125
  return failed(aborted.code, aborted.message, resolved.taskId);
@@ -1207,19 +1199,34 @@ function planOrchestrationStageCompletion(taskRef, event, options = {}) {
1207
1199
  function commitOrchestrationStageCompletion(plan) {
1208
1200
  saveRun(plan.taskDir, plan.updatedRun);
1209
1201
  }
1210
- function sealOrchestrationDelegation(taskRef, event, options = {}) {
1211
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1202
+ function sealOrchestrationDelegation(taskRef, event, expected, options = {}, beforeSeal) {
1203
+ const orchestrationOptions = options;
1204
+ const resolved = resolveTaskRef(taskRef, { repoRoot: orchestrationOptions.repoRoot });
1212
1205
  if (!resolved.ok)
1213
1206
  return failed(resolved.code, resolved.message, resolved.taskId);
1214
- const run = readRun(resolved.taskDir, options);
1215
- if (!run?.pendingDelegation)
1216
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1217
- const result = sealDelegation(run.pendingDelegation, event, { now: options.now });
1218
- if (!result.ok)
1219
- return pauseOrchestration(taskRef, result.code, result.message, true, options);
1220
- const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
1221
- saveRun(resolved.taskDir, updated);
1222
- return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1207
+ return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.seal', () => {
1208
+ const run = readRun(resolved.taskDir, orchestrationOptions);
1209
+ if (!run?.pendingDelegation)
1210
+ return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1211
+ if (expected && (run.runId !== expected.runId || run.pendingDelegation.id !== expected.receiptId)) {
1212
+ return failed('ORCHESTRATION_DELEGATION_CHANGED', 'pending delegation changed while collecting completion evidence', resolved.taskId);
1213
+ }
1214
+ const preSealFailure = beforeSeal?.();
1215
+ if (preSealFailure)
1216
+ return preSealFailure;
1217
+ const current = beforeSeal ? readRun(resolved.taskDir, orchestrationOptions) : run;
1218
+ if (!current?.pendingDelegation)
1219
+ return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1220
+ if (expected && (current.runId !== expected.runId || current.pendingDelegation.id !== expected.receiptId)) {
1221
+ return failed('ORCHESTRATION_DELEGATION_CHANGED', 'pending delegation changed before it was sealed', resolved.taskId);
1222
+ }
1223
+ const result = sealDelegation(current.pendingDelegation, event, { now: orchestrationOptions.now });
1224
+ if (!result.ok)
1225
+ return pauseOrchestration(taskRef, result.code, result.message, true, orchestrationOptions);
1226
+ const updated = withUpdatedRun(current, { pendingDelegation: result.receipt });
1227
+ saveRun(resolved.taskDir, updated);
1228
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1229
+ });
1223
1230
  }
1224
1231
  function advanceOrchestration(taskRef, options = {}) {
1225
1232
  const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
@@ -1255,5 +1262,5 @@ function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
1255
1262
  saveRun(resolved.taskDir, updated);
1256
1263
  return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1257
1264
  }
1258
- export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, awaitOrchestrationDelegationActivation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion, completeOrchestrationStage, dispatchOrchestrationDelegation, inspectOrchestrationStage, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, orchestrationPath, pauseMatchingOrchestrationDelegation, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, reconcileMatchingOrchestrationDelegation, recoverPreparedOrchestrationDelegation, recoverActivatedOrchestrationDelegationUnderLock, routeOrchestration, sealMatchingOrchestrationDelegation, sealMatchingOrchestrationDelegationWithHostEvidence, sealOrchestrationDelegation, statusOrchestration, };
1265
+ export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, awaitOrchestrationDelegationActivation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion, completeOrchestrationStage, dispatchOrchestrationDelegation, inspectOrchestrationStage, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, orchestrationPath, readDelegationAdapterEvidence, mutateDelegationAdapterEvidence, mutateDelegationAdapterEvidenceWithinTaskLock, pauseMatchingOrchestrationDelegation, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, reconcileMatchingOrchestrationDelegation, recoverPreparedOrchestrationDelegation, recoverActivatedOrchestrationDelegationUnderLock, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration, };
1259
1266
  //# sourceMappingURL=orchestration.js.map