@fitlab-ai/agent-infra 0.11.2 → 0.11.3

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 (79) hide show
  1. package/dist/bin/internal-cli.js +7 -2
  2. package/dist/lib/agent-clients/adapter.js +3 -0
  3. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
  6. package/dist/lib/agent-clients/adapters/codex.js +10 -0
  7. package/dist/lib/internal/manual-validation.js +22 -27
  8. package/dist/lib/internal/sandbox-control.js +1 -43
  9. package/dist/lib/internal/task-lifecycle.js +1 -1
  10. package/dist/lib/internal/task-operation-registry.js +6 -2
  11. package/dist/lib/platform/issue-comments.js +2 -31
  12. package/dist/lib/platform/pr-summary.js +7 -2
  13. package/dist/lib/sandbox/control/client.js +3 -44
  14. package/dist/lib/sandbox/control/executor.js +14 -79
  15. package/dist/lib/sandbox/control/finalization-response.js +3 -5
  16. package/dist/lib/sandbox/control/protocol.js +5 -64
  17. package/dist/lib/sandbox/control/server.js +29 -206
  18. package/dist/lib/sandbox/control/state.js +1 -7
  19. package/dist/lib/sandbox/control/task-view.js +1 -2
  20. package/dist/lib/task/control-authority.js +34 -36
  21. package/dist/lib/task/control-recovery.js +3 -42
  22. package/dist/lib/task/delegation-receipts.js +2 -2
  23. package/dist/lib/task/finalization-artifacts.js +1 -3
  24. package/dist/lib/task/finalization-state.js +1 -1
  25. package/dist/lib/task/finalization.js +53 -126
  26. package/dist/lib/task/lifecycle.js +1 -1
  27. package/dist/lib/task/manual-validation-receipt.js +8 -3
  28. package/dist/lib/task/orchestration.js +12 -6
  29. package/dist/lifecycle-build-manifest.json +3 -3
  30. package/lib/agent-clients/adapter.ts +46 -0
  31. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
  32. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
  33. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
  34. package/lib/agent-clients/adapters/codex.ts +10 -0
  35. package/lib/internal/manual-validation.ts +22 -26
  36. package/lib/internal/sandbox-control.ts +0 -42
  37. package/lib/internal/task-lifecycle.ts +2 -2
  38. package/lib/internal/task-operation-registry.ts +7 -4
  39. package/lib/platform/issue-comments.ts +1 -35
  40. package/lib/platform/pr-summary.ts +10 -2
  41. package/lib/sandbox/control/client.ts +5 -68
  42. package/lib/sandbox/control/executor.ts +14 -81
  43. package/lib/sandbox/control/finalization-response.ts +3 -5
  44. package/lib/sandbox/control/protocol.ts +7 -83
  45. package/lib/sandbox/control/server.ts +30 -220
  46. package/lib/sandbox/control/state.ts +1 -9
  47. package/lib/sandbox/control/task-view.ts +1 -2
  48. package/lib/task/control-authority.ts +38 -47
  49. package/lib/task/control-recovery.ts +3 -43
  50. package/lib/task/delegation-receipts.ts +2 -2
  51. package/lib/task/finalization-artifacts.ts +1 -3
  52. package/lib/task/finalization-state.ts +1 -1
  53. package/lib/task/finalization.ts +57 -121
  54. package/lib/task/lifecycle.ts +2 -2
  55. package/lib/task/manual-validation-receipt.ts +8 -2
  56. package/lib/task/orchestration.ts +30 -19
  57. package/package.json +1 -1
  58. package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
  59. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
  60. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  61. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  62. package/templates/.agents/skills/complete-task/SKILL.en.md +6 -7
  63. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +6 -7
  64. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
  65. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
  66. package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
  67. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
  68. package/dist/lib/sandbox/control/task-workflow.js +0 -74
  69. package/dist/lib/sandbox/control/workflow-executor.js +0 -142
  70. package/dist/lib/task/lifecycle-recovery.js +0 -549
  71. package/dist/lib/task/recovery-warning.js +0 -19
  72. package/dist/lib/task/workflow-command.js +0 -21
  73. package/dist/lib/task/workflow-dispatch.js +0 -213
  74. package/lib/sandbox/control/task-workflow.ts +0 -95
  75. package/lib/sandbox/control/workflow-executor.ts +0 -156
  76. package/lib/task/lifecycle-recovery.ts +0 -691
  77. package/lib/task/recovery-warning.ts +0 -21
  78. package/lib/task/workflow-command.ts +0 -22
  79. package/lib/task/workflow-dispatch.ts +0 -214
