@fitlab-ai/agent-infra 0.9.5 → 0.9.7

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 (172) hide show
  1. package/dist/bin/internal-cli.js +8 -3
  2. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +171 -20
  3. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +162 -0
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +244 -0
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -2
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +370 -0
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +68 -8
  8. package/dist/lib/agent-clients/adapters/codex.js +1 -1
  9. package/dist/lib/defaults.json +10 -2
  10. package/dist/lib/git/workflow.js +30 -4
  11. package/dist/lib/internal/codex-lifecycle.js +169 -3
  12. package/dist/lib/internal/codex-sandbox-controller.js +106 -0
  13. package/dist/lib/internal/release-workflow.js +108 -11
  14. package/dist/lib/internal/sandbox-control.js +43 -6
  15. package/dist/lib/internal/task-create.js +12 -4
  16. package/dist/lib/internal/task-orchestration.js +40 -7
  17. package/dist/lib/platform/context.js +6 -4
  18. package/dist/lib/platform/github-client.js +55 -2
  19. package/dist/lib/platform/issue-comments.js +2 -1
  20. package/dist/lib/process-data/checkpoint.js +319 -0
  21. package/dist/lib/process-data/index.js +128 -60
  22. package/dist/lib/process-data/materialize.js +84 -0
  23. package/dist/lib/process-data/normalize.js +34 -1
  24. package/dist/lib/process-data/reconcile.js +55 -1
  25. package/dist/lib/process-data/sources.js +422 -1
  26. package/dist/lib/process-data/store.js +341 -11
  27. package/dist/lib/release/workflow.js +29 -21
  28. package/dist/lib/sandbox/commands/create.js +111 -60
  29. package/dist/lib/sandbox/commands/prune.js +40 -16
  30. package/dist/lib/sandbox/commands/rm.js +372 -94
  31. package/dist/lib/sandbox/commands/start.js +4 -3
  32. package/dist/lib/sandbox/control/client.js +138 -22
  33. package/dist/lib/sandbox/control/container-identity.js +64 -0
  34. package/dist/lib/sandbox/control/executor.js +142 -0
  35. package/dist/lib/sandbox/control/lifecycle.js +557 -0
  36. package/dist/lib/sandbox/control/protocol.js +69 -26
  37. package/dist/lib/sandbox/control/server.js +395 -99
  38. package/dist/lib/sandbox/control/state.js +191 -0
  39. package/dist/lib/sandbox/control/timing.js +8 -0
  40. package/dist/lib/sandbox/index.js +2 -2
  41. package/dist/lib/sandbox/managed-fs.js +14 -2
  42. package/dist/lib/sandbox/mounts.js +22 -19
  43. package/dist/lib/sandbox/recovery.js +284 -50
  44. package/dist/lib/sandbox/workspace-view.js +90 -10
  45. package/dist/lib/sandbox/worktree-safety.js +196 -0
  46. package/dist/lib/server/daemon.js +1 -1
  47. package/dist/lib/server/process-control.js +17 -1
  48. package/dist/lib/server/process-state.js +57 -20
  49. package/dist/lib/task/artifact-lifecycle.js +87 -15
  50. package/dist/lib/task/artifact-receipts.js +128 -0
  51. package/dist/lib/task/codex-orchestration.js +236 -0
  52. package/dist/lib/task/commands/status.js +3 -0
  53. package/dist/lib/task/commands/validate.js +289 -0
  54. package/dist/lib/task/commit-finalization.js +36 -9
  55. package/dist/lib/task/create.js +10 -2
  56. package/dist/lib/task/delegation-receipts.js +109 -2
  57. package/dist/lib/task/events.js +149 -6
  58. package/dist/lib/task/finalization-artifacts.js +2 -1
  59. package/dist/lib/task/implementation-inputs.js +1 -7
  60. package/dist/lib/task/index.js +9 -0
  61. package/dist/lib/task/ledger.js +13 -1
  62. package/dist/lib/task/orchestration.js +365 -15
  63. package/dist/lib/task/sections.js +5 -1
  64. package/dist/lib/task/task-execution-lock.js +5 -4
  65. package/dist/lib/task/verification-engine.js +78 -14
  66. package/dist/lib/task/verification.js +1 -0
  67. package/dist/lifecycle-build-manifest.json +6 -0
  68. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +202 -18
  69. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +227 -0
  70. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +321 -0
  71. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -3
  72. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +26 -0
  73. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +453 -0
  74. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +83 -6
  75. package/lib/agent-clients/adapters/codex.ts +1 -1
  76. package/lib/defaults.json +10 -2
  77. package/lib/git/workflow.ts +25 -3
  78. package/lib/internal/codex-lifecycle.ts +175 -4
  79. package/lib/internal/codex-sandbox-controller.ts +109 -0
  80. package/lib/internal/release-workflow.ts +107 -11
  81. package/lib/internal/sandbox-control.ts +38 -6
  82. package/lib/internal/task-create.ts +16 -4
  83. package/lib/internal/task-orchestration.ts +34 -7
  84. package/lib/platform/context.ts +8 -5
  85. package/lib/platform/github-client.ts +61 -3
  86. package/lib/platform/issue-comments.ts +2 -1
  87. package/lib/process-data/checkpoint.ts +335 -0
  88. package/lib/process-data/index.ts +112 -60
  89. package/lib/process-data/materialize.ts +83 -0
  90. package/lib/process-data/normalize.ts +29 -1
  91. package/lib/process-data/reconcile.ts +64 -2
  92. package/lib/process-data/sources.ts +449 -2
  93. package/lib/process-data/store.ts +304 -11
  94. package/lib/process-data/types.ts +124 -2
  95. package/lib/release/workflow.ts +69 -20
  96. package/lib/sandbox/commands/create.ts +70 -25
  97. package/lib/sandbox/commands/prune.ts +53 -15
  98. package/lib/sandbox/commands/rm.ts +423 -108
  99. package/lib/sandbox/commands/start.ts +4 -3
  100. package/lib/sandbox/control/client.ts +152 -28
  101. package/lib/sandbox/control/container-identity.ts +93 -0
  102. package/lib/sandbox/control/executor.ts +167 -0
  103. package/lib/sandbox/control/lifecycle.ts +573 -0
  104. package/lib/sandbox/control/protocol.ts +121 -68
  105. package/lib/sandbox/control/server.ts +398 -109
  106. package/lib/sandbox/control/state.ts +213 -0
  107. package/lib/sandbox/control/timing.ts +12 -0
  108. package/lib/sandbox/index.ts +2 -2
  109. package/lib/sandbox/managed-fs.ts +17 -2
  110. package/lib/sandbox/mounts.ts +16 -8
  111. package/lib/sandbox/recovery.ts +335 -65
  112. package/lib/sandbox/workspace-view.ts +102 -11
  113. package/lib/sandbox/worktree-safety.ts +234 -0
  114. package/lib/server/daemon.ts +1 -1
  115. package/lib/server/process-control.ts +24 -3
  116. package/lib/server/process-state.ts +82 -27
  117. package/lib/task/artifact-lifecycle.ts +78 -14
  118. package/lib/task/artifact-receipts.ts +161 -0
  119. package/lib/task/codex-orchestration.ts +319 -0
  120. package/lib/task/commands/status.ts +2 -0
  121. package/lib/task/commands/validate.ts +282 -0
  122. package/lib/task/commit-finalization.ts +52 -9
  123. package/lib/task/create.ts +11 -2
  124. package/lib/task/delegation-receipts.ts +208 -7
  125. package/lib/task/events.ts +131 -5
  126. package/lib/task/finalization-artifacts.ts +2 -1
  127. package/lib/task/implementation-inputs.ts +1 -5
  128. package/lib/task/index.ts +8 -0
  129. package/lib/task/ledger.ts +16 -1
  130. package/lib/task/orchestration.ts +552 -18
  131. package/lib/task/sections.ts +6 -2
  132. package/lib/task/task-execution-lock.ts +11 -9
  133. package/lib/task/verification-engine.ts +93 -24
  134. package/lib/task/verification.ts +2 -1
  135. package/package.json +1 -1
  136. package/templates/.agents/README.en.md +7 -0
  137. package/templates/.agents/README.zh-CN.md +5 -0
  138. package/templates/.agents/hooks/lifecycle-delegation.js +72 -12
  139. package/templates/.agents/rules/lifecycle-orchestration.en.md +8 -2
  140. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +8 -2
  141. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -0
  142. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -0
  143. package/templates/.agents/rules/pr-checks-commands.github.en.md +1 -1
  144. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +1 -1
  145. package/templates/.agents/rules/review-handshake.en.md +3 -1
  146. package/templates/.agents/rules/review-handshake.zh-CN.md +3 -1
  147. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +3 -1
  148. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +3 -1
  149. package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
  150. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
  151. package/templates/.agents/skills/post-release/SKILL.en.md +32 -6
  152. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +32 -6
  153. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +42 -0
  154. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +42 -0
  155. package/templates/.agents/skills/run-manual-validation/config/verify.en.json +25 -0
  156. package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +25 -0
  157. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +67 -0
  158. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +67 -0
  159. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +69 -0
  160. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +69 -0
  161. package/templates/.agents/skills/run-task/SKILL.en.md +9 -7
  162. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -7
  163. package/templates/.agents/skills/run-task/reference/host-validation.en.md +5 -3
  164. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +5 -3
  165. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +75 -3
  166. package/templates/.claude/commands/run-manual-validation.en.md +9 -0
  167. package/templates/.claude/commands/run-manual-validation.zh-CN.md +9 -0
  168. package/templates/.codex/hooks.json +8 -8
  169. package/templates/.opencode/commands/run-manual-validation.en.md +11 -0
  170. package/templates/.opencode/commands/run-manual-validation.zh-CN.md +11 -0
  171. package/templates/.agents/workspace/README.en.md +0 -26
  172. package/templates/.agents/workspace/README.zh-CN.md +0 -26
