@fitlab-ai/agent-infra 0.11.2 → 0.11.4

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 (162) hide show
  1. package/README.md +17 -8
  2. package/README.zh-CN.md +17 -8
  3. package/dist/bin/internal-cli.js +7 -2
  4. package/dist/lib/agent-clients/adapter.js +3 -0
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
  8. package/dist/lib/agent-clients/adapters/codex.js +10 -0
  9. package/dist/lib/internal/cli-route-inventory.js +1 -1
  10. package/dist/lib/internal/manual-validation.js +22 -27
  11. package/dist/lib/internal/sandbox-control.js +1 -43
  12. package/dist/lib/internal/task-artifact.js +1 -1
  13. package/dist/lib/internal/task-finalization.js +41 -1
  14. package/dist/lib/internal/task-ledger.js +13 -5
  15. package/dist/lib/internal/task-lifecycle.js +1 -1
  16. package/dist/lib/internal/task-operation-registry.js +7 -2
  17. package/dist/lib/platform/completion-backfill.js +63 -17
  18. package/dist/lib/platform/issue-comments.js +2 -31
  19. package/dist/lib/platform/pr-summary.js +7 -2
  20. package/dist/lib/sandbox/control/client.js +3 -44
  21. package/dist/lib/sandbox/control/executor.js +14 -79
  22. package/dist/lib/sandbox/control/protocol.js +5 -64
  23. package/dist/lib/sandbox/control/server.js +49 -343
  24. package/dist/lib/sandbox/control/state.js +1 -7
  25. package/dist/lib/sandbox/control/task-view.js +1 -2
  26. package/dist/lib/sandbox/task-cleanup.js +13 -20
  27. package/dist/lib/task/artifact-command.js +11 -4
  28. package/dist/lib/task/artifact-lifecycle.js +217 -85
  29. package/dist/lib/task/artifact-operations.js +1 -26
  30. package/dist/lib/task/artifact-receipts.js +4 -1
  31. package/dist/lib/task/artifact-schema.js +1 -0
  32. package/dist/lib/task/artifact-selection.js +25 -0
  33. package/dist/lib/task/capabilities.js +158 -39
  34. package/dist/lib/task/commands/status.js +9 -2
  35. package/dist/lib/task/control-authority.js +37 -38
  36. package/dist/lib/task/control-recovery.js +3 -42
  37. package/dist/lib/task/decision-intents.js +12 -0
  38. package/dist/lib/task/delegation-receipts.js +2 -2
  39. package/dist/lib/task/delivery.js +1 -43
  40. package/dist/lib/task/events.js +80 -64
  41. package/dist/lib/task/finalization-artifacts.js +1 -3
  42. package/dist/lib/task/finalization-state.js +1 -1
  43. package/dist/lib/task/finalization.js +319 -234
  44. package/dist/lib/task/ledger-intents.js +157 -15
  45. package/dist/lib/task/lifecycle-path.js +75 -0
  46. package/dist/lib/task/lifecycle.js +11 -1
  47. package/dist/lib/task/local-artifact-finalization.js +1 -20
  48. package/dist/lib/task/manual-validation-receipt.js +8 -3
  49. package/dist/lib/task/orchestration.js +29 -13
  50. package/dist/lib/task/review-finalization.js +3 -16
  51. package/dist/lib/task/rework-intent.js +70 -4
  52. package/dist/lib/task/verification-engine.js +20 -30
  53. package/dist/lib/task/verification.js +1 -0
  54. package/dist/lib/task/workflow-warning-intents.js +21 -6
  55. package/dist/lib/task/workspace-snapshot.js +7 -2
  56. package/dist/lifecycle-build-manifest.json +3 -3
  57. package/lib/agent-clients/adapter.ts +46 -0
  58. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
  59. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
  60. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
  61. package/lib/agent-clients/adapters/codex.ts +10 -0
  62. package/lib/internal/cli-route-inventory.ts +1 -1
  63. package/lib/internal/manual-validation.ts +22 -26
  64. package/lib/internal/sandbox-control.ts +0 -42
  65. package/lib/internal/task-artifact.ts +1 -1
  66. package/lib/internal/task-finalization.ts +40 -1
  67. package/lib/internal/task-ledger.ts +12 -5
  68. package/lib/internal/task-lifecycle.ts +2 -2
  69. package/lib/internal/task-operation-registry.ts +8 -4
  70. package/lib/platform/completion-backfill.ts +78 -16
  71. package/lib/platform/issue-comments.ts +1 -35
  72. package/lib/platform/pr-summary.ts +10 -2
  73. package/lib/sandbox/control/client.ts +5 -68
  74. package/lib/sandbox/control/executor.ts +14 -81
  75. package/lib/sandbox/control/protocol.ts +7 -83
  76. package/lib/sandbox/control/server.ts +47 -362
  77. package/lib/sandbox/control/state.ts +1 -9
  78. package/lib/sandbox/control/task-view.ts +1 -2
  79. package/lib/sandbox/task-cleanup.ts +12 -20
  80. package/lib/task/artifact-command.ts +14 -4
  81. package/lib/task/artifact-lifecycle.ts +236 -85
  82. package/lib/task/artifact-operations.ts +1 -26
  83. package/lib/task/artifact-receipts.ts +4 -1
  84. package/lib/task/artifact-schema.ts +1 -0
  85. package/lib/task/artifact-selection.ts +53 -0
  86. package/lib/task/capabilities.ts +154 -40
  87. package/lib/task/commands/status.ts +11 -2
  88. package/lib/task/control-authority.ts +41 -48
  89. package/lib/task/control-recovery.ts +3 -43
  90. package/lib/task/decision-intents.ts +9 -0
  91. package/lib/task/delegation-receipts.ts +2 -2
  92. package/lib/task/delivery.ts +1 -40
  93. package/lib/task/events.ts +82 -64
  94. package/lib/task/finalization-artifacts.ts +1 -3
  95. package/lib/task/finalization-state.ts +1 -1
  96. package/lib/task/finalization.ts +310 -216
  97. package/lib/task/ledger-intents.ts +155 -15
  98. package/lib/task/lifecycle-path.ts +98 -0
  99. package/lib/task/lifecycle.ts +15 -2
  100. package/lib/task/local-artifact-finalization.ts +2 -25
  101. package/lib/task/manual-validation-receipt.ts +8 -2
  102. package/lib/task/orchestration.ts +56 -27
  103. package/lib/task/review-finalization.ts +3 -16
  104. package/lib/task/rework-intent.ts +75 -5
  105. package/lib/task/verification-engine.ts +19 -30
  106. package/lib/task/verification.ts +2 -1
  107. package/lib/task/workflow-warning-intents.ts +18 -6
  108. package/lib/task/workspace-snapshot.ts +3 -2
  109. package/package.json +1 -1
  110. package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
  111. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
  112. package/templates/.agents/rules/review-handshake.en.md +4 -4
  113. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  114. package/templates/.agents/skills/analyze-task/SKILL.en.md +11 -4
  115. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -4
  116. package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
  117. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
  118. package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
  119. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
  120. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
  121. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
  122. package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
  123. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
  124. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  125. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  126. package/templates/.agents/skills/complete-task/SKILL.en.md +7 -8
  127. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +7 -8
  128. package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
  129. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
  130. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
  131. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
  132. package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
  133. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
  134. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
  135. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
  136. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
  137. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
  138. package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
  139. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
  140. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
  141. package/templates/.agents/templates/task.en.md +2 -2
  142. package/templates/.agents/templates/task.zh-CN.md +2 -2
  143. package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
  144. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
  145. package/templates/.agents/workflows/feature-development.en.yaml +11 -6
  146. package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
  147. package/templates/.agents/workflows/refactoring.en.yaml +11 -6
  148. package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
  149. package/dist/lib/sandbox/control/finalization-response.js +0 -35
  150. package/dist/lib/sandbox/control/task-workflow.js +0 -74
  151. package/dist/lib/sandbox/control/workflow-executor.js +0 -142
  152. package/dist/lib/task/lifecycle-recovery.js +0 -549
  153. package/dist/lib/task/recovery-warning.js +0 -19
  154. package/dist/lib/task/workflow-command.js +0 -21
  155. package/dist/lib/task/workflow-dispatch.js +0 -213
  156. package/lib/sandbox/control/finalization-response.ts +0 -37
  157. package/lib/sandbox/control/task-workflow.ts +0 -95
  158. package/lib/sandbox/control/workflow-executor.ts +0 -156
  159. package/lib/task/lifecycle-recovery.ts +0 -691
  160. package/lib/task/recovery-warning.ts +0 -21
  161. package/lib/task/workflow-command.ts +0 -22
  162. package/lib/task/workflow-dispatch.ts +0 -214