@@ -1,5 +1,4 @@
1
1
  import { finalizationTerminalResponse } from './finalization-response.js';
2
- import { isDeepStrictEqual } from 'node:util';
3
2
  import { completedReentryView } from './completed-reentry.js';
4
3
  import fs from 'node:fs';
5
4
  import path from 'node:path';
@@ -13,10 +12,6 @@ import { validateSandboxControlRequest } from './protocol.js';
13
12
  import { requestAuditFields, resultAuditFields, appendCriticalAudit, appendDiagnosticAudit, createSandboxControlAuditContext, writeSandboxControlTransition, readSandboxControlTransition } from './audit.js';
14
13
  import { parseTaskControlOperation } from '../../task/control-authority.js';
15
14
  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
15
  import { readLifecycleJournalEvidence } from '../../task/lifecycle.js';
21
16
  import { resolveTaskRef } from '../../task/resolve-ref.js';
22
17
  import { loadShortIdByTaskId } from '../../task/short-id.js';
@@ -28,94 +23,32 @@ import { readCodexControllerRegistration } from './controller-registration.js';
28
23
  import { validateSandboxControlIdentity } from './identity-sentinel.js';
29
24
  import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
30
25
  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
26
  function appendBrokerAudit(manifest, event, fields = {}) {
75
27
  appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
76
28
  }
77
- function operationKey(request, output) {
29
+ function operationKey(request) {
78
30
  if (request.family === 'task-finalization')
79
31
  return request.operation;
80
32
  if (request.family === 'task-create')
81
33
  return 'create';
82
34
  if (request.family === 'codex-controller')
83
35
  return request.command;
84
- if (request.family === 'task-workflow')
85
- return request.workflow.operation;
86
- if (request.family !== 'task-lifecycle' && request.family !== 'task-orchestration')
36
+ if (request.family !== 'task-lifecycle')
87
37
  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
38
  try {
95
39
  const operation = parseTaskControlOperation(request.family, request.args);
96
- return operation.family === 'task-orchestration' ? operation.intent : operation.request.intent;
40
+ return operation.family === 'task-lifecycle' ? operation.request.intent : null;
97
41
  }
98
42
  catch {
99
43
  return null;
100
44
  }
101
45
  }
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;
46
+ function recoveryOperationKey(request) {
47
+ return operationKey(request);
113
48
  }
114
49
  function terminalResultMatchesRequest(request, terminalResult) {
115
50
  const operation = operationKey(request);
116
- const candidates = operation === 'route'
117
- ? ['route.read', 'route.clean-completion']
118
- : operation ? [operation] : [];
51
+ const candidates = operation ? [operation] : [];
119
52
  return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
120
53
  }
121
54
  function criticalRequestPhase(manifest, request, phase, outcome, reference) {
@@ -257,7 +190,7 @@ function outputMatchesEvidence(output, bytes, sha256) {
257
190
  return Buffer.byteLength(output, 'utf8') === bytes
258
191
  && createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
259
192
  }
260
- export function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery', warning = null) {
193
+ export function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
261
194
  const outputUnavailable = request.family === 'task-create' && !payload;
262
195
  return {
263
196
  version: 2,
@@ -265,11 +198,9 @@ export function genericRecoveryResponse(request, exitCode, payload, cause = 'rec
265
198
  phase: 'completed',
266
199
  exitCode,
267
200
  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',
201
+ stderr: payload ? '' : cause === 'publish'
202
+ ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
203
+ : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
273
204
  error: null,
274
205
  outputState: payload ? 'available' : 'unavailable',
275
206
  payload: payload ? payloadReference(payload) : null
@@ -335,32 +266,10 @@ function controllerDomainEvidence(manifest, manifestPath, request, output) {
335
266
  && output?.status === 'closed' && typeof output.changed === 'boolean' };
336
267
  }
337
268
  }
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) {
269
+ async function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
361
270
  if (!operation)
362
271
  return { domain: null };
363
- const taskRef = request.family === 'task-finalization' || request.family === 'task-workflow'
272
+ const taskRef = request.family === 'task-finalization'
364
273
  ? manifest.taskId
365
274
  : 'args' in request ? request.args[0] ?? null : manifest.taskId;
366
275
  const output = parseControlOutput(payloadOutput);
@@ -369,21 +278,7 @@ function readRecoveryDomain(manifest, manifestPath, request, operation, terminal
369
278
  }
370
279
  if (operation.family === 'task-lifecycle') {
371
280
  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
- }
281
+ return { domain: null };
387
282
  }
388
283
  if (!taskRef || !terminalResult.targetState)
389
284
  return { domain: null };
@@ -403,51 +298,12 @@ function readRecoveryDomain(manifest, manifestPath, request, operation, terminal
403
298
  return { domain: { consistent: false }, journal };
404
299
  }
405
300
  }
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
301
  if (operation.family === 'task-create') {
441
302
  return { domain: taskCreateDomainEvidence(manifest, output) };
442
303
  }