@@ -154,11 +154,47 @@ function inspectLocalReleaseFacts(cwd, version, run = command) {
154
154
  const localTagConflict = Boolean(tagCommit && !localTag && !localTagAncestor);
155
155
  const postSubject = `chore: prepare next dev iteration after v${version}`;
156
156
  const postLog = localTag || localTagAncestor
157
- ? git(cwd, ['log', '--format=%s', `${tag}..HEAD`], run) ?? ''
157
+ ? git(cwd, ['log', '--format=%H%x00%s', `${tag}..HEAD`], run) ?? ''
158
158
  : '';
159
- const postCommit = postLog.split('\n').some((subject) => subject === postSubject);
159
+ const postCommit = postLog.split('\n').map((line) => line.split('\0'))
160
+ .find(([, subject]) => subject === postSubject)?.[0] ?? null;
160
161
  return { localTag, localTagAncestor, localTagConflict, postCommit };
161
162
  }
163
+ function inspectPostReleaseFacts(cwd, commit, run = command) {
164
+ const inspected = inspectGitWorkflow(cwd);
165
+ const snapshot = inspected.snapshot;
166
+ const branch = snapshot?.branch ?? '';
167
+ const remoteLine = branch ? git(cwd, ['ls-remote', '--heads', 'origin', `refs/heads/${branch}`], run) : null;
168
+ const remoteHead = remoteLine?.split(/\s+/)[0] ?? null;
169
+ const packageJson = commit ? git(cwd, ['show', `${commit}:package.json`], run, false) : null;
170
+ let newVersion = null;
171
+ if (packageJson) {
172
+ try {
173
+ const version = JSON.parse(packageJson).version;
174
+ newVersion = typeof version === 'string' ? version : null;
175
+ }
176
+ catch {
177
+ newVersion = null;
178
+ }
179
+ }
180
+ const paths = commit ? git(cwd, ['diff-tree', '--root', '--no-commit-id', '--name-only', '-r', '-z', commit], run, false) : null;
181
+ const changedPaths = paths ? paths.split('\0').filter(Boolean)
182
+ .sort((left, right) => Buffer.from(left).compare(Buffer.from(right))) : [];
183
+ const digest = commit ? git(cwd, ['show', `${commit}:${DEMO_DIGEST_PATH}`], run) : null;
184
+ return {
185
+ commit,
186
+ isHead: Boolean(commit && snapshot?.head === commit),
187
+ published: Boolean(commit && remoteHead === commit),
188
+ branch,
189
+ upstream: snapshot?.upstream ?? null,
190
+ remoteHead,
191
+ newVersion,
192
+ changedPaths,
193
+ demoInputSha256: digest && /^[0-9a-f]{64}$/.test(digest) ? digest : null,
194
+ worktree: snapshot?.worktree ?? [],
195
+ staged: snapshot?.staged ?? []
196
+ };
197
+ }
162
198
  function releaseSmokeStatus(workflows, version, tagCommit) {
163
199
  const manualTitle = `Post-Release Smoke v${version}`;
164
200
  const targets = workflows.filter((run) => {
@@ -209,12 +245,15 @@ async function inspectFacts(cwd, version) {
209
245
  const workflows = platform.workflows;
210
246
  const smoke = releaseSmokeStatus(workflows, version, localTagTarget);
211
247
  return {
212
- ...local,
248
+ localTag: local.localTag,
249
+ localTagAncestor: local.localTagAncestor,
250
+ localTagConflict: local.localTagConflict,
213
251
  remoteBranch: Boolean(remoteBranch && remoteBranch.split(/\s+/)[0] === head), remoteTag: Boolean(remoteTag),
214
252
  githubRelease: platform.platform.type !== 'github'
215
253
  ? true
216
254
  : platform.status === 'blocked' ? null : Boolean(platform.release?.published),
217
- npm: npm.published, homebrew: homebrew.published, smoke
255
+ npm: npm.published, homebrew: homebrew.published, smoke,
256
+ post: inspectPostReleaseFacts(cwd, local.postCommit)
218
257
  };
219
258
  }
220
259
  function parsePorcelainPath(record) {
@@ -245,13 +284,21 @@ function updateReleaseMetadata(cwd, version) {
245
284
  fs.writeFileSync(filePath, content);
246
285
  }
247
286
  }
287
+ function optionValues(args, name) {
288
+ const values = [];
289
+ for (let index = 0; index < args.length; index += 1) {
290
+ if (args[index] === name)
291
+ values.push(args[index + 1] ?? '');
292
+ }
293
+ return values;
294
+ }
248
295
  async function releaseWorkflow(args = []) {
249
296
  const [action, rawVersion, ...rest] = args;
250
297
  const version = String(rawVersion || '').replace(/^v/, '');
251
298
  const cwdIndex = rest.indexOf('--cwd');
252
299
  const cwd = path.resolve(cwdIndex >= 0 && rest[cwdIndex + 1] ? rest[cwdIndex + 1] : process.cwd());
253
- if (!['inspect', 'prepare', 'publish', 'post'].includes(action || '') || !semver.valid(version)) {
254
- process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'RELEASE_INPUT_INVALID', message: 'Usage: release-workflow inspect|prepare|publish|post <version>' } })}\n`);
300
+ if (!['inspect', 'prepare', 'publish', 'post-prepare', 'post-publish'].includes(action || '') || !semver.valid(version)) {
301
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'RELEASE_INPUT_INVALID', message: 'Usage: release-workflow inspect|prepare|publish|post-prepare|post-publish <version>' } })}\n`);
255
302
  process.exitCode = 1;