@@ -1,5 +1,3 @@
1
- import { finalizationTerminalResponse } from './finalization-response.js';
2
- import { isDeepStrictEqual } from 'node:util';
3
1
  import { completedReentryView } from './completed-reentry.js';
4
2
  import fs from 'node:fs';
5
3
  import path from 'node:path';
@@ -13,10 +11,6 @@ import { validateSandboxControlRequest } from './protocol.js';
13
11
  import { requestAuditFields, resultAuditFields, appendCriticalAudit, appendDiagnosticAudit, createSandboxControlAuditContext, writeSandboxControlTransition, readSandboxControlTransition } from './audit.js';
14
12
  import { parseTaskControlOperation } from '../../task/control-authority.js';
15
13
  import { parseControlOutput, classifySandboxControlRecovery, findSandboxControlRecoveryOperation, operationRecoveryBinding, SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES } from '../../task/control-recovery.js';
16
- import { readRun } from '../../task/orchestration.js';
17
- import { readLifecycleRecoveryDomainEvidence } from '../../task/lifecycle-recovery.js';
18
- import { captureRepositorySnapshot } from '../../task/workspace-snapshot.js';
19
- import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
20
14
  import { readLifecycleJournalEvidence } from '../../task/lifecycle.js';
21
15
  import { resolveTaskRef } from '../../task/resolve-ref.js';