443
304
  if (operation.family === 'codex-controller') {
444
305
  return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
445
306
  }
446
- if (operation.family === 'task-workflow') {
447
- return {
448
- domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
449
- };
450
- }
451
307
  return { domain: null };
452
308
  }
453
309
  function readCommittedCriticalPhases(manifest, requestId) {
@@ -461,8 +317,8 @@ function readCommittedCriticalPhases(manifest, requestId) {
461
317
  }
462
318
  });
463
319
  }
464
- export function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
465
- const operationName = recoveryOperationKey(request, terminalResult, payload?.stdout);
320
+ export async function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
321
+ const operationName = recoveryOperationKey(request);
466
322
  const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
467
323
  if (!operation)
468
324
  return null;
@@ -476,32 +332,9 @@ export function recoveryResponse(manifest, manifestPath, request, evidence, payl
476
332
  const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
477
333
  if (evidence.exitCode === 0 && finalization?.status === 'deferred')
478
334
  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
335
  const recovery = finalization
503
336
  ? { domain: { consistent: finalization.status === 'matched' } }
504
- : readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
337
+ : await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
505
338
  const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName);
506
339
  const decision = classifySandboxControlRecovery({
507
340
  operation,
@@ -523,13 +356,13 @@ export function recoveryResponse(manifest, manifestPath, request, evidence, payl
523
356
  return null;
524
357
  return finalization.response ?? null;
525
358
  }
526
- return genericRecoveryResponse(request, evidence.exitCode, payload, 'recovery', terminalResult.warning ?? null);
359
+ return genericRecoveryResponse(request, evidence.exitCode, payload);
527
360
  }
528
- function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
361
+ async function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
529
362
  if (request.family === 'task-finalization' && evidence.exitCode === 0) {
530
363
  if (!terminalResult)
531
364
  return { valid: false, payloadReferenced: false };
532
- const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
365
+ const expected = await recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
533
366
  return {
534
367
  valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
535
368
  payloadReferenced: false
@@ -558,7 +391,7 @@ function terminalMatchesEvidence(manifest, manifestPath, request, response, evid
558
391
  if (response.outputState === 'unavailable') {
559
392
  const causes = ['recovery', 'publish'];
560
393
  const valid = causes.some((cause) => JSON.stringify(response)
561
- === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause, terminalResult?.warning ?? null)));
394
+ === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
562
395
  return { valid, payloadReferenced: false };
563
396
  }
564
397
  return {
@@ -573,7 +406,7 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
573
406
  const terminalResult = writeSandboxControlTerminalResult(manifest, {
574
407
  id: request.id,
575
408
  family: request.family,
576
- operation: operationKey(request, normalized.stdout)
409
+ operation: operationKey(request)
577
410
  }, normalized.stdout);
578
411
  if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
579
412
  criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
@@ -610,7 +443,7 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
610
443
  catch {
611
444
  payload = null;
612
445
  }
613
- terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish', terminalResult.warning ?? null);
446
+ terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
614
447
  }
615
448
  }
616
449
  if (!brokerOwns())
@@ -697,7 +530,7 @@ function assertCurrentSandboxControlIdentity(manifest, manifestPath) {
697
530
  throw new Error(`SANDBOX_CONTROL_IDENTITY_${result.state.replaceAll('-', '_').toUpperCase()}`);
698
531
  }
699
532
  }
700
- function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
533
+ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
701
534
  for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
702
535
  if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name))
703
536
  continue;