256
303
  return;
257
304
  }
@@ -331,10 +378,56 @@ async function releaseWorkflow(args = []) {
331
378
  process.exitCode = result.status === 'failed' ? 1 : result.status === 'degraded' ? 2 : 0;
332
379
  return;
333
380
  }
381
+ if (action === 'post-publish') {
382
+ const expectedValues = optionValues(rest, '--expected-sha256');
383
+ if (expectedValues.length !== 1 || !/^sha256:[0-9a-f]{64}$/.test(expectedValues[0])) {
384
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_INPUT_INVALID', message: 'post-publish requires one --expected-sha256 sha256:<64 hex>' } })}\n`);
385
+ process.exitCode = 1;
386
+ return;
387
+ }
388
+ if (before.phase === 'complete') {
389
+ process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
390
+ return;
391
+ }
392
+ const confirmation = 'postConfirmation' in before ? before.postConfirmation : null;
393
+ if (before.phase !== 'post-prepared' || !confirmation) {
394
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: confirmation ? 'RELEASE_PHASE_INVALID' : 'RELEASE_POST_CONFIRMATION_UNAVAILABLE', message: 'Post-release facts cannot authorize publish' } })}\n`);
395
+ process.exitCode = 1;
396
+ return;
397
+ }
398
+ if (confirmation.sha256 !== expectedValues[0]) {
399
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, snapshot: before, error: { code: 'RELEASE_POST_SNAPSHOT_MISMATCH', message: 'Post-release confirmation snapshot changed' } })}\n`);
400
+ process.exitCode = 1;
401
+ return;
402
+ }
403
+ const channelsComplete = before.facts.githubRelease === true && before.facts.npm === true && before.facts.homebrew === true;
404
+ if (!channelsComplete || before.facts.smoke !== 'success') {
405
+ process.stdout.write(`${JSON.stringify({ status: before.facts.smoke === 'failed' ? 'failed' : 'blocked', changed: false, snapshot: before, error: { code: 'RELEASE_CHANNELS_PENDING', message: 'Release channels or smoke workflow are not complete' } })}\n`);
406
+ process.exitCode = before.facts.smoke === 'failed' ? 1 : 2;
407
+ return;
408
+ }
409
+ const pushed = pushGitRefs({ cwd, remote: 'origin', refs: [before.facts.post.branch] });
410
+ const snapshot = releaseSnapshot(version, await inspectFacts(cwd, version));
411
+ if (snapshot.phase === 'complete') {
412
+ process.stdout.write(`${JSON.stringify({ ...pushed, status: 'applied', changed: true, snapshot, error: null })}\n`);
413
+ return;
414
+ }
415
+ const blocked = pushed.status !== 'failed';
416
+ process.stdout.write(`${JSON.stringify({ ...pushed, status: blocked ? 'blocked' : 'failed', snapshot, error: pushed.error ?? { code: 'RELEASE_PROGRESS_PENDING', message: 'Post-release push is not visible at the expected remote head' } })}\n`);
417
+ process.exitCode = blocked ? 2 : 1;
418
+ return;
419
+ }
334
420
  if (before.phase === 'complete') {
335
421
  process.stdout.write(`${JSON.stringify({ status: 'no-op', changed: false, snapshot: before, error: null })}\n`);
336
422
  return;
337
423
  }