22
16
  import { loadShortIdByTaskId } from '../../task/short-id.js';
@@ -26,96 +20,34 @@ import { nextSandboxControlBackoff } from './timing.js';
26
20
  import { readTaskFinalizationReceipt } from '../../task/finalization.js';
27
21
  import { readCodexControllerRegistration } from './controller-registration.js';
28
22
  import { validateSandboxControlIdentity } from './identity-sentinel.js';
29
- import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
23
+ import { mergeSandboxTaskView, taskViewForManifest } from './task-view.js';
30
24
  import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
31
- import { consumeLifecycleRecoveryAttestation, issueLifecycleRecoveryAttestation, recoverLifecycleRecoveryOperation } from '../../task/control-authority.js';
32
- import { createCodexCapabilityStore } from '../../agent-clients/adapters/codex-lifecycle/capability-store.js';
33
- import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
34
- function consumeLifecycleAuthorityForResult(manifest, execution) {
35
- const authority = execution.lifecycleAuthority?.attestation;
36
- if (!authority || execution.result?.exitCode !== 0)
37
- return true;
38
- try {
39
- consumeLifecycleRecoveryAttestation(authority);
40
- return true;
41
- }
42
- catch (error) {
43
- appendBrokerAudit(manifest, 'lifecycle-authority-consume-retry', {
44
- ...requestAuditFields(manifest, execution.request),
45
- errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_CONSUME_FAILED'
46
- });
47
- return false;
48
- }
49
- }
50
- function lifecycleAuthorityForRequest(manifest, manifestPath, request) {
51
- const selector = request.family === 'task-workflow' || request.family === 'task-orchestration'
52
- ? request.authority
53
- : undefined;
54
- if (!selector)
55
- return undefined;
56
- let controllerBinding = null;
57
- try {
58
- const registration = readCodexControllerRegistration(manifestPath);
59
- controllerBinding = {
60
- instanceDigest: registration.controllerInstanceDigest,
61
- controlGeneration: registration.controlGeneration
62
- };
63
- }
64
- catch {
65
- // The issuer reports the stable unavailable result; the broker never trusts
66
- // a controller binding supplied by the request.
67
- }
68
- return issueLifecycleRecoveryAttestation(selector, {
69
- controllerBinding,
70
- capabilityStore: createCodexCapabilityStore(),
71
- buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
72
- });
73
- }
74
25
  function appendBrokerAudit(manifest, event, fields = {}) {
75
26
  appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
76
27
  }
