@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.ts';
2
- import { isDeepStrictEqual } from 'node:util';
3
1
  import { completedReentryView } from './completed-reentry.ts';
4
2
  import fs from 'node:fs';
5
3
  import path from 'node:path';
@@ -12,7 +10,6 @@ import {
12
10
  SANDBOX_CONTROL_MAX_RESPONSE_BYTES,
13
11
  SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES,
14
12
  type SandboxControlManifest,
15
- type SandboxControlRecoveryWarning,
16
13
  type SandboxControlRequest,
17
14
  type SandboxControlResponse,
18
15
  type SandboxControlTimingPolicy
@@ -62,10 +59,6 @@ import {
62
59
  operationRecoveryBinding,
63
60
  SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES
64
61
  } from '../../task/control-recovery.ts';
65
- import { readRun } from '../../task/orchestration.ts';
66
- import { readLifecycleRecoveryDomainEvidence } from '../../task/lifecycle-recovery.ts';
67
- import { captureRepositorySnapshot } from '../../task/workspace-snapshot.ts';
68
- import { parseTypedTaskFrontmatter } from '../../task/frontmatter.ts';
69
62
  import { readLifecycleJournalEvidence } from '../../task/lifecycle.ts';
70
63
  import { resolveTaskRef } from '../../task/resolve-ref.ts';
71
64
  import { loadShortIdByTaskId } from '../../task/short-id.ts';
@@ -83,75 +76,20 @@ import { readCodexControllerRegistration } from './controller-registration.ts';
83
76
  import { validateSandboxControlIdentity } from './identity-sentinel.ts';
84
77
  import {
85
78
  mergeSandboxTaskView,
86
- taskViewAfterFinalization,
87
79
  taskViewForManifest,
88
80
  type SandboxTaskView
89
81
  } from './task-view.ts';
90
82
  import { taskCreateOutputUnavailableResult } from '../../task/create-service.ts';
91
- import {
92
- consumeLifecycleRecoveryAttestation,
93
- issueLifecycleRecoveryAttestation,
94
- recoverLifecycleRecoveryOperation,
95
- type LifecycleAuthorityResponseV1
96
- } from '../../task/control-authority.ts';
97
- import { createCodexCapabilityStore } from '../../agent-clients/adapters/codex-lifecycle/capability-store.ts';
98
- import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.ts';
99
83
 
100
84
  type ActiveExecution = {
101
85
  request: SandboxControlRequest;
102
86
  prepared: PreparedSandboxControlExecution;
103
- lifecycleAuthority: LifecycleAuthorityResponseV1 | undefined;
104
87
  result: SandboxControlExecutionResult | null;
105
88
  resultEvidenceWritten: boolean;
106
89
  failure: unknown;
107
90
  settled: boolean;
108
91
  };
109
92
 
110
- function consumeLifecycleAuthorityForResult(
111
- manifest: SandboxControlManifest,
112
- execution: ActiveExecution
113
- ): boolean {
114
- const authority = execution.lifecycleAuthority?.attestation;
115
- if (!authority || execution.result?.exitCode !== 0) return true;
116
- try {
117
- consumeLifecycleRecoveryAttestation(authority);
118
- return true;
119
- } catch (error) {
120
- appendBrokerAudit(manifest, 'lifecycle-authority-consume-retry', {
121
- ...requestAuditFields(manifest, execution.request),
122
- errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_CONSUME_FAILED'
123
- });
124
- return false;
125
- }
126
- }
127
-
128
- function lifecycleAuthorityForRequest(
129
- manifest: SandboxControlManifest,
130
- manifestPath: string,
131
- request: SandboxControlRequest
132
- ): LifecycleAuthorityResponseV1 | undefined {
133
- const selector = request.family === 'task-workflow' || request.family === 'task-orchestration'
134
- ? request.authority
135
- : undefined;
136
- if (!selector) return undefined;
137
- let controllerBinding: { instanceDigest: string; controlGeneration: string } | null = null;
138
- try {
139
- const registration = readCodexControllerRegistration(manifestPath);
140
- controllerBinding = {
141
- instanceDigest: registration.controllerInstanceDigest,
142
- controlGeneration: registration.controlGeneration
143
- };
144
- } catch {
145
- // The issuer reports the stable unavailable result; the broker never trusts
146
- // a controller binding supplied by the request.
147
- }
148
- return issueLifecycleRecoveryAttestation(selector, {
149
- controllerBinding,
150
- capabilityStore: createCodexCapabilityStore(),
151
- buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
152
- });
153
- }
154
-
155
93
  function appendBrokerAudit(
156
94
  manifest: SandboxControlManifest,
157
95
  event: string,
@@ -160,38 +98,21 @@ function appendBrokerAudit(
160
98
  appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
161
99
  }
162
100
 
163
- function operationKey(request: SandboxControlRequest, output?: string): string | null {
101
+ function operationKey(request: SandboxControlRequest): string | null {
164
102
  if (request.family === 'task-finalization') return request.operation;
165
103
  if (request.family === 'task-create') return 'create';
166
104
  if (request.family === 'codex-controller') return request.command;
167
- if (request.family === 'task-workflow') return request.workflow.operation;
168
- if (request.family !== 'task-lifecycle' && request.family !== 'task-orchestration') return null;
169
- if (request.family === 'task-orchestration' && request.args[1] === 'route') {
170
- if (!output) return 'route';
171
- const result = parseControlOutput(output);
172
- return result?.changed === true && result.status === 'completed' ? 'route.clean-completion' : 'route.read';
173
- }
105
+ if (request.family !== 'task-lifecycle') return null;
174
106
  try {
175
107
  const operation = parseTaskControlOperation(request.family, request.args);
176
- return operation.family === 'task-orchestration' ? operation.intent : operation.request.intent;
108
+ return operation.family === 'task-lifecycle' ? operation.request.intent : null;
177
109
  } catch {
178
110
  return null;
179
111
  }
180
112
  }
181
113
 
182
- function recoveryOperationKey(
183
- request: SandboxControlRequest,
184
- terminalResult: ReturnType<typeof readSandboxControlTerminalResult> | null,
185
- output: string | undefined
186
- ): string | null {
187
- const operation = operationKey(request, output);
188
- if (operation !== 'route') return operation;
189
- const digest = terminalResult?.intentDigest;
190
- for (const candidate of ['route.read', 'route.clean-completion'] as const) {
191
- const expected = operationRecoveryBinding(request.id, request.generation, null, request.family, candidate).intentDigest;
192
- if (digest === expected) return candidate;
193
- }
194
- return null;
114
+ function recoveryOperationKey(request: SandboxControlRequest): string | null {
115
+ return operationKey(request);
195
116
  }
196
117
 
197
118
  function terminalResultMatchesRequest(
@@ -199,9 +120,7 @@ function terminalResultMatchesRequest(
199
120
  terminalResult: ReturnType<typeof readSandboxControlTerminalResult>
200
121
  ): boolean {
201
122
  const operation = operationKey(request);
202
- const candidates = operation === 'route'
203
- ? ['route.read', 'route.clean-completion']
204
- : operation ? [operation] : [];
123
+ const candidates = operation ? [operation] : [];
205
124
  return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
206
125
  }
207
126
 
@@ -286,61 +205,6 @@ export function writeSandboxControlResponse(manifest: SandboxControlManifest, re
286
205
  return true;
287
206
  }
288
207
 
289
- type FinalizationRecovery = Readonly<{
290
- status: 'matched' | 'deferred' | 'not-applicable';
291
- response?: SandboxControlResponse;
292
- }>;
293
-
294
- function finalizationRecoveryResponse(
295
- manifest: SandboxControlManifest,
296
- requestId: string,
297
- exitCode: number
298
- ): FinalizationRecovery {
299
- if (!manifest.taskId || exitCode !== 0) return { status: 'not-applicable' };
300
- let receipt;
301
- try {
302
- receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
303
- if (!receipt || !receipt.controlBinding
304
- || receipt.controlBinding.generation !== manifest.generation
305
- || receipt.controlBinding.requestId !== requestId) return { status: 'deferred' };
306
- const resolved = resolveTaskRef(manifest.taskId, { repoRoot: manifest.repoRoot });
307
- if (!resolved.ok || resolved.state !== 'completed' || receipt.lifecycle !== 'done') return { status: 'deferred' };
308
- } catch {
309
- return { status: 'deferred' };
310
- }
311
- return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
312
- }
313
-
314
- function publishFinalizationTaskView(
315
- manifest: SandboxControlManifest,
316
- broker: BrokerOwner,
317
- requestId: string,
318
- state: 'starting' | 'healthy' | 'busy' | 'parked',
319
- reasonCode: string | null,
320
- activeRequestId: string | null
321
- ): SandboxTaskView {
322
- let view: SandboxTaskView;
323
- try {
324
- const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
325
- const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
326
- view = resolved.ok && resolved.state === 'completed'
327
- ? taskViewAfterFinalization({
328
- taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
329
- })
330
- : {
331
- state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
332
- reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
333
- };
334
- } catch {
335
- view = {
336
- state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
337
- reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
338
- };
339
- }
340
- writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
341
- return view;
342
- }
343
-
344
208
  function payloadReference(payload: ReturnType<typeof createSandboxControlPayload>) {
345
209
  return {
346
210
  version: payload.version,
@@ -374,8 +238,7 @@ export function genericRecoveryResponse(
374
238
  request: SandboxControlRequest,
375
239
  exitCode: number,
376
240
  payload: ReturnType<typeof readSandboxControlPayload> | null,
377
- cause: 'publish' | 'recovery' = 'recovery',
378
- warning: SandboxControlRecoveryWarning | null = null
241
+ cause: 'publish' | 'recovery' = 'recovery'
379
242
  ): SandboxControlResponse {
380
243
  const outputUnavailable = request.family === 'task-create' && !payload;
381
244
  return {
@@ -384,9 +247,7 @@ export function genericRecoveryResponse(
384
247
  phase: 'completed',
385
248
  exitCode,
386
249
  stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
387
- stderr: payload ? '' : warning
388
- ? `${warning.code}: ${warning.message}\nAction: ${warning.action}\n`
389
- : cause === 'publish'
250
+ stderr: payload ? '' : cause === 'publish'
390
251
  ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
391
252
  : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
392
253
  error: null,
@@ -465,42 +326,16 @@ function controllerDomainEvidence(
465
326
  }
466
327
  }
467
328
 
468
- function orchestrationDomainEvidence(
469
- operation: ReturnType<typeof findSandboxControlRecoveryOperation>,
470
- terminalResult: ReturnType<typeof readSandboxControlTerminalResult>,
471
- output: Record<string, unknown> | null,
472
- run: ReturnType<typeof readRun> | null
473
- ): Readonly<Record<string, unknown>> {
474
- if (!operation || !output) return { consistent: false };
475
- const outputRun = output.run && typeof output.run === 'object' && !Array.isArray(output.run)
476
- ? output.run as Record<string, unknown> : null;
477
- const runMatches = outputRun === null ? run === null : run !== null && JSON.stringify(run) === JSON.stringify(outputRun);
478
- if (operation.class === 'read-only') {
479
- return {
480
- consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
481
- snapshotValid: true,
482
- status: run?.status ?? terminalResult.status,
483
- pendingDelegation: run?.pendingDelegation ?? null
484
- };
485
- }
486
- if (!run || !outputRun) return { consistent: false };
487
- return {
488
- consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
489
- status: run.status,
490
- pendingDelegation: run.pendingDelegation
491
- };
492
- }
493
-
494
- function readRecoveryDomain(
329
+ async function readRecoveryDomain(
495
330
  manifest: SandboxControlManifest,
496
331
  manifestPath: string,
497
332
  request: SandboxControlRequest,
498
333
  operation: ReturnType<typeof findSandboxControlRecoveryOperation>,
499
334
  terminalResult: ReturnType<typeof readSandboxControlTerminalResult>,
500
335
  payloadOutput: string | null
501
- ): RecoveryDomainEvidence {
336
+ ): Promise<RecoveryDomainEvidence> {
502
337
  if (!operation) return { domain: null };
503
- const taskRef = request.family === 'task-finalization' || request.family === 'task-workflow'
338
+ const taskRef = request.family === 'task-finalization'
504
339
  ? manifest.taskId
505
340
  : 'args' in request ? request.args[0] ?? null : manifest.taskId;
506
341
  const output = parseControlOutput(payloadOutput);
@@ -510,19 +345,7 @@ function readRecoveryDomain(
510
345
 
511
346
  if (operation.family === 'task-lifecycle') {
512
347
  if (operation.intent === 'recover-started') {
513
- try {
514
- if (!('args' in request)) return { domain: null };
515
- const parsed = parseTaskControlOperation('task-lifecycle', request.args);
516
- if (parsed.family !== 'task-lifecycle' || parsed.request.intent !== 'recover-started') {
517
- return { domain: null };
518
- }
519
- return {
520
- domain: readLifecycleRecoveryDomainEvidence(manifest.repoRoot, parsed.request, terminalResult),
521
- journal: readLifecycleJournalEvidence(manifest.repoRoot, taskRef!)
522
- };
523
- } catch {
524
- return { domain: null };
525
- }
348
+ return { domain: null };
526
349
  }
527
350
  if (!taskRef || !terminalResult.targetState) return { domain: null };
528
351
  const journal = readLifecycleJournalEvidence(manifest.repoRoot, taskRef);
@@ -541,48 +364,12 @@ function readRecoveryDomain(
541
364
  }
542
365
  }
543
366
 
544
- if (operation.family === 'task-orchestration') {
545
- if (!taskRef) return { domain: null };
546
- try {
547
- const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
548
- if (!resolved.ok) return { domain: { consistent: false } };
549
- const run = readRun(resolved.taskDir);
550
- if (!run && operation.class !== 'read-only') return { domain: { consistent: false } };
551
- if (operation.class === 'route.clean-completion') {
552
- const snapshot = captureRepositorySnapshot(manifest.repoRoot);
553
- const metadata = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
554
- const completion = run?.completionEvidence ?? null;
555
- const consistent = run !== null && completion !== null
556
- && terminalResult.completionEvidence !== null
557
- && isDeepStrictEqual(completion, terminalResult.completionEvidence);
558
- return {
559
- domain: {
560
- consistent,
561
- status: run?.status ?? null,
562
- pendingDelegation: run?.pendingDelegation ?? null,
563
- completionEvidence: completion,
564
- snapshot,
565
- lastReviewedCommit: metadata.last_reviewed_commit
566
- }
567
- };
568
- }
569
- return { domain: orchestrationDomainEvidence(operation, terminalResult, output, run) };
570
- } catch {
571
- return { domain: { consistent: false } };
572
- }
573
- }
574
-
575
367
  if (operation.family === 'task-create') {
576
368
  return { domain: taskCreateDomainEvidence(manifest, output) };
577
369
  }
578
370
  if (operation.family === 'codex-controller') {
579
371
  return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
580
372
  }
581
- if (operation.family === 'task-workflow') {
582
- return {
583
- domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
584
- };
585
- }
586
373
  return { domain: null };
587
374
  }
588
375
 
@@ -597,15 +384,15 @@ function readCommittedCriticalPhases(manifest: SandboxControlManifest, requestId
597
384
  });
598
385
  }
599
386
 
600
- export function recoveryResponse(
387
+ export async function recoveryResponse(
601
388
  manifest: SandboxControlManifest,
602
389
  manifestPath: string,
603
390
  request: SandboxControlRequest,
604
391
  evidence: ReturnType<typeof readSandboxControlResultEvidence>,
605
392
  payload: ReturnType<typeof readSandboxControlPayload> | null,
606
393
  terminalResult: ReturnType<typeof readSandboxControlTerminalResult>
607
- ): SandboxControlResponse | null {
608
- const operationName = recoveryOperationKey(request, terminalResult, payload?.stdout);
394
+ ): Promise<SandboxControlResponse | null> {
395
+ const operationName = recoveryOperationKey(request);
609
396
  const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
610
397
  if (!operation) return null;
611
398
  if (payload) {
@@ -614,32 +401,7 @@ export function recoveryResponse(
614
401
  }, payload.stdout);
615
402
  if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult)) return unknown(request.id);
616
403
  }
617
- const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
618
- if (evidence.exitCode === 0 && finalization?.status === 'deferred') return null;
619
- if (evidence.exitCode === 0 && request.family === 'task-workflow' && request.authority?.phase === 'task-event.completed') {
620
- try {
621
- const registration = readCodexControllerRegistration(manifestPath);
622
- const recovered = recoverLifecycleRecoveryOperation(request.authority, {
623
- repoRoot: manifest.repoRoot,
624
- capabilityStore: createCodexCapabilityStore(),
625
- buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot),
626
- controllerBinding: {
627
- instanceDigest: registration.controllerInstanceDigest,
628
- controlGeneration: registration.controlGeneration
629
- }
630
- });
631
- if (recovered.status !== 'committed') return null;
632
- } catch (error) {
633
- appendBrokerAudit(manifest, 'lifecycle-authority-recovery-failed', {
634
- ...requestAuditFields(manifest, request),
635
- errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_RECOVERY_FAILED'
636
- });
637
- return null;
638
- }
639
- }
640
- const recovery: RecoveryDomainEvidence = finalization
641
- ? { domain: { consistent: finalization.status === 'matched' } }
642
- : readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
404
+ const recovery = await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
643
405
  const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName!);
644
406
  const decision = classifySandboxControlRecovery({
645
407
  operation,
@@ -653,31 +415,18 @@ export function recoveryResponse(
653
415
  if (decision.outcome === 'unknown' || decision.outcome === 'in-progress') return unknown(request.id);
654
416
  if (decision.outcome === 'not-executed') return notExecuted(request.id);
655
417
  if (decision.outcome !== 'success' && decision.outcome !== 'failure') return null;
656
- if (request.family === 'task-finalization' && evidence.exitCode === 0) {
657
- if (finalization?.status !== 'matched') return null;
658
- return finalization.response ?? null;
659
- }
660
- return genericRecoveryResponse(request, evidence.exitCode, payload, 'recovery', terminalResult.warning ?? null);
418
+ return genericRecoveryResponse(request, evidence.exitCode, payload);
661
419
  }
662
420
 
663
- function terminalMatchesEvidence(
421
+ async function terminalMatchesEvidence(
664
422
  manifest: SandboxControlManifest,
665
- manifestPath: string,
666
423
  request: SandboxControlRequest,
667
424
  response: SandboxControlResponse,
668
425
  evidence: ReturnType<typeof readSandboxControlResultEvidence>,
669
426
  payload: ReturnType<typeof readSandboxControlPayload> | null,
670
427
  payloadInvalid: boolean,
671
428
  terminalResult: ReturnType<typeof readSandboxControlTerminalResult> | null
672
- ): { valid: boolean; payloadReferenced: boolean } {
673
- if (request.family === 'task-finalization' && evidence.exitCode === 0) {
674
- if (!terminalResult) return { valid: false, payloadReferenced: false };
675
- const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
676
- return {
677
- valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
678
- payloadReferenced: false
679
- };
680
- }
429
+ ): Promise<{ valid: boolean; payloadReferenced: boolean }> {
681
430
  if (response.version !== 2 || response.id !== request.id || response.phase !== 'completed'
682
431
  || response.exitCode !== evidence.exitCode || response.error !== null) {
683
432
  return { valid: false, payloadReferenced: false };
@@ -701,7 +450,7 @@ function terminalMatchesEvidence(
701
450
  if (response.outputState === 'unavailable') {
702
451
  const causes = ['recovery', 'publish'] as const;
703
452
  const valid = causes.some((cause) => JSON.stringify(response)
704
- === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause, terminalResult?.warning ?? null)));
453
+ === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
705
454
  return { valid, payloadReferenced: false };
706
455
  }
707
456
  return {
@@ -716,55 +465,39 @@ function publishExecutionResult(
716
465
  manifest: SandboxControlManifest,
717
466
  request: SandboxControlRequest,
718
467
  result: SandboxControlExecutionResult,
719
- broker: BrokerOwner,
720
468
  brokerOwns: () => boolean
721
469
  ): boolean {
722
470
  const normalized = sanitizeSandboxControlResult(manifest, result);
723
471
  const terminalResult = writeSandboxControlTerminalResult(manifest, {
724
472
  id: request.id,
725
473
  family: request.family,
726
- operation: operationKey(request, normalized.stdout)
474
+ operation: operationKey(request)
727
475
  }, normalized.stdout);
728
476
  if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
729
477
  criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
730
478
  criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
731
479
  criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
732
480
  }
733
- let terminal: SandboxControlResponse | null = null;
734
- if (request.family === 'task-finalization') {
735
- const finalization = finalizationRecoveryResponse(manifest, request.id, normalized.exitCode);
736
- if (finalization.status === 'deferred') return false;
737
- terminal = finalization.response ?? null;
738
- }
739
- if (!terminal) {
740
- const inline: SandboxControlResponse = {
741
- version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
742
- stdout: normalized.stdout, stderr: normalized.stderr, error: null
743
- };
744
- if (sandboxControlEncodedJsonBytes(inline) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
745
- terminal = inline;
746
- }
481
+ let terminal: SandboxControlResponse = {
482
+ version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
483
+ stdout: normalized.stdout, stderr: normalized.stderr, error: null
484
+ };
485
+ if (sandboxControlEncodedJsonBytes(terminal) > SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
747
486
  let payload: ReturnType<typeof createSandboxControlPayload> | null = null;
748
- if (!terminal) {
749
- try {
750
- payload = createSandboxControlPayload(manifest, request.id, normalized);
751
- const usage = sandboxControlGenerationUsage(manifest);
752
- if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
753
- writeSandboxControlPayload(manifest, request.id, normalized);
754
- } else {
755
- payload = null;
756
- }
757
- } catch {
487
+ try {
488
+ payload = createSandboxControlPayload(manifest, request.id, normalized);
489
+ const usage = sandboxControlGenerationUsage(manifest);
490
+ if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
491
+ writeSandboxControlPayload(manifest, request.id, normalized);
492
+ } else {
758
493
  payload = null;
759
494
  }
760
- terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish', terminalResult.warning ?? null);
495
+ } catch {
496
+ payload = null;
761
497
  }
498
+ terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
762
499
  }
763
500
  if (!brokerOwns()) return false;
764
- if (request.family === 'task-finalization' && normalized.exitCode === 0) {
765
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
766
- if (view.state === 'unknown') return false;
767
- }
768
501
  const committed = writeSandboxControlResponse(manifest, terminal);
769
502
  if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
770
503
  criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
@@ -848,7 +581,7 @@ function assertCurrentSandboxControlIdentity(manifest: SandboxControlManifest, m
848
581
  }
849
582
  }
850
583
 
851
- function recoverProcessing(manifest: SandboxControlManifest, manifestPath: string, broker: BrokerOwner, brokerOwns: () => boolean): boolean {
584
+ async function recoverProcessing(manifest: SandboxControlManifest, manifestPath: string, broker: BrokerOwner, brokerOwns: () => boolean): Promise<boolean> {
852
585
  for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
853
586
  if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name)) continue;
854
587
  if (!brokerOwns()) return false;
@@ -955,9 +688,8 @@ function recoverProcessing(manifest: SandboxControlManifest, manifestPath: strin
955
688
  if (terminal && (!request || !resultEvidence)) continue;
956
689
  if (terminal && request && resultEvidence) {
957
690
  if (!terminalResult && request.family !== 'task-create') continue;
958
- const reconciliation = terminalMatchesEvidence(
691
+ const reconciliation = await terminalMatchesEvidence(
959
692
  manifest,
960
- manifestPath,
961
693
  request,
962
694
  existingTerminalResponse!,
963
695
  resultEvidence,
@@ -968,10 +700,6 @@ function recoverProcessing(manifest: SandboxControlManifest, manifestPath: strin
968
700
  if (!reconciliation.valid) continue;
969
701
  terminalReconciled = true;
970
702
  payloadReferenced = reconciliation.payloadReferenced;
971
- if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
972
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
973
- if (view.state === 'unknown') continue;
974
- }
975
703
  }
976
704
  if (!terminalResult && !terminalReconciled) {
977
705
  if (terminal) continue;
@@ -989,19 +717,15 @@ function recoverProcessing(manifest: SandboxControlManifest, manifestPath: strin
989
717
  if (!terminal) {
990
718
  if (!brokerOwns()) return false;
991
719
  if (!resultEvidence || !request || payloadInvalid) continue;
992
- const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult!);
720
+ const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult!);
993
721
  if (!recovered) continue;
994
- if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
995
- const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
996
- if (view.state === 'unknown') continue;
997
- }
998
722
  writeSandboxControlResponse(manifest, recovered);
999
723
  const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
1000
724
  if (transitionProtocolActive && !preserveRecoveryEvidence) {
1001
725
  writeSandboxControlTransition(manifest, { requestId: entry.name, phase: 'recovered' });
1002
726
  }
1003
727
  if (preserveRecoveryEvidence) continue;
1004
- payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
728
+ payloadReferenced = Boolean(payload);
1005
729
  }
1006
730
  } else {
1007
731
  if (!brokerOwns()) return false;
@@ -1099,14 +823,10 @@ export async function serveSandboxControl(
1099
823
  try {
1100
824
  startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
1101
825
  } catch {
1102
- // Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
1103
826
  startupReceipt = {};
1104
827
  }
1105
828
  }
1106
- let taskView = taskViewForManifest({
1107
- ...manifest,
1108
- receipt: startupReceipt
1109
- });
829
+ let taskView = taskViewForManifest({ ...manifest, receipt: startupReceipt });
1110
830
  try {
1111
831
  const previous = readSandboxControlStatus(manifest.publicStatusDir);
1112
832
  if (previous.generation === manifest.generation) taskView = mergeSandboxTaskView(taskView, previous.taskView);
@@ -1140,7 +860,7 @@ export async function serveSandboxControl(
1140
860
  pid: broker.pid,
1141
861
  brokerId: broker.brokerId
1142
862
  });
1143
- if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns)) return;
863
+ if (!await recoverProcessing(manifest, manifestPath, broker, brokerOwns)) return;
1144
864
  try {
1145
865
  const published = readSandboxControlStatus(manifest.publicStatusDir);
1146
866
  if (published.generation === manifest.generation) taskView = published.taskView;
@@ -1263,19 +983,7 @@ export async function serveSandboxControl(
1263
983
  if (!brokerOwns()) break;
1264
984
  let terminalCommitted = false;
1265
985
  if (settledExecution.result && settledExecution.resultEvidenceWritten) {
1266
- if (!consumeLifecycleAuthorityForResult(manifest, settledExecution)) {
1267
- active = settledExecution;
1268
- continue;
1269
- }
1270
- terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
1271
- if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
1272
- try {
1273
- const published = readSandboxControlStatus(manifest.publicStatusDir);
1274
- if (published.generation === manifest.generation) taskView = published.taskView;
1275
- } catch {
1276
- taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
1277
- }
1278
- }
986
+ terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
1279
987
  } else {
1280
988
  terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
1281
989
  }
@@ -1351,17 +1059,9 @@ export async function serveSandboxControl(
1351
1059
  executorCwd: manifest.repoRoot,
1352
1060
  executorEntry: options.internalCliPath ?? process.argv[1] ?? null
1353
1061
  });
1354
- const lifecycleAuthority = lifecycleAuthorityForRequest(manifest, manifestPath, request);
1355
- if (lifecycleAuthority?.status === 'rejected') {
1356
- throw Object.assign(
1357
- new Error(lifecycleAuthority.error?.message ?? 'lifecycle authority was rejected'),
1358
- { code: lifecycleAuthority.error?.code ?? 'LIFECYCLE_AUTHORITY_REJECTED' }
1359
- );
1360
- }
1361
1062
  prepared = await prepareExecution({
1362
1063
  manifest, manifestPath, request, requestPath: claimed,
1363
- internalCliPath: options.internalCliPath ?? process.argv[1]!,
1364
- ...(lifecycleAuthority ? { lifecycleAuthority } : {})
1064
+ internalCliPath: options.internalCliPath ?? process.argv[1]!
1365
1065
  });
1366
1066
  const preparedExecution = prepared;
1367
1067
  if (!preparedExecution) throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
@@ -1376,7 +1076,7 @@ export async function serveSandboxControl(
1376
1076
  executionPath: executionPath(manifest, request.id)
1377
1077
  });
1378
1078
  const execution: ActiveExecution = {
1379
- request, prepared: preparedExecution, lifecycleAuthority, result: null,
1079
+ request, prepared: preparedExecution, result: null,
1380
1080
  resultEvidenceWritten: false, failure: null, settled: false
1381
1081
  };
1382
1082
  active = execution;
@@ -1530,8 +1230,7 @@ export async function serveSandboxControl(
1530
1230
  }
1531
1231
  }
1532
1232
  if (owned && active.result && active.resultEvidenceWritten) {
1533
- if (consumeLifecycleAuthorityForResult(manifest, active)
1534
- && publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
1233
+ if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
1535
1234
  if (brokerOwns()) {
1536
1235
  removeAcceptedResponse(manifest, active.request.id);
1537
1236
  fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
@@ -1540,22 +1239,8 @@ export async function serveSandboxControl(
1540
1239
  }
1541
1240
  }
1542
1241
  if (active) {
1543
- const terminationConfirmed = active.prepared.terminate(owned);
1544
- if (owned && brokerOwns() && active.request.family === 'task-finalization') {
1545
- const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
1546
- const view = recovered.status === 'matched'
1547
- ? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
1548
- : null;
1549
- if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
1550
- && writeSandboxControlResponse(manifest, recovered.response)) {
1551
- if (terminationConfirmed && brokerOwns()) {
1552
- removeAcceptedResponse(manifest, active.request.id);
1553
- fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
1554
- active = null;
1555
- }
1556
- }
1557
- }
1558
- if (owned && brokerOwns() && active && active.request.family !== 'task-finalization' && !active.resultEvidenceWritten) {
1242
+ active.prepared.terminate(owned);
1243
+ if (owned && brokerOwns() && active && !active.resultEvidenceWritten) {
1559
1244
  writeSandboxControlResponse(manifest, unknown(active.request.id));
1560
1245
  }
1561
1246
  }