424
+ if (before.phase === 'post-prepared') {
425
+ const confirmation = 'postConfirmation' in before ? before.postConfirmation : null;
426
+ const status = confirmation ? 'no-op' : 'blocked';
427
+ process.stdout.write(`${JSON.stringify({ status, changed: false, snapshot: before, error: confirmation ? null : { code: 'RELEASE_POST_CONFIRMATION_UNAVAILABLE', message: 'Post commit is not the clean current HEAD' } })}\n`);
428
+ process.exitCode = confirmation ? 0 : 2;
429
+ return;
430
+ }
338
431
  const channelsComplete = before.facts.githubRelease === true && before.facts.npm === true && before.facts.homebrew === true;
339
432
  if (!['published', 'post-pending'].includes(before.phase) || !channelsComplete || before.facts.smoke !== 'success') {
340
433
  process.stdout.write(`${JSON.stringify({ status: before.facts.smoke === 'failed' ? 'failed' : 'blocked', changed: false, snapshot: before, error: { code: 'RELEASE_CHANNELS_PENDING', message: 'Release channels or smoke workflow are not complete' } })}\n`);
@@ -373,10 +466,14 @@ async function releaseWorkflow(args = []) {
373
466
  process.exitCode = 1;
374
467
  return;
375
468
  }
376
- const branch = git(cwd, ['branch', '--show-current']) || '';
377
- const pushed = pushGitRefs({ cwd, remote: 'origin', refs: [branch] });
378
- process.stdout.write(`${JSON.stringify({ ...pushed, demo, snapshot: releaseSnapshot(version, await inspectFacts(cwd, version)) })}\n`);
379
- process.exitCode = pushed.status === 'failed' ? 1 : pushed.status === 'degraded' ? 2 : 0;
469
+ const snapshot = releaseSnapshot(version, await inspectFacts(cwd, version));
470
+ const confirmation = 'postConfirmation' in snapshot ? snapshot.postConfirmation : null;
471
+ if (snapshot.phase !== 'post-prepared' || !confirmation) {
472
+ process.stdout.write(`${JSON.stringify({ status: 'failed', changed: true, demo, snapshot, error: { code: 'RELEASE_POST_CONFIRMATION_UNAVAILABLE', message: 'Prepared post commit did not produce a confirmation snapshot' } })}\n`);
473
+ process.exitCode = 1;
474
+ return;
475
+ }
476
+ process.stdout.write(`${JSON.stringify({ status: 'applied', changed: true, demo, snapshot, error: null })}\n`);
380
477
  }