77
- function operationKey(request, output) {
28
+ function operationKey(request) {
78
29
  if (request.family === 'task-finalization')
79
30
  return request.operation;
80
31
  if (request.family === 'task-create')
81
32
  return 'create';
82
33
  if (request.family === 'codex-controller')
83
34
  return request.command;
84
- if (request.family === 'task-workflow')
85
- return request.workflow.operation;
86
- if (request.family !== 'task-lifecycle' && request.family !== 'task-orchestration')
35
+ if (request.family !== 'task-lifecycle')
87
36
  return null;
88
- if (request.family === 'task-orchestration' && request.args[1] === 'route') {
89
- if (!output)
90
- return 'route';
91
- const result = parseControlOutput(output);
92
- return result?.changed === true && result.status === 'completed' ? 'route.clean-completion' : 'route.read';
93
- }
94
37
  try {
95
38
  const operation = parseTaskControlOperation(request.family, request.args);
96
- return operation.family === 'task-orchestration' ? operation.intent : operation.request.intent;
39
+ return operation.family === 'task-lifecycle' ? operation.request.intent : null;
97
40
  }
98
41
  catch {
99
42
  return null;
100
43
  }
101
44
  }
102
- function recoveryOperationKey(request, terminalResult, output) {
103
- const operation = operationKey(request, output);
104
- if (operation !== 'route')
105
- return operation;
106
- const digest = terminalResult?.intentDigest;
107
- for (const candidate of ['route.read', 'route.clean-completion']) {
108
- const expected = operationRecoveryBinding(request.id, request.generation, null, request.family, candidate).intentDigest;
109
- if (digest === expected)
110
- return candidate;
111
- }
112
- return null;
45
+ function recoveryOperationKey(request) {
46
+ return operationKey(request);
113
47
  }
114
48
  function terminalResultMatchesRequest(request, terminalResult) {
115
49
  const operation = operationKey(request);
116
- const candidates = operation === 'route'
117
- ? ['route.read', 'route.clean-completion']
118
- : operation ? [operation] : [];
50
+ const candidates = operation ? [operation] : [];
119
51
  return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
120
52
  }
121
53
  function criticalRequestPhase(manifest, request, phase, outcome, reference) {
@@ -192,48 +124,6 @@ export function writeSandboxControlResponse(manifest, response) {
192
124
  }
193
125
  return true;
194
126
  }
195
- function finalizationRecoveryResponse(manifest, requestId, exitCode) {
196
- if (!manifest.taskId || exitCode !== 0)
197
- return { status: 'not-applicable' };
198
- let receipt;
199
- try {
200
- receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
201
- if (!receipt || !receipt.controlBinding
202
- || receipt.controlBinding.generation !== manifest.generation
203
- || receipt.controlBinding.requestId !== requestId)
204
- return { status: 'deferred' };
205
- const resolved = resolveTaskRef(manifest.taskId, { repoRoot: manifest.repoRoot });
206
- if (!resolved.ok || resolved.state !== 'completed' || receipt.lifecycle !== 'done')
207
- return { status: 'deferred' };
208
- }
209
- catch {
210
- return { status: 'deferred' };
211
- }
212
- return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
213
- }
214
- function publishFinalizationTaskView(manifest, broker, requestId, state, reasonCode, activeRequestId) {
215
- let view;
216
- try {
217
- const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
218
- const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
219
- view = resolved.ok && resolved.state === 'completed'
220
- ? taskViewAfterFinalization({
221
- taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
222
- })
223
- : {
224
- state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
225
- reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
226
- };
227
- }
228
- catch {
229
- view = {
230
- state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
231
- reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
232
- };
233
- }
234
- writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
235
- return view;
236
- }
237
127
  function payloadReference(payload) {
238
128
  return {
239
129
  version: payload.version,
@@ -257,7 +147,7 @@ function outputMatchesEvidence(output, bytes, sha256) {
257
147
  return Buffer.byteLength(output, 'utf8') === bytes
258
148
  && createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
259
149
  }
260
- export function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery', warning = null) {
150
+ export function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
261
151
  const outputUnavailable = request.family === 'task-create' && !payload;
262
152
  return {
263
153
  version: 2,
@@ -265,11 +155,9 @@ export function genericRecoveryResponse(request, exitCode, payload, cause = 'rec
265
155
  phase: 'completed',
266
156
  exitCode,
267
157
  stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
268
- stderr: payload ? '' : warning
269
- ? `${warning.code}: ${warning.message}\nAction: ${warning.action}\n`
270
- : cause === 'publish'
271
- ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
272
- : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
158
+ stderr: payload ? '' : cause === 'publish'
159
+ ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
160
+ : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
273
161
  error: null,
274
162
  outputState: payload ? 'available' : 'unavailable',
275
163
  payload: payload ? payloadReference(payload) : null
@@ -335,32 +223,10 @@ function controllerDomainEvidence(manifest, manifestPath, request, output) {
335
223
  && output?.status === 'closed' && typeof output.changed === 'boolean' };
336
224
  }
337
225
  }
338
- function orchestrationDomainEvidence(operation, terminalResult, output, run) {
339
- if (!operation || !output)
340
- return { consistent: false };
341
- const outputRun = output.run && typeof output.run === 'object' && !Array.isArray(output.run)
342
- ? output.run : null;
343
- const runMatches = outputRun === null ? run === null : run !== null && JSON.stringify(run) === JSON.stringify(outputRun);
344
- if (operation.class === 'read-only') {
345
- return {
346
- consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
347
- snapshotValid: true,
348
- status: run?.status ?? terminalResult.status,
349
- pendingDelegation: run?.pendingDelegation ?? null
350
- };
351
- }
352
- if (!run || !outputRun)
353
- return { consistent: false };
354
- return {
355
- consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
356
- status: run.status,
357
- pendingDelegation: run.pendingDelegation
358
- };
359
- }
360
- function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
226
+ async function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
361
227
  if (!operation)
362
228
  return { domain: null };
363
- const taskRef = request.family === 'task-finalization' || request.family === 'task-workflow'
229
+ const taskRef = request.family === 'task-finalization'
364
230
  ? manifest.taskId
365
231
  : 'args' in request ? request.args[0] ?? null : manifest.taskId;
366
232
  const output = parseControlOutput(payloadOutput);
@@ -369,21 +235,7 @@ function readRecoveryDomain(manifest, manifestPath, request, operation, terminal
369
235
  }
370
236
  if (operation.family === 'task-lifecycle') {
371
237
  if (operation.intent === 'recover-started') {
372
- try {
373
- if (!('args' in request))
374
- return { domain: null };
375
- const parsed = parseTaskControlOperation('task-lifecycle', request.args);
376
- if (parsed.family !== 'task-lifecycle' || parsed.request.intent !== 'recover-started') {
377
- return { domain: null };
378
- }
379
- return {
380
- domain: readLifecycleRecoveryDomainEvidence(manifest.repoRoot, parsed.request, terminalResult),
381
- journal: readLifecycleJournalEvidence(manifest.repoRoot, taskRef)
382
- };
383
- }
384
- catch {
385
- return { domain: null };
386
- }
238
+ return { domain: null };
387
239
  }
388
240
  if (!taskRef || !terminalResult.targetState)
389
241
  return { domain: null };
@@ -403,51 +255,12 @@ function readRecoveryDomain(manifest, manifestPath, request, operation, terminal
403
255
  return { domain: { consistent: false }, journal };
404
256
  }
405
257
  }
406
- if (operation.family === 'task-orchestration') {
407
- if (!taskRef)
408
- return { domain: null };
409
- try {
410
- const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
411
- if (!resolved.ok)
412
- return { domain: { consistent: false } };
413
- const run = readRun(resolved.taskDir);
414
- if (!run && operation.class !== 'read-only')
415
- return { domain: { consistent: false } };
416
- if (operation.class === 'route.clean-completion') {
417
- const snapshot = captureRepositorySnapshot(manifest.repoRoot);
418
- const metadata = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
419
- const completion = run?.completionEvidence ?? null;
420
- const consistent = run !== null && completion !== null
421
- && terminalResult.completionEvidence !== null
422
- && isDeepStrictEqual(completion, terminalResult.completionEvidence);
423
- return {
424
- domain: {
425
- consistent,
426
- status: run?.status ?? null,
427
- pendingDelegation: run?.pendingDelegation ?? null,
428
- completionEvidence: completion,
429
- snapshot,
430
- lastReviewedCommit: metadata.last_reviewed_commit
431
- }
432
- };
433
- }
434
- return { domain: orchestrationDomainEvidence(operation, terminalResult, output, run) };
435
- }
436
- catch {
437
- return { domain: { consistent: false } };
438
- }
439
- }
440
258
  if (operation.family === 'task-create') {
441
259
  return { domain: taskCreateDomainEvidence(manifest, output) };
442
260
  }
443
261
  if (operation.family === 'codex-controller') {
444
262
  return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
445
263
  }
446
- if (operation.family === 'task-workflow') {
447
- return {
448
- domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
449
- };
450
- }
451
264
  return { domain: null };
452
265
  }
453
266
  function readCommittedCriticalPhases(manifest, requestId) {
@@ -461,8 +274,8 @@ function readCommittedCriticalPhases(manifest, requestId) {
461
274
  }
462
275
  });
463
276
  }
464
- export function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
465
- const operationName = recoveryOperationKey(request, terminalResult, payload?.stdout);
277
+ export async function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
278
+ const operationName = recoveryOperationKey(request);
466
279
  const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
467
280
  if (!operation)
468
281
  return null;
@@ -473,35 +286,7 @@ export function recoveryResponse(manifest, manifestPath, request, evidence, payl
473
286
  if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult))