@@ -819,7 +652,7 @@ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
819
652
  if (terminal && request && resultEvidence) {
820
653
  if (!terminalResult && request.family !== 'task-create')
821
654
  continue;
822
- const reconciliation = terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
655
+ const reconciliation = await terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
823
656
  if (!reconciliation.valid)
824
657
  continue;
825
658
  terminalReconciled = true;
@@ -852,7 +685,7 @@ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
852
685
  return false;
853
686
  if (!resultEvidence || !request || payloadInvalid)
854
687
  continue;
855
- const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
688
+ const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
856
689
  if (!recovered)
857
690
  continue;
858
691
  if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
@@ -1008,7 +841,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1008
841
  pid: broker.pid,
1009
842
  brokerId: broker.brokerId
1010
843
  });
1011
- if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns))
844
+ if (!await recoverProcessing(manifest, manifestPath, broker, brokerOwns))
1012
845
  return;
1013
846
  try {
1014
847
  const published = readSandboxControlStatus(manifest.publicStatusDir);
@@ -1150,10 +983,6 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1150
983
  break;
1151
984
  let terminalCommitted = false;
1152
985
  if (settledExecution.result && settledExecution.resultEvidenceWritten) {
1153
- if (!consumeLifecycleAuthorityForResult(manifest, settledExecution)) {
1154
- active = settledExecution;
1155
- continue;
1156
- }
1157
986
  terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
1158
987
  if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
1159
988
  try {
@@ -1247,14 +1076,9 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1247
1076
  executorCwd: manifest.repoRoot,
1248
1077
  executorEntry: options.internalCliPath ?? process.argv[1] ?? null
1249
1078
  });
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
1079
  prepared = await prepareExecution({
1255
1080
  manifest, manifestPath, request, requestPath: claimed,
1256
- internalCliPath: options.internalCliPath ?? process.argv[1],
1257
- ...(lifecycleAuthority ? { lifecycleAuthority } : {})
1081
+ internalCliPath: options.internalCliPath ?? process.argv[1]
1258
1082
  });
1259
1083
  const preparedExecution = prepared;
1260
1084
  if (!preparedExecution)
@@ -1270,7 +1094,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1270
1094
  executionPath: executionPath(manifest, request.id)
1271
1095
  });
1272
1096
  const execution = {
1273
- request, prepared: preparedExecution, lifecycleAuthority, result: null,
1097
+ request, prepared: preparedExecution, result: null,
1274
1098
  resultEvidenceWritten: false, failure: null, settled: false
1275
1099
  };
1276
1100
  active = execution;
@@ -1431,8 +1255,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1431
1255
  }
1432
1256
  }