381
- export { changedPaths, computeDemoInputDigest, inspectFacts, inspectLocalReleaseFacts, inspectPostWorktree, releaseSmokeStatus, releaseWorkflow, runOptionalDemo };
478
+ export { changedPaths, computeDemoInputDigest, inspectFacts, inspectLocalReleaseFacts, inspectPostReleaseFacts, inspectPostWorktree, releaseSmokeStatus, releaseWorkflow, runOptionalDemo };
382
479
  //# sourceMappingURL=release-workflow.js.map
@@ -1,24 +1,61 @@
1
- import { requestSandboxControl } from '../sandbox/control/client.js';
1
+ import { requestSandboxControl, SandboxControlClientError } from '../sandbox/control/client.js';
2
2
  import { serveSandboxControl } from '../sandbox/control/server.js';
3
- function sandboxControl(args) {
3
+ import { runSandboxControlExecutor } from '../sandbox/control/executor.js';
4
+ async function sandboxControl(args) {
4
5
  const [operation, ...rest] = args;
5
6
  if (operation === 'serve') {
6
7
  const manifestIndex = rest.indexOf('--manifest');
7
8
  const manifest = manifestIndex >= 0 ? rest[manifestIndex + 1] : undefined;
8
9
  if (!manifest)
9
10
  throw new Error('sandbox-control serve requires --manifest');
10
- serveSandboxControl(manifest);
11
+ const controller = new AbortController();
12
+ const abort = () => controller.abort();
13
+ process.once('SIGINT', abort);
14
+ process.once('SIGTERM', abort);
15
+ try {
16
+ await serveSandboxControl(manifest, controller.signal);
17
+ }
18
+ finally {
19
+ process.off('SIGINT', abort);
20
+ process.off('SIGTERM', abort);
21
+ }
22
+ return;
23
+ }
24
+ if (operation === 'execute') {
25
+ const requestIndex = rest.indexOf('--request');
26
+ const nonceIndex = rest.indexOf('--nonce');
27
+ const request = requestIndex >= 0 ? rest[requestIndex + 1] : undefined;
28
+ const nonce = nonceIndex >= 0 ? rest[nonceIndex + 1] : undefined;
29
+ if (!request || !nonce)
30
+ throw new Error('sandbox-control execute requires --request and --nonce');
31
+ await runSandboxControlExecutor(request, nonce);
11
32
  return;
12
33
  }
13
34
  if (operation === 'client') {
14
35
  const [family = '', ...commandArgs] = rest;
15
- const response = requestSandboxControl({ family, args: commandArgs });
36
+ let response;
37
+ try {
38
+ response = requestSandboxControl({ family, args: commandArgs });
39
+ }
40
+ catch (error) {
41
+ if (!(error instanceof SandboxControlClientError))
42
+ throw error;
43
+ process.stderr.write(`${error.detail.message}\n`);
44
+ process.exitCode = error.detail.retryable ? 75 : 1;
45
+ return;
46
+ }
16
47
  process.stdout.write(response.stdout);
17
48
  process.stderr.write(response.stderr);
18
- process.exitCode = response.exitCode;
49
+ if (response.phase === 'rejected') {
50
+ process.stderr.write(response.error?.message ?? response.stderr);
51
+ process.exitCode = response.error?.retryable ? 75 : 1;
52
+ }
53
+ else {
54
+ process.exitCode = response.exitCode ?? 1;
55
+ }
19
56
  return;
20
57
  }
21
- throw new Error('Usage: agent-infra-internal sandbox-control serve --manifest <path> | client <family> [args...]');
58
+ throw new Error('Usage: agent-infra-internal sandbox-control serve --manifest <path> | execute --request <path> --nonce <nonce> | client <family> [args...]');
22
59
  }
23
60
  export { sandboxControl };
24
61
  //# sourceMappingURL=sandbox-control.js.map
@@ -1,13 +1,13 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { requestSandboxTaskCreate } from '../sandbox/control/client.js';
3
+ import { requestSandboxTaskCreate, SandboxControlClientError } from '../sandbox/control/client.js';
4
4
  import { SANDBOX_CONTROL_MAX_BYTES } from '../sandbox/control/protocol.js';
5
5
  import { validateTaskCreateCandidate } from '../task/create.js';
6
6
  import { createTask } from '../task/create-service.js';
7
- function failed(code, message) {
7
+ function failed(code, message, retryable = false) {
8
8
  return {
9
- status: 'failed', changed: false, task: { id: null, shortId: null }, issue: null,
10
- operations: [], warnings: [], error: { code, message, retryable: false }
9
+ status: retryable ? 'blocked' : 'failed', changed: false, task: { id: null, shortId: null }, issue: null,
10
+ operations: [], warnings: [], error: { code, message, retryable }
11
11
  };
12
12
  }
13
13
  function output(result) {
@@ -31,6 +31,10 @@ function taskCreate(args) {
31
31
  const candidate = validateTaskCreateCandidate(JSON.parse(fs.readFileSync(inputPath, 'utf8')));
32
32
  if (process.env.AGENT_INFRA_CONTROL_TOKEN) {
33
33
  const response = requestSandboxTaskCreate({ candidate });
34
+ if (response.phase === 'rejected') {
35
+ output(failed(response.error?.code ?? 'SANDBOX_CONTROL_REJECTED', response.error?.message ?? response.stderr, response.error?.retryable ?? false));
36
+ return;
37
+ }
34
38
  process.stdout.write(response.stdout);
35
39
  process.stderr.write(response.stderr);
36
40
  process.exitCode = response.exitCode;
@@ -39,6 +43,10 @@ function taskCreate(args) {
39
43
  output(createTask(candidate, { repoRoot: process.cwd() }));
40
44
  }
41
45
  catch (error) {
46
+ if (error instanceof SandboxControlClientError) {
47
+ output(failed(error.detail.code, error.detail.message, error.detail.retryable));
48
+ return;
49
+ }
42
50
  const message = error instanceof Error ? error.message : String(error);
43
51
  const code = /^([A-Z][A-Z0-9_]+)/.exec(message)?.[1] ?? 'TASK_CREATE_INPUT_INVALID';
44
52
  output(failed(code, message));
@@ -1,7 +1,8 @@
1
1
  import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
2
- import { abortCommitIntent, activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, completeCommitIntent, pauseOrchestration, prepareOrchestrationDelegation, recoverCommitIntent, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, startCommitAttempt, statusCommitIntent, statusOrchestration, terminateCommitAttempt } from '../task/orchestration.js';
2
+ import { abortCommitIntent, activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, awaitOrchestrationDelegationActivation, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, completeCommitIntent, dispatchOrchestrationDelegation, pauseOrchestration, recoverCommitIntent, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, startCommitAttempt, statusCommitIntent, statusOrchestration, terminateCommitAttempt } from '../task/orchestration.js';
3
+ import { prepareCodexOrchestrationDelegation } from '../task/codex-orchestration.js';
3
4
  import { isAgentClientId } from '../agent-clients/types.js';
4
- const USAGE = 'Usage: agent-infra-internal task-orchestration <task-ref|auto> <begin-or-resume|route|prepare|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';
5
+ 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';
5
6
  function usageFailure(message) {
6
7
  process.stdout.write(`${JSON.stringify({
7
8
  status: 'failed', changed: false,
@@ -10,7 +11,7 @@ function usageFailure(message) {
10
11
  process.stderr.write(USAGE);
11
12
  process.exitCode = 2;
12
13
  }
13
- function taskOrchestration(args = []) {
14
+ async function taskOrchestration(args = []) {
14
15
  if (args[0] === '--help' || args[0] === '-h') {
15
16
  process.stdout.write(USAGE);
16
17
  return;
@@ -21,7 +22,8 @@ function taskOrchestration(args = []) {
21
22
  }
22
23
  const [taskRef, intent] = args;
23
24
  if (![
24
- 'begin-or-resume', 'route', 'prepare', 'hook-start', 'hook-stop', 'advance', 'pause', 'status',
25
+ 'begin-or-resume', 'route', 'prepare', 'dispatch', 'await-activation', 'recover-prepared',
26
+ 'hook-start', 'hook-stop', 'advance', 'pause', 'status',
25
27
  'commit-start', 'commit-begin', 'commit-checkpoint', 'commit-complete', 'commit-recover', 'commit-abort', 'commit-terminate', 'commit-status'
26
28
  ].includes(intent)) {
27
29
  usageFailure(`unknown intent '${intent}'`);
@@ -44,7 +46,9 @@ function taskOrchestration(args = []) {
44
46
  if (![
45
47
  '--max-steps', '--executor-model', '--executor-reasoning-effort',
46
48
  '--reviewer-model', '--reviewer-reasoning-effort',
47
- '--client', '--requested-model', '--requested-reasoning-effort', '--parent-id', '--before-fingerprint',
49
+ '--client', '--requested-model', '--requested-reasoning-effort', '--capability-token',
50
+ '--parent-id', '--before-fingerprint',
51
+ '--stage', '--round', '--artifact', '--role',
48
52
  '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
49
53
  '--model-fallback-reason', '--reasoning-effort-fallback-reason',
50
54
  '--exit-code', '--after-fingerprint', '--changed-paths', '--code', '--message', '--recoverable', '--agent',
@@ -260,12 +264,41 @@ function taskOrchestration(args = []) {
260
264
  usageFailure(`intent 'prepare' requires '${missing}'`);
261
265
  return;
262
266
  }
263
- result = prepareOrchestrationDelegation(taskRef, {
267
+ result = await prepareCodexOrchestrationDelegation(taskRef, {
264
268
  client: values['--client'],
265
269
  requestedModel: values['--requested-model'],
266
- requestedReasoningEffort: values['--requested-reasoning-effort']
270
+ requestedReasoningEffort: values['--requested-reasoning-effort'],
271
+ capabilityToken: values['--capability-token']
272
+ }, { orchestrationOptions: coreOptions });
273
+ }
274
+ else if (intent === 'dispatch') {
275
+ result = dispatchOrchestrationDelegation(taskRef, coreOptions);
276
+ }
277
+ else if (intent === 'await-activation') {
278
+ const missing = requireValues(['--stage', '--round', '--artifact', '--role']);
279
+ if (missing) {
280
+ usageFailure(`intent 'await-activation' requires '${missing}'`);
281
+ return;
282
+ }
283
+ const round = Number(values['--round']);
284
+ if (!Number.isSafeInteger(round) || round < 1) {
285
+ usageFailure('--round must be a positive integer');
286
+ return;
287
+ }
288
+ if (!['executor', 'reviewer'].includes(values['--role'])) {
289
+ usageFailure('--role must be executor or reviewer');
290
+ return;
291
+ }
292
+ result = await awaitOrchestrationDelegationActivation(taskRef, {
293
+ stage: values['--stage'],
294
+ round,
295
+ artifact: values['--artifact'],
296
+ role: values['--role']
267
297
  }, coreOptions);
268
298
  }
299
+ else if (intent === 'recover-prepared') {
300
+ result = recoverPreparedOrchestrationDelegation(taskRef, coreOptions);
301
+ }
269
302
  else if (intent === 'hook-start') {
270
303
  const missing = requireValues([
271
304
  ...(taskRef === 'auto' ? ['--client'] : []),
@@ -5,6 +5,7 @@ import semver from 'semver';
5
5
  import { getPlatformAdapter, registerPlatformAdapter } from './adapters.js';
6
6
  import { createGitHubClient, MINIMUM_GITHUB_CLI_VERSION } from './github-client.js';
7
7
  import { platformResult } from './types.js';
8
+ const CURRENT_USER_QUERY = 'query { viewer { login } }';
8
9
  function findRepoRoot(cwd) {
9
10
  try {
10
11
  return execFileSync('git', ['rev-parse', '--show-toplevel'], {
@@ -89,18 +90,19 @@ function resolveGitHubContext(options) {
89
90
  error: { code: 'UPSTREAM_UNRESOLVED', message: 'Unable to resolve the upstream repository', retryable: false }
90
91
  });
91
92
  }
92
- const user = client.json(['api', 'user'], { cwd });
93
+ const user = client.json(['api', 'graphql', '-f', `query=${CURRENT_USER_QUERY}`], { cwd });
93
94
  if (!user.ok)
94
95
  return failure(user.error, upstream);
95
96
  const userValue = user.value;
97
+ const currentUser = userValue?.data?.viewer?.login || null;
96
98
  const permissions = client.json(['api', `repos/${upstream}`], { cwd });
97
99
  if (!permissions.ok)
98
100
  return failure(permissions.error, upstream);
99
101
  const permissionValue = permissions.value;
100
102
  const values = permissionValue?.permissions || {};
101
103
  const capabilities = {
102
- authenticated: Boolean(userValue?.login),
103
- comment: Boolean(userValue?.login),
104
+ authenticated: Boolean(currentUser),
105
+ comment: Boolean(currentUser),
104
106
  triage: Boolean(values.triage || values.push || values.admin),
105
107
  push: Boolean(values.push || values.admin),
106
108
  admin: Boolean(values.admin)
@@ -108,7 +110,7 @@ function resolveGitHubContext(options) {
108
110
  const status = Object.values(capabilities).every(Boolean) ? 'no-op' : 'degraded';
109
111
  return platformResult(status, {
110
112
  changed: false,
111
- platform: { type: platform, repository: upstream, currentUser: userValue?.login || null },
113
+ platform: { type: platform, repository: upstream, currentUser },
112
114
  resource: { kind: 'repository', number: null },
113
115
  capabilities,
114
116
  operations: [{ name: 'resolve', status: 'no-op', reasonCode: null }]
@@ -1,7 +1,9 @@
1
1
  import process from 'node:process';
2
2
  import spawn from 'cross-spawn';
3
3
  import semver from 'semver';
4
- const MINIMUM_GITHUB_CLI_VERSION = '2.16.0';
4
+ // closingIssuesReferences (introduced in gh 2.72.0) is the highest-versioned gh feature
5
+ // the platform layer depends on; see github-release-notes.ts:102.
6
+ const MINIMUM_GITHUB_CLI_VERSION = '2.72.0';
5
7
  const GITHUB_CLI_MAX_BUFFER = 64 * 1024 * 1024;
6
8
  const ERROR_DETAIL_LIMIT = 4096;
7
9
  function boundedFailureDetail(result) {
@@ -76,6 +78,41 @@ function classifyGitHubFailure(result) {
76
78
  }
77
79
  return { code: 'PLATFORM_REQUEST_FAILED', message: detail || 'GitHub request failed', retryable: false };
78
80
  }
81
+ function responseUrl(args) {
82
+ const endpoint = args.find((arg, index) => index > 0 && !arg.startsWith('-')) || '';
83
+ return endpoint.startsWith('http') ? endpoint : `https://api.github.com/${endpoint.replace(/^\//, '')}`;
84
+ }
85
+ function parseIncludedResponse(stdout, args) {
86
+ const statusBlocks = [...stdout.matchAll(/^HTTP\/[^\r\n]+/gim)];
87
+ const headerStart = statusBlocks.at(-1)?.index;
88
+ if (headerStart === undefined)
89
+ return null;
90
+ const response = stdout.slice(headerStart);
91
+ const separator = response.match(/\r?\n\r?\n/);
92
+ if (!separator || separator.index === undefined)
93
+ return null;
94
+ const header = response.slice(0, separator.index);
95
+ const body = response.slice(separator.index + separator[0].length);
96
+ const status = header.match(/^HTTP\/[^ ]+\s+(\d{3})/mi)?.[1];
97
+ if (!status)
98
+ return null;
99
+ const date = header.match(/^Date:\s*(.+)$/mi)?.[1]?.trim();
100
+ const links = [...header.matchAll(/^Link:\s*(.+)$/gim)].flatMap((match) => match[1].split(',').map((value) => value.trim()));
101
+ try {
102
+ return {
103
+ value: JSON.parse(body || 'null'),
104
+ metadata: {
105
+ status: Number(status),
106
+ requestUrl: responseUrl(args),
107
+ ...(date ? { date } : {}),
108
+ links
109
+ }
110
+ };
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ }
79
116
  function createGitHubClient(options = {}) {
80
117
  const runner = options.runner || defaultRunner;
81
118
  const delays = options.retryDelaysMs || retryDelaysFromEnvironment();
@@ -134,8 +171,24 @@ function createGitHubClient(options = {}) {
134
171
  error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub returned invalid JSON', retryable: true }
135
172
  };
136
173
  }
174
+ },
175
+ jsonWithMetadata(args, request = {}) {
176
+ const includeArgs = args[0] === 'api' && args[1] !== '--include'
177
+ ? [args[0], '--include', ...args.slice(1)]
178
+ : args;
179
+ const result = run(includeArgs, request);
180
+ if (!result.ok)
181
+ return result;
182
+ const parsed = parseIncludedResponse(result.value, args);
183
+ if (!parsed) {
184
+ return {
185
+ ok: false,
186
+ error: { code: 'INVALID_PLATFORM_RESPONSE', message: 'GitHub response metadata or JSON is invalid', retryable: true }
187
+ };
188
+ }
189
+ return { ok: true, value: parsed };
137
190
  }
138
191
  };
139
192
  }
140
- export { classifyGitHubFailure, createGitHubClient, MINIMUM_GITHUB_CLI_VERSION };
193
+ export { classifyGitHubFailure, createGitHubClient, MINIMUM_GITHUB_CLI_VERSION, parseIncludedResponse };
141
194
  //# sourceMappingURL=github-client.js.map
@@ -20,6 +20,7 @@ const ARTIFACT_TITLES = {
20
20
  code: '实现报告',
21
21
  'review-code': '代码审查',
22
22
  'manual-validation': '人工验证报告',
23
+ 'validation-run': '验证运行证据',
23
24
  'pr-review': 'PR 审查报告'
24
25
  };
25
26
  function normalizeCommentContent(content) {
@@ -52,7 +53,7 @@ function renderTaskComment(content, taskId, agent) {
52
53
  }
53
54
  function artifactIdentity(artifact) {
54
55
  const stem = path.basename(artifact, '.md');
55
- const match = stem.match(/^(analysis|review-analysis|plan|review-plan|code|review-code|manual-validation|pr-review)(?:-r(\d+))?$/);
56
+ const match = stem.match(/^(analysis|review-analysis|plan|review-plan|code|review-code|manual-validation|validation-run|pr-review)(?:-r(\d+))?$/);
56
57
  if (!match)
57
58
  throw new Error(`unsupported artifact '${artifact}'`);
58
59
  const base = ARTIFACT_TITLES[match[1]];