474
287
  return unknown(request.id);
475
288
  }
476
- const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
477
- if (evidence.exitCode === 0 && finalization?.status === 'deferred')
478
- return null;
479
- if (evidence.exitCode === 0 && request.family === 'task-workflow' && request.authority?.phase === 'task-event.completed') {
480
- try {
481
- const registration = readCodexControllerRegistration(manifestPath);
482
- const recovered = recoverLifecycleRecoveryOperation(request.authority, {
483
- repoRoot: manifest.repoRoot,
484
- capabilityStore: createCodexCapabilityStore(),
485
- buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot),
486
- controllerBinding: {
487
- instanceDigest: registration.controllerInstanceDigest,
488
- controlGeneration: registration.controlGeneration
489
- }
490
- });
491
- if (recovered.status !== 'committed')
492
- return null;
493
- }
494
- catch (error) {
495
- appendBrokerAudit(manifest, 'lifecycle-authority-recovery-failed', {
496
- ...requestAuditFields(manifest, request),
497
- errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_RECOVERY_FAILED'
498
- });
499
- return null;
500
- }
501
- }
502
- const recovery = finalization
503
- ? { domain: { consistent: finalization.status === 'matched' } }
504
- : readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
289
+ const recovery = await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
505
290
  const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName);
506
291
  const decision = classifySandboxControlRecovery({
507
292
  operation,
@@ -518,23 +303,9 @@ export function recoveryResponse(manifest, manifestPath, request, evidence, payl
518
303
  return notExecuted(request.id);
519
304
  if (decision.outcome !== 'success' && decision.outcome !== 'failure')
520
305
  return null;
521
- if (request.family === 'task-finalization' && evidence.exitCode === 0) {
522
- if (finalization?.status !== 'matched')
523
- return null;
524
- return finalization.response ?? null;
525
- }
526
- return genericRecoveryResponse(request, evidence.exitCode, payload, 'recovery', terminalResult.warning ?? null);
306
+ return genericRecoveryResponse(request, evidence.exitCode, payload);
527
307
  }
528
- function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
529
- if (request.family === 'task-finalization' && evidence.exitCode === 0) {
530
- if (!terminalResult)
531
- return { valid: false, payloadReferenced: false };
532
- const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
533
- return {
534
- valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
535
- payloadReferenced: false
536
- };
537
- }
308
+ async function terminalMatchesEvidence(manifest, request, response, evidence, payload, payloadInvalid, terminalResult) {
538
309
  if (response.version !== 2 || response.id !== request.id || response.phase !== 'completed'
539
310
  || response.exitCode !== evidence.exitCode || response.error !== null) {
540
311
  return { valid: false, payloadReferenced: false };
@@ -558,7 +329,7 @@ function terminalMatchesEvidence(manifest, manifestPath, request, response, evid
558
329
  if (response.outputState === 'unavailable') {
559
330
  const causes = ['recovery', 'publish'];
560
331
  const valid = causes.some((cause) => JSON.stringify(response)
561
- === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause, terminalResult?.warning ?? null)));
332
+ === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
562
333
  return { valid, payloadReferenced: false };
563
334
  }
564
335
  return {
@@ -568,58 +339,41 @@ function terminalMatchesEvidence(manifest, manifestPath, request, response, evid
568
339
  payloadReferenced: false
569
340
  };
570
341
  }
571
- function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
342
+ function publishExecutionResult(manifest, request, result, brokerOwns) {
572
343
  const normalized = sanitizeSandboxControlResult(manifest, result);
573
344
  const terminalResult = writeSandboxControlTerminalResult(manifest, {
574
345
  id: request.id,
575
346
  family: request.family,
576
- operation: operationKey(request, normalized.stdout)
347
+ operation: operationKey(request)
577
348
  }, normalized.stdout);
578
349
  if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
579
350
  criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
580
351
  criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
581
352
  criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
582
353
  }
583
- let terminal = null;
584
- if (request.family === 'task-finalization') {
585
- const finalization = finalizationRecoveryResponse(manifest, request.id, normalized.exitCode);
586
- if (finalization.status === 'deferred')
587
- return false;
588
- terminal = finalization.response ?? null;
589
- }
590
- if (!terminal) {
591
- const inline = {
592
- version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
593
- stdout: normalized.stdout, stderr: normalized.stderr, error: null
594
- };
595
- if (sandboxControlEncodedJsonBytes(inline) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
596
- terminal = inline;
597
- }
354
+ let terminal = {
355
+ version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
356
+ stdout: normalized.stdout, stderr: normalized.stderr, error: null
357
+ };
358
+ if (sandboxControlEncodedJsonBytes(terminal) > SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
598
359
  let payload = null;
599
- if (!terminal) {
600
- try {
601
- payload = createSandboxControlPayload(manifest, request.id, normalized);
602
- const usage = sandboxControlGenerationUsage(manifest);
603
- if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
604
- writeSandboxControlPayload(manifest, request.id, normalized);
605
- }
606
- else {
607
- payload = null;
608
- }
360
+ try {
361
+ payload = createSandboxControlPayload(manifest, request.id, normalized);
362
+ const usage = sandboxControlGenerationUsage(manifest);
363
+ if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
364
+ writeSandboxControlPayload(manifest, request.id, normalized);
609
365
  }
610
- catch {
366
+ else {
611
367
  payload = null;
612
368
  }
613
- terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish', terminalResult.warning ?? null);
614
369
  }
370
+ catch {
371
+ payload = null;
372
+ }
373
+ terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
615
374
  }
616
375
  if (!brokerOwns())
617
376
  return false;
618
- if (request.family === 'task-finalization' && normalized.exitCode === 0) {
619
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
620
- if (view.state === 'unknown')
621
- return false;
622
- }
623
377
  const committed = writeSandboxControlResponse(manifest, terminal);
624
378
  if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
625
379
  criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
@@ -697,7 +451,7 @@ function assertCurrentSandboxControlIdentity(manifest, manifestPath) {
697
451
  throw new Error(`SANDBOX_CONTROL_IDENTITY_${result.state.replaceAll('-', '_').toUpperCase()}`);
698
452
  }
699
453
  }
700
- function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
454
+ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
701
455
  for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
702
456
  if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name))
703
457
  continue;
@@ -819,16 +573,11 @@ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
819
573
  if (terminal && request && resultEvidence) {
820
574
  if (!terminalResult && request.family !== 'task-create')
821
575
  continue;
822
- const reconciliation = terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
576
+ const reconciliation = await terminalMatchesEvidence(manifest, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
823
577
  if (!reconciliation.valid)
824
578
  continue;
825
579
  terminalReconciled = true;
826
580
  payloadReferenced = reconciliation.payloadReferenced;
827
- if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
828
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
829
- if (view.state === 'unknown')
830
- continue;
831
- }
832
581
  }
833
582
  if (!terminalResult && !terminalReconciled) {
834
583
  if (terminal)
@@ -852,14 +601,9 @@ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
852
601
  return false;
853
602
  if (!resultEvidence || !request || payloadInvalid)
854
603
  continue;
855
- const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
604
+ const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
856
605
  if (!recovered)
857
606
  continue;
858
- if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
859
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
860
- if (view.state === 'unknown')
861
- continue;
862
- }
863
607
  writeSandboxControlResponse(manifest, recovered);
864
608
  const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
865
609
  if (transitionProtocolActive && !preserveRecoveryEvidence) {
@@ -867,7 +611,7 @@ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
867
611
  }
868
612
  if (preserveRecoveryEvidence)
869
613
  continue;
870
- payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
614
+ payloadReferenced = Boolean(payload);
871
615
  }
872
616
  }
