@fitlab-ai/agent-infra 0.9.8 → 0.9.10

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 (182) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/bin/internal-cli.js +11 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +58 -21
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +25 -11
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +173 -10
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +127 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +63 -92
  10. package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
  11. package/dist/lib/agent-clients/adapters/index.js +3 -1
  12. package/dist/lib/agent-clients/adapters/traecli.js +66 -0
  13. package/dist/lib/agent-clients/tokens.js +2 -2
  14. package/dist/lib/agent-clients/types.js +2 -1
  15. package/dist/lib/defaults.json +5 -0
  16. package/dist/lib/git/workflow.js +10 -1
  17. package/dist/lib/internal/codex-lifecycle.js +20 -6
  18. package/dist/lib/internal/codex-sandbox-controller.js +9 -15
  19. package/dist/lib/internal/git-workflow.js +5 -7
  20. package/dist/lib/internal/platform-pr.js +20 -7
  21. package/dist/lib/internal/release-workflow.js +2 -1
  22. package/dist/lib/internal/sandbox-control.js +79 -2
  23. package/dist/lib/internal/task-finalization.js +54 -0
  24. package/dist/lib/internal/task-orchestration.js +156 -291
  25. package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +75 -42
  26. package/dist/lib/platform/pr-summary.js +48 -10
  27. package/dist/lib/platform/pull-requests.js +205 -41
  28. package/dist/lib/run/tui.js +5 -2
  29. package/dist/lib/sandbox/commands/rm.js +316 -55
  30. package/dist/lib/sandbox/control/client.js +190 -6
  31. package/dist/lib/sandbox/control/controller-registration.js +275 -0
  32. package/dist/lib/sandbox/control/executor.js +140 -5
  33. package/dist/lib/sandbox/control/lifecycle.js +3 -1
  34. package/dist/lib/sandbox/control/protocol.js +67 -6
  35. package/dist/lib/sandbox/index.js +5 -3
  36. package/dist/lib/sandbox/managed-fs.js +5 -0
  37. package/dist/lib/sandbox/task-resolver.js +50 -6
  38. package/dist/lib/sandbox/workspace-identity.js +27 -0
  39. package/dist/lib/sandbox/worktree-safety.js +215 -2
  40. package/dist/lib/task/codex-orchestration.js +161 -39
  41. package/dist/lib/task/commands/status.js +59 -1
  42. package/dist/lib/task/commit-operation.js +335 -0
  43. package/dist/lib/task/commit-policy.js +18 -0
  44. package/dist/lib/task/delegation-receipts.js +248 -6
  45. package/dist/lib/task/finalization.js +593 -0
  46. package/dist/lib/task/index.js +0 -9
  47. package/dist/lib/task/operation-outcome.js +33 -0
  48. package/dist/lib/task/orchestration.js +187 -761
  49. package/dist/lib/task/short-id.js +59 -1
  50. package/dist/lib/task/task-execution-lock.js +6 -1
  51. package/dist/lib/task/verification-engine.js +110 -60
  52. package/dist/lib/task/verification.js +2 -1
  53. package/dist/lib/task/workflow-warning-intents.js +50 -1
  54. package/dist/lifecycle-build-manifest.json +3 -3
  55. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +61 -19
  56. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +37 -10
  57. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +276 -12
  58. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +163 -0
  59. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
  60. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
  61. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +91 -118
  62. package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
  63. package/lib/agent-clients/adapters/index.ts +3 -1
  64. package/lib/agent-clients/adapters/traecli.ts +67 -0
  65. package/lib/agent-clients/tokens.ts +2 -2
  66. package/lib/agent-clients/types.ts +2 -1
  67. package/lib/defaults.json +5 -0
  68. package/lib/git/workflow.ts +11 -2
  69. package/lib/internal/codex-lifecycle.ts +19 -6
  70. package/lib/internal/codex-sandbox-controller.ts +9 -14
  71. package/lib/internal/git-workflow.ts +5 -6
  72. package/lib/internal/platform-pr.ts +14 -7
  73. package/lib/internal/release-workflow.ts +2 -1
  74. package/lib/internal/sandbox-control.ts +92 -2
  75. package/lib/internal/task-finalization.ts +68 -0
  76. package/lib/internal/task-orchestration.ts +20 -103
  77. package/lib/{task/commands/validate.ts → internal/task-validate.ts} +75 -40
  78. package/lib/platform/pr-summary.ts +55 -11
  79. package/lib/platform/pull-requests.ts +211 -41
  80. package/lib/run/tui.ts +5 -3
  81. package/lib/sandbox/commands/rm.ts +413 -61
  82. package/lib/sandbox/control/client.ts +255 -6
  83. package/lib/sandbox/control/controller-registration.ts +352 -0
  84. package/lib/sandbox/control/executor.ts +169 -5
  85. package/lib/sandbox/control/lifecycle.ts +2 -1
  86. package/lib/sandbox/control/protocol.ts +80 -7
  87. package/lib/sandbox/index.ts +5 -3
  88. package/lib/sandbox/managed-fs.ts +5 -0
  89. package/lib/sandbox/task-resolver.ts +58 -7
  90. package/lib/sandbox/workspace-identity.ts +50 -2
  91. package/lib/sandbox/workspace-view.ts +3 -3
  92. package/lib/sandbox/worktree-safety.ts +236 -2
  93. package/lib/task/codex-orchestration.ts +168 -40
  94. package/lib/task/commands/status.ts +78 -1
  95. package/lib/task/commit-operation.ts +430 -0
  96. package/lib/task/commit-policy.ts +30 -0
  97. package/lib/task/delegation-receipts.ts +263 -4
  98. package/lib/task/finalization.ts +761 -0
  99. package/lib/task/index.ts +0 -8
  100. package/lib/task/operation-outcome.ts +48 -0
  101. package/lib/task/orchestration.ts +218 -982
  102. package/lib/task/short-id.ts +65 -1
  103. package/lib/task/task-execution-lock.ts +10 -1
  104. package/lib/task/verification-engine.ts +115 -63
  105. package/lib/task/verification.ts +3 -2
  106. package/lib/task/workflow-warning-intents.ts +57 -2
  107. package/package.json +2 -2
  108. package/templates/.agents/QUICKSTART.en.md +3 -4
  109. package/templates/.agents/QUICKSTART.zh-CN.md +3 -4
  110. package/templates/.agents/hooks/lifecycle-delegation.js +1 -21
  111. package/templates/.agents/rules/README.en.md +1 -0
  112. package/templates/.agents/rules/README.zh-CN.md +1 -0
  113. package/templates/.agents/rules/compatibility-policy.en.md +39 -0
  114. package/templates/.agents/rules/compatibility-policy.zh-CN.md +39 -0
  115. package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
  116. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
  117. package/templates/.agents/rules/pr-sync.en.md +1 -1
  118. package/templates/.agents/rules/pr-sync.github.en.md +2 -1
  119. package/templates/.agents/rules/pr-sync.github.zh-CN.md +2 -0
  120. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -0
  121. package/templates/.agents/rules/review-method.en.md +1 -0
  122. package/templates/.agents/rules/review-method.zh-CN.md +1 -0
  123. package/templates/.agents/rules/task-management.en.md +2 -2
  124. package/templates/.agents/rules/task-management.zh-CN.md +2 -2
  125. package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -0
  126. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -0
  127. package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
  128. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
  129. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  130. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  131. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
  132. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
  133. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
  134. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
  135. package/templates/.agents/skills/code-task/SKILL.en.md +1 -0
  136. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -0
  137. package/templates/.agents/skills/commit/SKILL.en.md +41 -125
  138. package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
  139. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  140. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  141. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
  142. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
  143. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  144. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  145. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
  146. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
  147. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
  148. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
  149. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  150. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  151. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  152. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  153. package/templates/.agents/skills/create-pr/SKILL.en.md +17 -9
  154. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +17 -9
  155. package/templates/.agents/skills/create-pr/reference/change-report.en.md +41 -0
  156. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +41 -0
  157. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
  158. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
  159. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -1
  160. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -1
  161. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +8 -6
  162. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +8 -6
  163. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +3 -3
  164. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +3 -3
  165. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
  166. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
  167. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
  168. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
  169. package/templates/.agents/skills/run-task/SKILL.en.md +6 -6
  170. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -6
  171. package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
  172. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
  173. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +24 -0
  174. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
  175. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
  176. package/templates/.codex/hooks.json +4 -4
  177. package/templates/.trae/skills/update-agent-infra.en.md +8 -0
  178. package/templates/.trae/skills/update-agent-infra.zh-CN.md +8 -0
  179. package/dist/lib/task/commit-finalization.js +0 -305
  180. package/dist/lib/task/commit-intent.js +0 -165
  181. package/lib/task/commit-finalization.ts +0 -419
  182. package/lib/task/commit-intent.ts +0 -230
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runCodexSandboxController,
3
- verifyCodexSandboxControllerContext
3
+ verifyCodexSandboxControllerContextWithWarnings
4
4
  } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