1433
1257
  if (owned && active.result && active.resultEvidenceWritten) {
1434
- if (consumeLifecycleAuthorityForResult(manifest, active)
1435
- && publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
1258
+ if (publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
1436
1259
  if (brokerOwns()) {
1437
1260
  removeAcceptedResponse(manifest, active.request.id);
1438
1261
  fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
@@ -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
  }
@@ -13,11 +13,10 @@ function receiptIdentity(value, generation, requestId) {
13
13
  if (!binding || typeof binding !== 'object' || Array.isArray(binding))
14
14
  return null;
15
15
  const controlBinding = binding;
16
- if (receipt.version !== 3 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
16
+ if (receipt.version !== 4 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
17
17
  || !Number.isSafeInteger(receipt.revision) || receipt.revision < 0
18
18
  || receipt.lifecycle !== 'done'
19
19
  || !['pending', 'done', 'skipped'].includes(String(receipt.summary))
20
- || !['pending', 'done', 'skipped'].includes(String(receipt.postSummaryVerification))
21
20
  || controlBinding.generation !== generation
22
21
  || typeof controlBinding.requestId !== 'string'
23
22
  || !/^[a-f0-9-]{16,64}$/u.test(controlBinding.requestId)
@@ -3,12 +3,11 @@ import crypto from 'node:crypto';
3
3
  import fs from 'node:fs';
4
4
  import { normalizeAgentToken } from '../agent-clients/tokens.js';
5
5
  import { isAgentClientId } from '../agent-clients/types.js';
6
+ import { getAgentClientAdapter } from '../agent-clients/registry.js';
6
7
  import { consumeHumanOverride, failureId, overrideDryRunConflict } from './human-override.js';
7
8
  import { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, awaitOrchestrationDelegationActivation, beginOrResumeOrchestration, dispatchOrchestrationDelegation, OrchestrationStateError, pauseOrchestration, prepareOrchestrationDelegation, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration } from './orchestration.js';
8
- import { prepareCodexOrchestrationDelegation } from './codex-orchestration.js';
9
9
  import { applyTaskFinalization } from './finalization.js';
10
10
  import { applyTaskLifecycle, taskLifecycleFailure } from './lifecycle.js';
11
- import { recoverStartedLifecycleUnderLock, recoveryFailure } from './lifecycle-recovery.js';
12
11
  import { locateActivityLog } from './activity-log.js';
13
12
  import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
14
13
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
@@ -407,7 +406,6 @@ export function createSandboxExecutorExecutionContext(params) {
407
406
  generation: params.generation,
408
407
  manifestPath: absolute('manifestPath', params.manifestPath),
409
408
  requestId: params.requestId,
410
- ...(params.diagnosticLog === undefined ? {} : { diagnosticLog: params.diagnosticLog }),
411
409
  ...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) }),
412
410
  ...(params.lifecycleRecoveryAttestation === undefined ? {} : {
413
411
  lifecycleRecoveryAttestation: params.lifecycleRecoveryAttestation === null
@@ -450,11 +448,7 @@ function operationTaskId(context, taskRef) {
450
448
  function domainOptions(context, options) {
451
449
  return {
452
450
  ...options,
453
- repoRoot: context.repoRoot,
454
- ...(context.source === 'sandbox-executor' && context.diagnosticLog !== undefined
455
- ? { diagnosticLog: context.diagnosticLog }
456
- : {}),
457
- ...(context.source === 'sandbox-executor' ? { gitWorktreeRoot: context.worktreeRoot } : {})
451
+ repoRoot: context.repoRoot
458
452
  };
459
453
  }
460
454
  function lifecycleTaskId(request, repoRoot) {
@@ -475,7 +469,17 @@ async function applyLifecycleWithAuthority(context, request) {
475
469
  }
476
470
  const execute = async () => {
477
471
  if (request.intent === 'recover-started') {
478
- return recoverStartedLifecycleUnderLock(request, { repoRoot: context.repoRoot });
472
+ const adapter = isAgentClientId(request.agent)
473
+ ? getAgentClientAdapter(request.agent).orchestrationAdapter
474
+ : undefined;
475
+ if (!adapter?.recoverStarted) {
476
+ return {
477
+ status: 'no-op', changed: false, targetState: 'active',
478
+ requestRef: request.taskRef, intent: 'recover-started', taskId: null,
479
+ receiptId: null, childId: null, error: null
480
+ };
481
+ }
482
+ return adapter.recoverStarted(request, { repoRoot: context.repoRoot });
479
483
  }
480
484
  const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
481
485
  if (lifecycleResult.status !== 'failed' || !request.overrideTicket)
@@ -511,7 +515,12 @@ async function applyLifecycleWithAuthority(context, request) {
511
515
  if (!(error instanceof TaskExecutionLockError))
512
516
  throw error;
513
517
  if (request.intent === 'recover-started') {
514
- return recoveryFailure(request, 'owner-unknown', error.code, error.message);
518
+ return {
519
+ status: 'owner-unknown', changed: false, targetState: 'active',
520
+ requestRef: request.taskRef, intent: 'recover-started', taskId,
521
+ receiptId: null, childId: null,
522
+ error: { code: error.code, message: error.message }
523
+ };
515
524
  }
516
525
  return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
517
526
  }
@@ -536,8 +545,10 @@ function orchestration(context, operation) {
536
545
  requestedModel: input.requestedModel,
537
546
  requestedReasoningEffort: input.requestedReasoningEffort,
538
547
  };
539
- if (prepareInput.client === 'codex') {
540
- return prepareCodexOrchestrationDelegation(operation.taskRef, prepareInput, {
548
+ const prepareDelegation = getAgentClientAdapter(prepareInput.client)
549
+ .orchestrationAdapter?.prepareDelegation;
550
+ if (prepareDelegation) {
551
+ return prepareDelegation(operation.taskRef, prepareInput, {
541
552
  repoRoot: context.repoRoot,
542
553
  orchestrationOptions: options,
543
554
  });
@@ -617,7 +628,7 @@ function operationInvalid(message) {
617
628
  }
618
629
  const LIFECYCLE_FLAGS = new Set([
619
630
  '--agent', '--reason', '--unblock-condition', '--note', '--alert-number', '--staging-dir', '--issue-number',
620
- '--stage', '--round', '--artifact', '--override-ticket', '--override-target', '--override-scope', '--dry-run'
631
+ '--auto', '--override-ticket', '--override-target', '--override-scope', '--dry-run'
621
632
  ]);
622
633
  const FINALIZATION_FLAGS = new Set(['--agent']);
623
634
  const ORCHESTRATION_FLAGS = new Set([
@@ -626,13 +637,13 @@ const ORCHESTRATION_FLAGS = new Set([
626
637
  '--parent-id', '--before-fingerprint', '--stage', '--round', '--artifact', '--role',
627
638
  '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
628
639
  '--model-fallback-reason', '--reasoning-effort-fallback-reason', '--exit-code', '--after-fingerprint',
629
- '--changed-paths', '--code', '--message', '--recoverable', '--git-worktree-root'
640
+ '--changed-paths', '--code', '--message', '--recoverable'
630
641
  ]);
631
642
  function parseValues(args, start, flags) {
632
643
  const values = {};
633
644
  for (let index = start; index < args.length; index += 1) {
634
645
  const flag = args[index];
635
- if (flag === '--dry-run') {
646
+ if (flag === '--dry-run' || flag === '--auto') {
636
647
  if (!flags.has(flag))
637
648
  operationInvalid(`unknown option '${flag}'`);
638
649
  if (values[flag] !== undefined)
@@ -693,31 +704,21 @@ export function parseTaskControlOperation(family, args) {
693
704
  ...(value(values, '--override-scope') ? { overrideScope: value(values, '--override-scope') } : {}),
694
705
  ...(value(values, '--alert-number') ? { alertNumber: Number(value(values, '--alert-number')) } : {}),
695
706
  ...(value(values, '--issue-number') ? { issueNumber: Number(value(values, '--issue-number')) } : {}),
696
- ...(value(values, '--stage') ? { stage: value(values, '--stage') } : {}),
697
- ...(value(values, '--round') ? { round: Number(value(values, '--round')) } : {}),
698
- ...(value(values, '--artifact') ? { artifact: value(values, '--artifact') } : {}),
707
+ ...(values['--auto'] === true ? { auto: true } : {}),
699
708
  ...(values['--dry-run'] === true ? { dryRun: true } : {})
700
709
  };
701
710
  if (intent === 'recover-started') {
702
- const recoveryFlags = new Set(['--agent', '--stage', '--round', '--artifact', '--reason']);
711
+ const recoveryFlags = new Set(['--agent', '--auto']);
703
712
  for (const flag of Object.keys(values)) {
704
713
  if (!recoveryFlags.has(flag))
705
714
  operationInvalid(`option '${flag}' is not supported for recover-started`);
706
715
  }
707
- required(values, ['--stage', '--round', '--artifact', '--reason']);
708
- const stage = value(values, '--stage');
709
- if (!['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code'].includes(stage ?? '')) {
710
- operationInvalid('--stage must be a supported lifecycle stage');
711
- }
712
- const round = Number(value(values, '--round'));
713
- if (!Number.isSafeInteger(round) || round < 1)
714
- operationInvalid('--round must be a positive integer');
715
- if (value(values, '--reason').includes('\n') || value(values, '--reason').includes('\r')) {
716
- operationInvalid('--reason must be a single line');
717
- }
716
+ if (values['--auto'] !== true)
717
+ operationInvalid('--auto is required for recover-started');
718
+ return { family, request: input };
718
719
  }
719
- else if (values['--stage'] !== undefined || values['--round'] !== undefined || values['--artifact'] !== undefined) {
720
- operationInvalid('--stage, --round, and --artifact are only supported for recover-started');
720
+ else if (values['--auto'] !== undefined) {
721
+ operationInvalid('--auto is only supported for recover-started');
721
722
  }
722
723
  return { family, request: input };
723
724
  }
@@ -820,9 +821,6 @@ export function parseTaskControlOperation(family, args) {
820
821
  input.message = value(values, '--message');
821
822
  input.recoverable = recoverable === 'true';
822
823
  }
823
- const options = value(values, '--git-worktree-root') === undefined
824
- ? undefined
825
- : { gitWorktreeRoot: value(values, '--git-worktree-root') };
826
- return { family, taskRef, intent: parsedIntent, input, ...(options ? { options } : {}) };
824
+ return { family, taskRef, intent: parsedIntent, input };
827
825
  }
828
826
  //# sourceMappingURL=control-authority.js.map