873
617
  else {
@@ -961,14 +705,10 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
961
705
  startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
962
706
  }
963
707
  catch {
964
- // Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
965
708
  startupReceipt = {};
966
709
  }
967
710
  }
968
- let taskView = taskViewForManifest({
969
- ...manifest,
970
- receipt: startupReceipt
971
- });
711
+ let taskView = taskViewForManifest({ ...manifest, receipt: startupReceipt });
972
712
  try {
973
713
  const previous = readSandboxControlStatus(manifest.publicStatusDir);
974
714
  if (previous.generation === manifest.generation)
@@ -1008,7 +748,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1008
748
  pid: broker.pid,
1009
749
  brokerId: broker.brokerId
1010
750
  });
1011
- if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns))
751
+ if (!await recoverProcessing(manifest, manifestPath, broker, brokerOwns))
1012
752
  return;
1013
753
  try {
1014
754
  const published = readSandboxControlStatus(manifest.publicStatusDir);
@@ -1150,21 +890,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1150
890
  break;
1151
891
  let terminalCommitted = false;
1152
892
  if (settledExecution.result && settledExecution.resultEvidenceWritten) {
1153
- if (!consumeLifecycleAuthorityForResult(manifest, settledExecution)) {
1154
- active = settledExecution;
1155
- continue;
1156
- }
1157
- terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
1158
- if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
1159
- try {
1160
- const published = readSandboxControlStatus(manifest.publicStatusDir);
1161
- if (published.generation === manifest.generation)
1162
- taskView = published.taskView;
1163
- }
1164
- catch {
1165
- taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
1166
- }
1167
- }
893
+ terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
1168
894
  }