5
5
 
6
6
  const USAGE = 'Usage: agent-infra-internal codex-sandbox-controller <run|verify-context> [options]\n';
@@ -26,8 +26,6 @@ function parse(args: string[]): Readonly<{ operation: string; values: Readonly<R
26
26
  return null;
27
27
  }
28
28
  const allowed = new Set([
29
- '--task-id',
30
- '--task-ref',
31
29
  '--context',
32
30
  '--executor-model',
33
31
  '--executor-reasoning-effort',
@@ -37,6 +35,11 @@ function parse(args: string[]): Readonly<{ operation: string; values: Readonly<R
37
35
  const values: Record<string, string> = {};
38
36
  for (let index = 1; index < args.length; index += 1) {
39
37
  const flag = args[index]!;
38
+ if (flag === '--task-id' || flag.startsWith('--task-id=')
39
+ || flag === '--task-ref' || flag.startsWith('--task-ref=')) {
40
+ fail('CODEX_SANDBOX_CONTROLLER_IDENTITY_OPTION_UNSUPPORTED', 'controller task identity options are not supported', 2);
41
+ return null;
42
+ }
40
43
  const value = args[++index];
41
44
  if (!allowed.has(flag) || Object.hasOwn(values, flag) || !value || value.startsWith('--')) {
42
45
  fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', `invalid option '${flag}'`, 2);
@@ -50,17 +53,13 @@ function parse(args: string[]): Readonly<{ operation: string; values: Readonly<R
50
53
  async function codexSandboxController(args: string[] = []): Promise<void> {
51
54
  const parsed = parse(args);
52
55
  if (!parsed || process.exitCode) return;
53
- const taskId = parsed.values['--task-id'];
54
- if (!taskId) {
55
- fail('CODEX_SANDBOX_CONTROLLER_PAYLOAD_INVALID', '--task-id is required', 2);
56
- return;
57
- }
58
56
  try {
59
57
  if (parsed.operation === 'verify-context') {
60
58
  const contextPath = parsed.values['--context']
61
59
  ?? process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
62
60
  if (!contextPath) throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_MISSING');
63
- const context = verifyCodexSandboxControllerContext(contextPath, taskId);
61
+ const verified = verifyCodexSandboxControllerContextWithWarnings(contextPath);
62
+ const context = verified.context;
64
63
  process.stdout.write(`${JSON.stringify({
65
64
  status: 'ready',
66
65
  changed: false,
@@ -71,14 +70,12 @@ async function codexSandboxController(args: string[] = []): Promise<void> {
71
70
  expiresAt: context.expiresAt,
72
71
  buildIdentity: context.buildIdentity,
73
72
  hookDefinitionHash: context.hookDefinitionHash,
74
- lifecycleProfilesHash: context.lifecycleProfilesHash
75
73
  },
74
+ ...(verified.warnings.length > 0 ? { warnings: verified.warnings } : {}),
76
75
  error: null
77
76
  })}\n`);
78
77
  return;
79
78
  }
80
- const taskRef = parsed.values['--task-ref'];
81
- if (!taskRef) throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_REF_REQUIRED');
82
79
  const policy = [
83
80
  '--executor-model',
84
81
  '--executor-reasoning-effort',
@@ -90,8 +87,6 @@ async function codexSandboxController(args: string[] = []): Promise<void> {
90
87
  throw new Error('CODEX_SANDBOX_CONTROLLER_MODEL_POLICY_INVALID');
91
88
  }
92
89
  const exitCode = await runCodexSandboxController({
93
- taskId,
94
- taskRef,
95
90
  executorModel: parsed.values['--executor-model'],
96
91
  executorReasoningEffort: parsed.values['--executor-reasoning-effort'],
97
92
  reviewerModel: parsed.values['--reviewer-model'],
@@ -1,7 +1,8 @@
1
1
  import fs from 'node:fs';
2
- import { commitExplicitPaths, inspectGitWorkflow, pushGitRefs, pushRebasedBranch } from '../git/workflow.ts';
2
+ import { inspectGitWorkflow, pushRebasedBranch } from '../git/workflow.ts';
3
3
  import { compareReviewTrees, snapshotReview } from '../git/review-snapshot.ts';
4
4
  import { resolvePostReviewGlobs } from '../task/review-fingerprint.ts';
5
+ import { executeCommitOperation } from '../task/commit-operation.ts';
5
6
 
6
7
  function output(value: unknown): void { process.stdout.write(`${JSON.stringify(value)}\n`); }
7
8
 
@@ -18,11 +19,9 @@ function gitWorkflow(args: string[] = []): void {
18
19
  else if (action === 'commit') {
19
20
  const inputIndex = rest.indexOf('--input');
20
21
  const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1]!, 'utf8')) : null;
21
- result = input ? commitExplicitPaths({ cwd: cwd ?? process.cwd(), ...input }) : { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
22
- } else if (action === 'push') {
23
- const inputIndex = rest.indexOf('--input');
24
- const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1]!, 'utf8')) : null;
25
- result = input ? pushGitRefs({ cwd: cwd ?? process.cwd(), ...input }) : { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
22
+ result = input
23
+ ? executeCommitOperation({ cwd: cwd ?? process.cwd(), ...input })
24
+ : { status: 'failed', changed: false, error: { code: 'GIT_INPUT_REQUIRED', message: '--input JSON file is required' } };
26
25
  } else if (action === 'push-rebased') {
27
26
  const inputIndex = rest.indexOf('--input');
28
27
  const input = inputIndex >= 0 && rest[inputIndex + 1] ? JSON.parse(fs.readFileSync(rest[inputIndex + 1]!, 'utf8')) : null;
@@ -17,13 +17,13 @@ const USAGE = `Usage: agent-infra-internal platform-pr inspect <task-ref> [--cwd
17
17
  agent-infra-internal platform-pr resolve-external <task-ref> --agent <agent> [--pr <N>] [--dry-run] [--cwd <path>]
18
18
  agent-infra-internal platform-pr create <task-ref> --agent <agent> --base <branch> --head <branch> --title-file <path|-> --body-file <path|-> [--draft] [--dry-run] [--cwd <path>]
19
19
  agent-infra-internal platform-pr bind <task-ref> --pr <N> --agent <agent> [--dry-run] [--cwd <path>]
20
- agent-infra-internal platform-pr sync <task-ref> --agent <agent> [--metadata] [--closing-issue] [--dry-run] [--cwd <path>]
20
+ agent-infra-internal platform-pr sync <task-ref> --agent <agent> [--metadata] [--closing-issue] --result <pr_created|pr_reused|no_op> [--dry-run] [--cwd <path>]
21
21
  agent-infra-internal platform-pr summary-context <task-ref> [--cwd <path>]
22
- agent-infra-internal platform-pr summary-sync <task-ref> --agent <agent> --body-file <path|-> [--dry-run] [--cwd <path>]
22
+ agent-infra-internal platform-pr summary-sync <task-ref> --agent <agent> --body-file <path|-> --result <pr_created|pr_reused|no_op> [--dry-run] [--cwd <path>]
23
23
  `;
24
24
 
25
25
  const BOOLEAN_FLAGS = new Set(['--draft', '--dry-run', '--metadata', '--closing-issue']);
26
- const VALUE_FLAGS = new Set(['--cwd', '--agent', '--base', '--head', '--title-file', '--body-file', '--pr']);
26
+ const VALUE_FLAGS = new Set(['--cwd', '--agent', '--base', '--head', '--title-file', '--body-file', '--pr', '--result']);
27
27
 
28
28
  function key(flag: string): string {
29
29
  return flag.slice(2).replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
@@ -76,9 +76,9 @@ function platformPr(args: string[] = []): void {
76
76
  'resolve-external': ['cwd', 'agent', 'pr', 'dryRun'],
77
77
  create: ['cwd', 'agent', 'base', 'head', 'titleFile', 'bodyFile', 'draft', 'dryRun'],
78
78
  bind: ['cwd', 'agent', 'pr', 'dryRun'],
79
- sync: ['cwd', 'agent', 'metadata', 'closingIssue', 'dryRun'],
79
+ sync: ['cwd', 'agent', 'metadata', 'closingIssue', 'result', 'dryRun'],
80
80
  'summary-context': ['cwd'],
81
- 'summary-sync': ['cwd', 'agent', 'bodyFile', 'dryRun']
81
+ 'summary-sync': ['cwd', 'agent', 'bodyFile', 'result', 'dryRun']
82
82
  };
83
83
  const unexpected = Object.keys(values).find((name) => !allowed[operation]!.includes(name));
84
84
  if (unexpected) { fail(`${operation} does not accept --${unexpected}`); return; }
@@ -88,6 +88,11 @@ function platformPr(args: string[] = []): void {
88
88
  const agent = normalizeAgentToken(values.agent);
89
89
  if (!agent) { fail(`invalid --agent '${values.agent}': ${AGENT_USAGE_HINT}`); return; }
90
90
  values.agent = agent;
91
+ const primaryResult = values.result === undefined ? undefined : values.result;
92
+ if (primaryResult !== undefined && !['pr_created', 'pr_reused', 'no_op'].includes(primaryResult as string)) {
93
+ fail('--result must be pr_created, pr_reused, or no_op');
94
+ return;
95
+ }
91
96
  if (operation === 'resolve-external') {
92
97
  const pr = values.pr === undefined ? undefined : Number(values.pr);
93
98
  if (pr !== undefined && (!Number.isInteger(pr) || pr <= 0)) { fail('resolve-external requires a positive --pr'); return; }
@@ -102,13 +107,15 @@ function platformPr(args: string[] = []): void {
102
107
  }
103
108
  if (operation === 'sync') {
104
109
  if (values.metadata !== true && values.closingIssue !== true) { fail('sync requires --metadata or --closing-issue'); return; }
105
- finish(syncPlatformPullRequest(taskRef, { cwd, agent: values.agent, metadata: values.metadata === true, closingIssue: values.closingIssue === true, dryRun: values.dryRun === true }));
110
+ if (!primaryResult) { fail('sync requires --result pr_created, pr_reused, or no_op'); return; }
111
+ finish(syncPlatformPullRequest(taskRef, { cwd, agent: values.agent, metadata: values.metadata === true, closingIssue: values.closingIssue === true, primaryResult: primaryResult as 'pr_created' | 'pr_reused' | 'no_op', dryRun: values.dryRun === true }));
106
112
  return;
107
113
  }
108
114
  if (operation === 'summary-sync') {
109
115
  if (typeof values.bodyFile !== 'string') { fail('summary-sync requires --body-file'); return; }
116
+ if (!primaryResult) { fail('summary-sync requires --result pr_created, pr_reused, or no_op'); return; }
110
117
  try {
111
- finish(syncPullRequestSummary(taskRef, { cwd, agent: values.agent, body: readFile(values.bodyFile, cwd), dryRun: values.dryRun === true }));
118
+ finish(syncPullRequestSummary(taskRef, { cwd, agent: values.agent, body: readFile(values.bodyFile, cwd), primaryResult: primaryResult as 'pr_created' | 'pr_reused' | 'no_op', dryRun: values.dryRun === true }));
112
119
  } catch (error) {
113
120
  fail(`unable to read body file: ${error instanceof Error ? error.message : String(error)}`);
114
121
  }
@@ -25,12 +25,13 @@ type DemoResult = {
25
25
  outputPath: string | null;
26
26
  };
27
27
 
28
+ // The tape is the canonical init interaction contract. Do not hash all of
29
+ // lib/init.ts: non-visual config serialization changes must not re-record it.
28
30
  const DEMO_INPUT_PATHS = [
29
31
  'assets/demo-init.tape',
30
32
  'scripts/demo-regen.sh',
31
33
  'scripts/normalize-gif-duration.py',
32
34
  'bin/cli.ts',
33
- 'lib/init.ts',
34
35
  'lib/log.ts',
35
36
  'lib/prompt.ts',
36
37
  'lib/paths.ts',
@@ -1,6 +1,84 @@
1
- import { requestSandboxControl, SandboxControlClientError } from '../sandbox/control/client.ts';
1
+ import {
2
+ requestSandboxControl,
3
+ requestSandboxTaskFinalization,
4
+ requestSandboxTaskControl,
5
+ SandboxControlClientError
6
+ } from '../sandbox/control/client.ts';
7
+ import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
2
8
  import { serveSandboxControl } from '../sandbox/control/server.ts';
3
9
  import { runSandboxControlExecutor } from '../sandbox/control/executor.ts';
10
+ import {
11
+ controllerProofFromContext,
12
+ verifyCodexSandboxControllerContextWithWarnings
13
+ } from '../agent-clients/adapters/codex-lifecycle/sandbox-controller.ts';
14
+
15
+ function isCanonicalCodexPrepare(args: readonly string[]): boolean {
16
+ if (args[1] !== 'prepare') return false;
17
+ const clients: string[] = [];
18
+ for (let index = 2; index < args.length; index += 1) {
19
+ if (args[index] === '--client') clients.push(args[index + 1] ?? '');
20
+ else if (args[index]?.startsWith('--client=')) clients.push(args[index]!.slice('--client='.length));
21
+ }
22
+ return clients.length === 1 && clients[0] === 'codex';
23
+ }
24
+
25
+ type FinalizationStatus = 'completed' | 'failed' | 'blocked' | 'unknown';
26
+
27
+ function writeFinalizationEnvelope(
28
+ status: FinalizationStatus,
29
+ accepted: boolean,
30
+ error: { code: string; message: string; retryable: boolean },
31
+ changed = false,
32
+ result: unknown = null
33
+ ): void {
34
+ process.stdout.write(`${JSON.stringify({ version: 1, status, changed, accepted, result, error })}\n`);
35
+ }
36
+
37
+ function finalizationErrorStatus(error: { retryable: boolean; code: string }): FinalizationStatus {
38
+ if (error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN') return 'unknown';
39
+ return error.retryable ? 'blocked' : 'failed';
40
+ }
41
+
42
+ function sandboxFinalizationClient(args: string[]): void {
43
+ if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
44
+ const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: 'task ref, complete intent, and --agent are required', retryable: false };
45
+ writeFinalizationEnvelope('failed', false, error);
46
+ process.stderr.write('Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n');
47
+ process.exitCode = 1;
48
+ return;
49
+ }
50
+ const agent = normalizeAgentToken(args[3]);
51
+ if (!agent) {
52
+ const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message: `invalid --agent: ${AGENT_USAGE_HINT}`, retryable: false };
53
+ writeFinalizationEnvelope('failed', false, error);
54
+ process.exitCode = 1;
55
+ return;
56
+ }
57
+ let response;
58
+ try {
59
+ response = requestSandboxTaskFinalization({ agent });
60
+ } catch (error) {
61
+ if (!(error instanceof SandboxControlClientError)) throw error;
62
+ writeFinalizationEnvelope(finalizationErrorStatus(error.detail), error.accepted, error.detail);
63
+ process.stderr.write(`${error.detail.message}\n`);
64
+ process.exitCode = error.detail.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.detail.retryable ? 2 : 1;
65
+ return;
66
+ }
67
+ if (response.phase === 'rejected') {
68
+ const error = response.error ?? {
69
+ code: 'SANDBOX_CONTROL_REJECTED',
70
+ message: 'sandbox control rejected the finalization request',
71
+ retryable: false
72
+ };
73
+ writeFinalizationEnvelope(finalizationErrorStatus(error), error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN', error);
74
+ process.stderr.write(`${error.message}\n`);
75
+ process.exitCode = error.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN' ? 1 : error.retryable ? 2 : 1;
76
+ return;
77
+ }
78
+ process.stdout.write(response.stdout);
79
+ process.stderr.write(response.stderr);
80
+ process.exitCode = response.exitCode ?? 1;
81
+ }
4
82
 
5
83
  async function sandboxControl(args: string[]): Promise<void> {
6
84
  const [operation, ...rest] = args;
@@ -31,9 +109,21 @@ async function sandboxControl(args: string[]): Promise<void> {
31
109
  }
32
110
  if (operation === 'client') {
33
111
  const [family = '', ...commandArgs] = rest;
112
+ if (family === 'task-finalization') {
113
+ sandboxFinalizationClient(commandArgs);
114
+ return;
115
+ }
34
116
  let response;
35
117
  try {
36
- response = requestSandboxControl({ family, args: commandArgs });
118
+ if (family === 'task-orchestration' && isCanonicalCodexPrepare(commandArgs)) {
119
+ const contextPath = process.env.AGENT_INFRA_CODEX_CONTROLLER_CONTEXT;
120
+ const proof = contextPath
121
+ ? controllerProofFromContext(verifyCodexSandboxControllerContextWithWarnings(contextPath).context)
122
+ : null;
123
+ response = requestSandboxTaskControl({ family, args: commandArgs, controllerProof: proof });
124
+ } else {
125
+ response = requestSandboxControl({ family, args: commandArgs });
126
+ }
37
127
  } catch (error) {
38
128
  if (!(error instanceof SandboxControlClientError)) throw error;
39
129
  process.stderr.write(`${error.detail.message}\n`);
@@ -0,0 +1,68 @@
1
+ import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
2
+ import { applyTaskFinalization } from '../task/finalization.ts';
3
+ import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.ts';
4
+ import { verifyTaskEvent } from '../task/verification.ts';
5
+
6
+ const USAGE = 'Usage: agent-infra-internal task-finalization <N | TASK-id> complete --agent <agent>\n';
7
+
8
+ type FinalizationError = Readonly<{ code: string; message: string; retryable: boolean }>;
9
+
10
+ function envelope(
11
+ status: 'completed' | 'failed' | 'blocked' | 'unknown',
12
+ changed: boolean,
13
+ accepted: boolean,
14
+ result: ReturnType<typeof applyTaskFinalization> | null,
15
+ error: FinalizationError | null
16
+ ): string {
17
+ return `${JSON.stringify({ version: 1, status, changed, accepted, result, error })}\n`;
18
+ }
19
+
20
+ function exitCode(status: 'completed' | 'failed' | 'blocked' | 'unknown'): number {
21
+ return status === 'completed' ? 0 : status === 'blocked' ? 2 : 1;
22
+ }
23
+
24
+ function fail(message: string): void {
25
+ const error = { code: 'TASK_FINALIZATION_PAYLOAD_INVALID', message, retryable: false };
26
+ process.stdout.write(envelope('failed', false, false, null, error));
27
+ process.stderr.write(USAGE);
28
+ process.exitCode = 1;
29
+ }
30
+
31
+ function taskFinalization(args: string[] = []): void {
32
+ if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(USAGE); return; }
33
+ if (args.length !== 4 || !args[0] || args[1] !== 'complete' || args[2] !== '--agent' || !args[3]) {
34
+ fail('task ref, complete intent, and --agent are required');
35
+ return;
36
+ }
37
+ const agent = normalizeAgentToken(args[3]);
38
+ if (!agent) {
39
+ fail(`invalid --agent: ${AGENT_USAGE_HINT}`);
40
+ return;
41
+ }
42
+ let repoRoot: string;
43
+ try {
44
+ repoRoot = detectRepoRoot();
45
+ } catch (error) {
46
+ const detail = { code: 'REPO_ROOT_NOT_FOUND', message: error instanceof Error ? error.message : String(error), retryable: false };
47
+ process.stdout.write(envelope('failed', false, true, null, detail));
48
+ process.exitCode = 1;
49
+ return;
50
+ }
51
+ const resolved = resolveTaskRef(args[0], { repoRoot });
52
+ if (!resolved.ok) {
53
+ const detail = { code: resolved.code, message: resolved.message, retryable: false };
54
+ process.stdout.write(envelope('failed', false, true, null, detail));
55
+ process.exitCode = 1;
56
+ return;
57
+ }
58
+ const result = applyTaskFinalization(
59
+ { taskRef: resolved.taskId, intent: 'complete', agent },
60
+ { repoRoot, preflight: (request, options) => verifyTaskEvent(
61
+ { ...request, event: 'complete-task.hard-preflight' }, options
62
+ ) }
63
+ );
64
+ process.stdout.write(envelope(result.status, result.changed, true, result, result.error));
65
+ process.exitCode = exitCode(result.status);
66
+ }
67
+
68
+ export { taskFinalization };
@@ -1,31 +1,23 @@
1
- import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.ts';
2
1
  import {
3
- abortCommitIntent,
4
2
  activateMatchingOrchestrationDelegation,
5
3
  activateOrchestrationDelegation,
6
4
  advanceOrchestration,
7
5
  awaitOrchestrationDelegationActivation,
8
- beginCommitIntent,
9
6
  beginOrResumeOrchestration,
10
- checkpointCommitIntent,
11
- completeCommitIntent,
12
7
  dispatchOrchestrationDelegation,
8
+ OrchestrationStateError,
13
9
  pauseOrchestration,
14
- recoverCommitIntent,
15
10
  recoverPreparedOrchestrationDelegation,
16
11
  routeOrchestration,
17
12
  sealMatchingOrchestrationDelegation,
18
13
  sealOrchestrationDelegation,
19
- startCommitAttempt,
20
- statusCommitIntent,
21
- statusOrchestration,
22
- terminateCommitAttempt
14
+ statusOrchestration
23
15
  } from '../task/orchestration.ts';
24
16
  import { prepareCodexOrchestrationDelegation } from '../task/codex-orchestration.ts';
25
17
  import { isAgentClientId } from '../agent-clients/types.ts';
26
18
  import type { AgentClientId } from '../agent-clients/types.ts';
27
19
 
28
- const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|dispatch|await-activation|recover-prepared|hook-start|hook-stop|advance|pause|status|commit-start|commit-begin|commit-checkpoint|commit-complete|commit-recover|commit-abort|commit-terminate|commit-status> [options]\n';
20
+ const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|dispatch|await-activation|recover-prepared|hook-start|hook-stop|advance|pause|status> [options]\n';
29
21
 
30
22
  function usageFailure(message: string): void {
31
23
  process.stdout.write(`${JSON.stringify({
@@ -48,23 +40,15 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
48
40
  const [taskRef, intent] = args;
49
41
  if (![
50
42
  'begin-or-resume', 'route', 'prepare', 'dispatch', 'await-activation', 'recover-prepared',
51
- 'hook-start', 'hook-stop', 'advance', 'pause', 'status',
52
- 'commit-start', 'commit-begin', 'commit-checkpoint', 'commit-complete', 'commit-recover', 'commit-abort', 'commit-terminate', 'commit-status'
43
+ 'hook-start', 'hook-stop', 'advance', 'pause', 'status'
53
44
  ].includes(intent!)) {
54
45
  usageFailure(`unknown intent '${intent}'`);
55
46
  return;
56
47
  }
57
48
  const values: Record<string, string> = {};
58
49
  const seen = new Set<string>();
59
- let orchestrated = false;
60
50
  for (let index = 2; index < args.length; index += 1) {
61
51
  const flag = args[index]!;
62
- if (flag === '--orchestrated') {
63
- if (seen.has(flag)) { usageFailure(`duplicate option '${flag}'`); return; }
64
- seen.add(flag);
65
- orchestrated = true;
66
- continue;
67
- }
68
52
  if (![
69
53
  '--max-steps', '--executor-model', '--executor-reasoning-effort',
70
54
  '--reviewer-model', '--reviewer-reasoning-effort',
@@ -74,8 +58,7 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
74
58
  '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
75
59
  '--model-fallback-reason', '--reasoning-effort-fallback-reason',
76
60
  '--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent',
77
- '--baseline-head', '--token', '--kind', '--head', '--remote', '--ref', '--expected-head'
78
- , '--attempt', '--git-worktree-root'
61
+ '--git-worktree-root'
79
62
  ].includes(flag)) {
80
63
  usageFailure(`unknown option '${flag}'`);
81
64
  return;
@@ -92,26 +75,6 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
92
75
  }
93
76
  values[flag] = value;
94
77
  }
95
- const commitIntentFlags: Record<string, readonly string[]> = {
96
- 'commit-start': ['--agent', '--git-worktree-root'],
97
- 'commit-begin': ['--agent', '--baseline-head', '--attempt', '--orchestrated', '--git-worktree-root'],
98
- 'commit-checkpoint': ['--token', '--kind', '--head', '--remote', '--ref', '--git-worktree-root'],
99
- 'commit-complete': ['--token', '--agent', '--git-worktree-root'],
100
- 'commit-recover': ['--agent', '--git-worktree-root'],
101
- 'commit-abort': ['--token', '--expected-head', '--git-worktree-root'],
102
- 'commit-terminate': ['--attempt', '--agent', '--code', '--git-worktree-root'],
103
- 'commit-status': ['--git-worktree-root']
104
- };
105
- if (intent! in commitIntentFlags) {
106
- const allowed = commitIntentFlags[intent!]!;
107
- const invalid = [...seen].find((flag) => !allowed.includes(flag));
108
- if (invalid) { usageFailure(`intent '${intent}' does not accept '${invalid}'`); return; }
109
- } else {
110
- const commitOnly = [...seen].find((flag) => [
111
- '--orchestrated', '--baseline-head', '--attempt', '--token', '--kind', '--head', '--remote', '--ref', '--expected-head'
112
- ].includes(flag));
113
- if (commitOnly) { usageFailure(`intent '${intent}' does not accept '${commitOnly}'`); return; }
114
- }
115
78
  const requireValues = (flags: string[]) => flags.find((flag) => values[flag] === undefined);
116
79
  const coreOptions = values['--git-worktree-root'] === undefined
117
80
  ? {}
@@ -121,66 +84,8 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
121
84
  return;
122
85
  }
123
86
  let result;
124
- if (intent === 'commit-start') {
125
- const missing = requireValues(['--agent']);
126
- if (missing) { usageFailure(`intent 'commit-start' requires '${missing}'`); return; }
127
- const agent = normalizeAgentToken(values['--agent']!);
128
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
129
- result = startCommitAttempt(taskRef!, { agent }, coreOptions);
130
- } else if (intent === 'commit-begin') {
131
- const missing = requireValues(['--agent', '--baseline-head', '--attempt']);
132
- if (missing) { usageFailure(`intent 'commit-begin' requires '${missing}'`); return; }
133
- const agent = normalizeAgentToken(values['--agent']!);
134
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
135
- result = beginCommitIntent(taskRef!, {
136
- agent, orchestrated, baselineHead: values['--baseline-head']!, attempt: values['--attempt']!
137
- }, coreOptions);
138
- } else if (intent === 'commit-checkpoint') {
139
- const missing = requireValues(['--token', '--kind', '--head']);
140
- if (missing) { usageFailure(`intent 'commit-checkpoint' requires '${missing}'`); return; }
141
- if (!['committed', 'pushed'].includes(values['--kind']!)) {
142
- usageFailure('--kind must be committed or pushed'); return;
143
- }
144
- if (values['--kind'] === 'pushed') {
145
- const pushedMissing = requireValues(['--remote', '--ref']);
146
- if (pushedMissing) { usageFailure(`pushed checkpoint requires '${pushedMissing}'`); return; }
147
- } else if (values['--remote'] !== undefined || values['--ref'] !== undefined) {
148
- usageFailure('committed checkpoint does not accept --remote or --ref'); return;
149
- }
150
- result = checkpointCommitIntent(taskRef!, {
151
- token: values['--token']!, kind: values['--kind'] as 'committed' | 'pushed',
152
- head: values['--head']!, remote: values['--remote'], ref: values['--ref']
153
- }, coreOptions);
154
- } else if (intent === 'commit-complete') {
155
- const missing = requireValues(['--token', '--agent']);
156
- if (missing) { usageFailure(`intent 'commit-complete' requires '${missing}'`); return; }
157
- const agent = normalizeAgentToken(values['--agent']!);
158
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
159
- result = completeCommitIntent(taskRef!, { token: values['--token']!, agent }, coreOptions);
160
- } else if (intent === 'commit-recover') {
161
- const missing = requireValues(['--agent']);
162
- if (missing) { usageFailure(`intent 'commit-recover' requires '${missing}'`); return; }
163
- const agent = normalizeAgentToken(values['--agent']!);
164
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
165
- result = recoverCommitIntent(taskRef!, { agent }, coreOptions);
166
- } else if (intent === 'commit-abort') {
167
- const missing = requireValues(['--token', '--expected-head']);
168
- if (missing) { usageFailure(`intent 'commit-abort' requires '${missing}'`); return; }
169
- result = abortCommitIntent(taskRef!, { token: values['--token']!, expectedHead: values['--expected-head']! }, coreOptions);
170
- } else if (intent === 'commit-terminate') {
171
- const missing = requireValues(['--attempt', '--agent', '--code']);
172
- if (missing) { usageFailure(`intent 'commit-terminate' requires '${missing}'`); return; }
173
- const agent = normalizeAgentToken(values['--agent']!);
174
- if (!agent) { usageFailure(`invalid --agent '${values['--agent']}': ${AGENT_USAGE_HINT}`); return; }
175
- if (!/^[A-Z][A-Z0-9_]{2,63}$/.test(values['--code']!)) {
176
- usageFailure('--code must be a stable uppercase identifier'); return;
177
- }
178
- result = terminateCommitAttempt(taskRef!, {
179
- attempt: values['--attempt']!, agent, code: values['--code']!
180
- }, coreOptions);
181
- } else if (intent === 'commit-status') {
182
- result = statusCommitIntent(taskRef!, coreOptions);
183
- } else if (intent === 'begin-or-resume') {
87
+ try {
88
+ if (intent === 'begin-or-resume') {
184
89
  const missing = requireValues(['--client']);
185
90
  if (missing) { usageFailure(`intent 'begin-or-resume' requires '${missing}'`); return; }
186
91
  const maxSteps = values['--max-steps'] === undefined ? undefined : Number(values['--max-steps']);
@@ -295,7 +200,19 @@ async function taskOrchestration(args: string[] = []): Promise<void> {
295
200
  if (!['true', 'false'].includes(values['--recoverable']!)) { usageFailure('--recoverable must be true or false'); return; }
296
201
  result = pauseOrchestration(taskRef!, values['--code']!, values['--message']!, values['--recoverable'] === 'true', coreOptions);
297
202
  }
298
- process.stdout.write(`${JSON.stringify(result)}\n`);
203
+ } catch (error) {
204
+ if (!(error instanceof OrchestrationStateError)) throw error;
205
+ result = {
206
+ status: 'failed' as const,
207
+ changed: false,
208
+ taskId: error.taskId,
209
+ run: null,
210
+ next: null,
211
+ error: { code: error.code, message: error.message }
212
+ };
213
+ }
214
+ const output = result;
215
+ process.stdout.write(`${JSON.stringify(output)}\n`);
299
216
  if (result.status === 'failed') process.exitCode = 1;
300
217
  }
301
218