1169
895
  else {
1170
896
  terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
@@ -1247,14 +973,9 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1247
973
  executorCwd: manifest.repoRoot,
1248
974
  executorEntry: options.internalCliPath ?? process.argv[1] ?? null
1249
975
  });
1250
- const lifecycleAuthority = lifecycleAuthorityForRequest(manifest, manifestPath, request);
1251
- if (lifecycleAuthority?.status === 'rejected') {
1252
- throw Object.assign(new Error(lifecycleAuthority.error?.message ?? 'lifecycle authority was rejected'), { code: lifecycleAuthority.error?.code ?? 'LIFECYCLE_AUTHORITY_REJECTED' });
1253
- }
1254
976
  prepared = await prepareExecution({
1255
977
  manifest, manifestPath, request, requestPath: claimed,
1256
- internalCliPath: options.internalCliPath ?? process.argv[1],
1257
- ...(lifecycleAuthority ? { lifecycleAuthority } : {})
978
+ internalCliPath: options.internalCliPath ?? process.argv[1]
1258
979
  });
1259
980
  const preparedExecution = prepared;
1260
981
  if (!preparedExecution)
@@ -1270,7 +991,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1270
991
  executionPath: executionPath(manifest, request.id)
1271
992
  });
1272
993
  const execution = {
1273
- request, prepared: preparedExecution, lifecycleAuthority, result: null,
994
+ request, prepared: preparedExecution, result: null,
1274
995
  resultEvidenceWritten: false, failure: null, settled: false
1275
996
  };
1276
997
  active = execution;
@@ -1431,8 +1152,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1431
1152
  }
1432
1153
  }
1433
1154
  if (owned && active.result && active.resultEvidenceWritten) {
1434
- if (consumeLifecycleAuthorityForResult(manifest, active)
1435
- && publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
1155
+ if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
1436
1156
  if (brokerOwns()) {
1437
1157
  removeAcceptedResponse(manifest, active.request.id);
1438
1158
  fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
@@ -1441,22 +1161,8 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1441
1161
  }
1442
1162
  }
1443
1163
  if (active) {
1444
- const terminationConfirmed = active.prepared.terminate(owned);
1445
- if (owned && brokerOwns() && active.request.family === 'task-finalization') {
1446
- const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
1447
- const view = recovered.status === 'matched'
1448
- ? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
1449
- : null;
1450
- if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
1451
- && writeSandboxControlResponse(manifest, recovered.response)) {
1452
- if (terminationConfirmed && brokerOwns()) {
1453
- removeAcceptedResponse(manifest, active.request.id);
1454
- fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
1455
- active = null;
1456
- }
1457
- }
1458
- }
1459
- if (owned && brokerOwns() && active && active.request.family !== 'task-finalization' && !active.resultEvidenceWritten) {
1164
+ active.prepared.terminate(owned);
1165
+ if (owned && brokerOwns() && active && !active.resultEvidenceWritten) {
1460
1166
  writeSandboxControlResponse(manifest, unknown(active.request.id));
1461
1167
  }
1462
1168
  }
@@ -1,6 +1,5 @@
1
1
  import { isCompletionEvidence } from '../../task/orchestration.js';
2
2
  import { parseControlOutput } from '../../task/control-recovery.js';
3
- import { isRecoveryWarning } from '../../task/recovery-warning.js';
4
3
  import { execFileSync } from 'node:child_process';
5
4
  import fs from 'node:fs';
6
5
  import path from 'node:path';
@@ -84,7 +83,6 @@ export function createSandboxControlTerminalResult(manifest, request, output) {
84
83
  ? nested.run : null;
85
84
  const rawCompletion = nested.completionEvidence ?? run?.completionEvidence;
86
85
  const operation = request.operation ?? (typeof nested.intent === 'string' ? nested.intent : null) ?? '';
87
- const warning = isRecoveryWarning(nested.warning) ? nested.warning : null;
88
86
  return {
89
87
  version: 1,
90
88
  requestId: request.id,
@@ -94,8 +92,7 @@ export function createSandboxControlTerminalResult(manifest, request, output) {
94
92
  targetState: typeof nested.targetState === 'string' ? nested.targetState : null,
95
93
  status: typeof nested.status === 'string' ? nested.status : 'completed',
96
94
  changed: typeof nested.changed === 'boolean' ? nested.changed : null,
97
- completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null,
98
- ...(warning ? { warning } : {})
95
+ completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
99
96
  };
100
97
  }
101
98
  export function writeSandboxControlTerminalResult(manifest, request, output) {
@@ -126,9 +123,6 @@ export function readSandboxControlTerminalResult(filePath) {
126
123
  if (value.completionEvidence !== null && !isCompletionEvidence(value.completionEvidence)) {
127
124
  throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
128
125
  }
129
- if (value.warning !== undefined && value.warning !== null && !isRecoveryWarning(value.warning)) {
130
- throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
131
- }
132
126
  if (value.changed === undefined) {
133
127
  throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
134
128
  }