@fitlab-ai/agent-infra 0.9.13 → 0.9.14

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 (280) hide show
  1. package/bin/cli.ts +26 -9
  2. package/dist/bin/cli.js +173 -157
  3. package/dist/bin/internal-cli.js +42 -21
  4. package/dist/lib/agent-client.js +5 -0
  5. package/dist/lib/internal/agent-client.js +5 -2
  6. package/dist/lib/internal/cli-route-inventory.js +152 -0
  7. package/dist/lib/internal/codex-lifecycle.js +9 -6
  8. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  9. package/dist/lib/internal/git-workflow.js +10 -6
  10. package/dist/lib/internal/platform-checks.js +16 -4
  11. package/dist/lib/internal/platform-comment.js +16 -4
  12. package/dist/lib/internal/platform-context.js +4 -1
  13. package/dist/lib/internal/platform-issue.js +12 -4
  14. package/dist/lib/internal/platform-metadata.js +88 -0
  15. package/dist/lib/internal/platform-pr-review.js +16 -3
  16. package/dist/lib/internal/platform-pr.js +24 -10
  17. package/dist/lib/internal/platform-release-notes.js +6 -3
  18. package/dist/lib/internal/platform-security.js +93 -0
  19. package/dist/lib/internal/pr-review-grade.js +14 -3
  20. package/dist/lib/internal/release-workflow.js +19 -5
  21. package/dist/lib/internal/sandbox-control.js +22 -5
  22. package/dist/lib/internal/task-activity.js +13 -4
  23. package/dist/lib/internal/task-artifact.js +186 -27
  24. package/dist/lib/internal/task-context.js +4 -1
  25. package/dist/lib/internal/task-create.js +48 -15
  26. package/dist/lib/internal/task-delivery.js +4 -1
  27. package/dist/lib/internal/task-event.js +4 -1
  28. package/dist/lib/internal/task-finalization.js +4 -1
  29. package/dist/lib/internal/task-invalidation.js +4 -1
  30. package/dist/lib/internal/task-ledger.js +12 -1
  31. package/dist/lib/internal/task-lifecycle.js +4 -1
  32. package/dist/lib/internal/task-operation-registry.js +421 -0
  33. package/dist/lib/internal/task-orchestration.js +12 -0
  34. package/dist/lib/internal/task-override.js +10 -3
  35. package/dist/lib/internal/task-qualification.js +90 -0
  36. package/dist/lib/internal/task-review.js +4 -1
  37. package/dist/lib/internal/task-short-id.js +11 -1
  38. package/dist/lib/internal/task-snapshot.js +4 -1
  39. package/dist/lib/internal/task-validate.js +7 -2
  40. package/dist/lib/internal/task-verify.js +5 -1
  41. package/dist/lib/internal/task-warning.js +8 -1
  42. package/dist/lib/platform/comment-safety.js +226 -0
  43. package/dist/lib/platform/github-provider.js +233 -6
  44. package/dist/lib/platform/issue-comments.js +146 -34
  45. package/dist/lib/platform/pr-change-report.js +6 -12
  46. package/dist/lib/platform/pr-summary.js +28 -7
  47. package/dist/lib/platform/provider-contract.d.ts +67 -1
  48. package/dist/lib/platform/provider-contract.js +6 -2
  49. package/dist/lib/platform/provider-validation.js +76 -3
  50. package/dist/lib/platform/pull-requests.js +115 -170
  51. package/dist/lib/platform/repository-metadata.js +377 -0
  52. package/dist/lib/platform/security-alerts.js +155 -0
  53. package/dist/lib/platform/verification-sync.js +33 -26
  54. package/dist/lib/process-data/index.js +7 -0
  55. package/dist/lib/run/index.js +5 -0
  56. package/dist/lib/sandbox/commands/control-status.js +22 -0
  57. package/dist/lib/sandbox/commands/create.js +32 -6
  58. package/dist/lib/sandbox/commands/ls.js +5 -0
  59. package/dist/lib/sandbox/commands/rm.js +827 -120
  60. package/dist/lib/sandbox/commands/show.js +16 -0
  61. package/dist/lib/sandbox/control/client.js +83 -9
  62. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  63. package/dist/lib/sandbox/control/container-identity.js +28 -31
  64. package/dist/lib/sandbox/control/executor.js +149 -14
  65. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  66. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  67. package/dist/lib/sandbox/control/server.js +305 -35
  68. package/dist/lib/sandbox/control/state.js +5 -3
  69. package/dist/lib/sandbox/control/task-view.js +183 -0
  70. package/dist/lib/sandbox/engines/authority.js +289 -0
  71. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  72. package/dist/lib/sandbox/index.js +5 -0
  73. package/dist/lib/sandbox/recovery.js +21 -1
  74. package/dist/lib/sandbox/workspace-view.js +2 -1
  75. package/dist/lib/sandbox/worktree-safety.js +4 -1
  76. package/dist/lib/server/index.js +8 -0
  77. package/dist/lib/task/artifact-lifecycle.js +42 -13
  78. package/dist/lib/task/artifact-operations.js +520 -0
  79. package/dist/lib/task/artifact-repair-intent.js +59 -0
  80. package/dist/lib/task/artifact-schema.js +135 -0
  81. package/dist/lib/task/capabilities.js +72 -1
  82. package/dist/lib/task/control-authority.js +4 -0
  83. package/dist/lib/task/create-service.js +103 -4
  84. package/dist/lib/task/create.js +25 -0
  85. package/dist/lib/task/events.js +186 -67
  86. package/dist/lib/task/index.js +11 -3
  87. package/dist/lib/task/local-artifact-finalization.js +75 -150
  88. package/dist/lib/task/orchestration.js +192 -52
  89. package/dist/lib/task/qualification-audit.js +490 -0
  90. package/dist/lib/task/qualification-confirmation.js +109 -0
  91. package/dist/lib/task/qualification-intents.js +148 -0
  92. package/dist/lib/task/review-artifacts.js +22 -23
  93. package/dist/lib/task/review-finalization.js +121 -4
  94. package/dist/lib/task/sections.js +6 -4
  95. package/dist/lib/task/verification-config.js +11 -0
  96. package/dist/lib/task/verification-engine.js +54 -4
  97. package/dist/lifecycle-build-manifest.json +3 -3
  98. package/lib/agent-client.ts +9 -0
  99. package/lib/internal/agent-client.ts +4 -2
  100. package/lib/internal/cli-route-inventory.ts +140 -0
  101. package/lib/internal/codex-lifecycle.ts +8 -6
  102. package/lib/internal/codex-sandbox-controller.ts +7 -2
  103. package/lib/internal/git-workflow.ts +9 -6
  104. package/lib/internal/platform-checks.ts +12 -4
  105. package/lib/internal/platform-comment.ts +12 -4
  106. package/lib/internal/platform-context.ts +3 -1
  107. package/lib/internal/platform-issue.ts +11 -4
  108. package/lib/internal/platform-metadata.ts +70 -0
  109. package/lib/internal/platform-pr-review.ts +14 -3
  110. package/lib/internal/platform-pr.ts +23 -10
  111. package/lib/internal/platform-release-notes.ts +5 -3
  112. package/lib/internal/platform-security.ts +83 -0
  113. package/lib/internal/pr-review-grade.ts +10 -3
  114. package/lib/internal/release-workflow.ts +17 -5
  115. package/lib/internal/sandbox-control.ts +19 -5
  116. package/lib/internal/task-activity.ts +12 -4
  117. package/lib/internal/task-artifact.ts +164 -25
  118. package/lib/internal/task-context.ts +3 -1
  119. package/lib/internal/task-create.ts +76 -15
  120. package/lib/internal/task-delivery.ts +3 -1
  121. package/lib/internal/task-event.ts +3 -1
  122. package/lib/internal/task-finalization.ts +3 -1
  123. package/lib/internal/task-invalidation.ts +3 -1
  124. package/lib/internal/task-ledger.ts +11 -1
  125. package/lib/internal/task-lifecycle.ts +3 -1
  126. package/lib/internal/task-operation-registry.ts +482 -0
  127. package/lib/internal/task-orchestration.ts +12 -0
  128. package/lib/internal/task-override.ts +9 -3
  129. package/lib/internal/task-qualification.ts +61 -0
  130. package/lib/internal/task-review.ts +3 -1
  131. package/lib/internal/task-short-id.ts +10 -1
  132. package/lib/internal/task-snapshot.ts +3 -1
  133. package/lib/internal/task-validate.ts +6 -2
  134. package/lib/internal/task-verify.ts +4 -1
  135. package/lib/internal/task-warning.ts +7 -1
  136. package/lib/platform/comment-safety.ts +250 -0
  137. package/lib/platform/github-provider.ts +229 -7
  138. package/lib/platform/issue-comments.ts +160 -35
  139. package/lib/platform/pr-change-report.ts +6 -12
  140. package/lib/platform/pr-summary.ts +27 -6
  141. package/lib/platform/provider-contract.ts +78 -3
  142. package/lib/platform/provider-validation.ts +85 -3
  143. package/lib/platform/pull-requests.ts +126 -178
  144. package/lib/platform/repository-metadata.ts +453 -0
  145. package/lib/platform/security-alerts.ts +191 -0
  146. package/lib/platform/verification-sync.ts +32 -28
  147. package/lib/process-data/index.ts +7 -0
  148. package/lib/run/index.ts +5 -0
  149. package/lib/sandbox/commands/control-status.ts +26 -0
  150. package/lib/sandbox/commands/create.ts +46 -6
  151. package/lib/sandbox/commands/ls.ts +7 -0
  152. package/lib/sandbox/commands/rm.ts +1000 -87
  153. package/lib/sandbox/commands/show.ts +16 -0
  154. package/lib/sandbox/control/client.ts +103 -9
  155. package/lib/sandbox/control/completed-reentry.ts +98 -0
  156. package/lib/sandbox/control/container-identity.ts +27 -43
  157. package/lib/sandbox/control/executor.ts +177 -12
  158. package/lib/sandbox/control/lifecycle.ts +629 -17
  159. package/lib/sandbox/control/native-file-lock.ts +209 -0
  160. package/lib/sandbox/control/protocol.ts +5 -2
  161. package/lib/sandbox/control/server.ts +319 -37
  162. package/lib/sandbox/control/state.ts +6 -3
  163. package/lib/sandbox/control/task-view.ts +240 -0
  164. package/lib/sandbox/engines/authority.ts +365 -0
  165. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  166. package/lib/sandbox/index.ts +7 -0
  167. package/lib/sandbox/recovery.ts +21 -1
  168. package/lib/sandbox/workspace-view.ts +9 -3
  169. package/lib/sandbox/worktree-safety.ts +4 -1
  170. package/lib/server/index.ts +10 -0
  171. package/lib/task/artifact-lifecycle.ts +38 -16
  172. package/lib/task/artifact-operations.ts +626 -0
  173. package/lib/task/artifact-repair-intent.ts +71 -0
  174. package/lib/task/artifact-schema.ts +192 -0
  175. package/lib/task/capabilities.ts +70 -1
  176. package/lib/task/control-authority.ts +11 -1
  177. package/lib/task/create-service.ts +131 -6
  178. package/lib/task/create.ts +27 -0
  179. package/lib/task/events.ts +161 -65
  180. package/lib/task/index.ts +16 -3
  181. package/lib/task/local-artifact-finalization.ts +87 -218
  182. package/lib/task/orchestration.ts +172 -49
  183. package/lib/task/qualification-audit.ts +599 -0
  184. package/lib/task/qualification-confirmation.ts +117 -0
  185. package/lib/task/qualification-intents.ts +150 -0
  186. package/lib/task/review-artifacts.ts +21 -22
  187. package/lib/task/review-finalization.ts +133 -4
  188. package/lib/task/sections.ts +6 -4
  189. package/lib/task/verification-config.ts +12 -0
  190. package/lib/task/verification-engine.ts +55 -4
  191. package/package.json +6 -1
  192. package/templates/.agents/rules/README.en.md +2 -0
  193. package/templates/.agents/rules/README.zh-CN.md +2 -0
  194. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  195. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  196. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  197. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  198. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  199. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  200. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  201. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  202. package/templates/.agents/rules/security-alerts.en.md +8 -8
  203. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  204. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  205. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  206. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  207. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  208. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  209. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  210. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  211. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  212. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  213. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  214. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  215. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  216. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  217. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  218. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  219. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  220. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  221. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  222. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  223. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  224. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -1
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -1
  226. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  227. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  228. package/templates/.agents/skills/complete-task/SKILL.en.md +9 -22
  229. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +9 -22
  230. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  231. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  232. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  233. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  234. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  235. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  236. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  237. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  238. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  239. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  240. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  241. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  242. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  243. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  244. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  245. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  246. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  247. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  248. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  249. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  250. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  251. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  252. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  253. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  254. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  255. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  256. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  257. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  258. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  259. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  260. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  261. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  262. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  263. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  264. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  265. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  266. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -1
  267. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -1
  268. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  269. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  270. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  271. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  272. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  273. package/templates/.agents/templates/task.en.md +6 -0
  274. package/templates/.agents/templates/task.zh-CN.md +6 -0
  275. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  276. package/templates/.agents/scripts/security-alerts.sh +0 -31
  277. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  278. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  279. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  280. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -7,6 +7,7 @@ import { fetchSandboxRows, selectSandboxContainer } from './list-running.js';
7
7
  import { resolveTools, toolConfigDirCandidates } from '../tools.js';
8
8
  import { detectEngine } from '../engine.js';
9
9
  import { resolveSandboxTarget } from '../workspace-identity.js';
10
+ import { readSandboxControlStatusForRow } from './control-status.js';
10
11
  const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
11
12
 
12
13
  Shows one sandbox's worktree path and per-tool state paths (Claude Code,
@@ -62,6 +63,21 @@ export function show(args = []) {
62
63
  p.log.step('Workspace identity');
63
64
  process.stdout.write(` Mode: ${container?.workspaceMode ?? 'legacy-invalid'}\n`);
64
65
  process.stdout.write(` Task: ${container?.taskId ?? '-'}\n`);
66
+ p.log.step('Control status');
67
+ const controlStatus = container
68
+ ? readSandboxControlStatusForRow(config, container)
69
+ : null;
70
+ if (!controlStatus) {
71
+ p.log.warn(' Control status unavailable');
72
+ }
73
+ else {
74
+ process.stdout.write(` Health: ${controlStatus.state}\n`);
75
+ process.stdout.write(` Task view: ${controlStatus.taskView.state}\n`);
76
+ process.stdout.write(` Task source: ${controlStatus.taskView.observedSource ?? '-'}\n`);
77
+ if (controlStatus.taskView.reasonCode) {
78
+ process.stdout.write(` Task view reason: ${controlStatus.taskView.reasonCode}\n`);
79
+ }
80
+ }
65
81
  for (const tool of detail.toolStates) {
66
82
  p.log.step(`${tool.name} state`);
67
83
  if (tool.entries.length === 0) {
@@ -4,7 +4,54 @@ import { randomUUID } from 'node:crypto';
4
4
  import { SANDBOX_CONTROL_ADMISSION_WINDOW_MS, SANDBOX_CONTROL_MAX_BYTES, SANDBOX_CONTROL_STATUS_STALE_MS, isSandboxControlFamily } from './protocol.js';
5
5
  import { normalizeAgentToken } from '../../agent-clients/tokens.js';
6
6
  import { readSandboxControlPayload, readSandboxControlStatus } from './state.js';
7
+ import { accessSandboxTaskView, taskViewFromStatus } from './task-view.js';
7
8
  const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
9
+ export function classifySandboxControlEnvironment(env = process.env) {
10
+ const controlKeys = [
11
+ 'AGENT_INFRA_CONTROL_TOKEN',
12
+ 'AGENT_INFRA_CONTROL_GENERATION',
13
+ 'AGENT_INFRA_CONTROL_DIR',
14
+ 'AGENT_INFRA_CONTROL_STATUS_DIR'
15
+ ];
16
+ const hasControlMarker = controlKeys.some((key) => Boolean(env[key]));
17
+ const hasTaskIdentity = Boolean(env.AGENT_INFRA_TASK_ID);
18
+ const hasRuntime = Boolean(env.AGENT_INFRA_RUNTIME_DIR);
19
+ const hasExecutorMarker = Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
20
+ const hasControllerBinding = Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING);
21
+ const hasAnyMarker = hasControlMarker || hasTaskIdentity || hasRuntime || hasExecutorMarker || hasControllerBinding;
22
+ const hasCompleteControl = controlKeys.every((key) => Boolean(env[key]));
23
+ if (!hasAnyMarker)
24
+ return { kind: 'direct' };
25
+ if (hasExecutorMarker) {
26
+ return {
27
+ kind: 'invalid',
28
+ code: 'TASK_CONTROL_TRANSPORT_INVALID',
29
+ message: 'executor context is only valid for sandbox-control execute'
30
+ };
31
+ }
32
+ if (hasControllerBinding) {
33
+ return {
34
+ kind: 'invalid',
35
+ code: 'TASK_CONTROL_TRANSPORT_INVALID',
36
+ message: 'sandbox client control configuration is incomplete or conflicting'
37
+ };
38
+ }
39
+ if (!hasCompleteControl) {
40
+ return {
41
+ kind: 'invalid',
42
+ code: 'TASK_CONTROL_TRANSPORT_INVALID',
43
+ message: 'sandbox client control configuration is incomplete or conflicting'
44
+ };
45
+ }
46
+ if (hasTaskIdentity !== hasRuntime) {
47
+ return {
48
+ kind: 'invalid',
49
+ code: 'TASK_CONTROL_TRANSPORT_INVALID',
50
+ message: 'sandbox task identity and runtime binding must be provided together'
51
+ };
52
+ }
53
+ return { kind: 'controlled', mode: hasTaskIdentity ? 'task-bound' : 'branch-only' };
54
+ }
8
55
  export class SandboxControlClientError extends Error {
9
56
  detail;
10
57
  accepted;
@@ -23,7 +70,12 @@ function sleep(milliseconds) {
23
70
  function clientError(code, message, retryable, accepted = false, requestId = null) {
24
71
  throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted, requestId);
25
72
  }
26
- function preflight(statusDir, generation, now = Date.now()) {
73
+ function hasTaskBoundMarker(env = process.env) {
74
+ return Boolean(env.AGENT_INFRA_TASK_ID)
75
+ && Boolean(env.AGENT_INFRA_CONTROL_TOKEN || env.AGENT_INFRA_CONTROL_GENERATION
76
+ || env.AGENT_INFRA_CONTROL_STATUS_DIR || env.AGENT_INFRA_RUNTIME_DIR || env.AGENT_INFRA_CONTROL_DIR);
77
+ }
78
+ function preflight(statusDir, generation, taskViewEffect = null, now = Date.now(), env = process.env) {
27
79
  let status;
28
80
  try {
29
81
  status = readSandboxControlStatus(statusDir);
@@ -45,8 +97,25 @@ function preflight(statusDir, generation, now = Date.now()) {
45
97
  }
46
98
  if (status.state !== 'healthy')
47
99
  clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', 'broker is not ready', true);
100
+ if (taskViewEffect && hasTaskBoundMarker(env)) {
101
+ const view = taskViewFromStatus(status);
102
+ if (view.taskId !== env.AGENT_INFRA_TASK_ID) {
103
+ clientError('SANDBOX_TASK_VIEW_TASK_ID_MISMATCH', 'task view task id does not match the sandbox task marker', false);
104
+ }
105
+ const access = accessSandboxTaskView(view, taskViewEffect);
106
+ if (!access.allowed) {
107
+ clientError(access.reasonCode ?? 'SANDBOX_TASK_VIEW_DENIED', access.message ?? 'task view denied', false);
108
+ }
109
+ }
110
+ }
111
+ function taskViewEffectForRequest(request) {
112
+ if (request.family === 'task-lifecycle' || request.family === 'task-finalization')
113
+ return 'progress';
114
+ if (request.family === 'task-orchestration')
115
+ return request.args[1] === 'status' ? 'diagnostic' : 'progress';
116
+ return null;
48
117
  }
49
- function parseResponse(raw, id) {
118
+ function parseResponse(raw, id, accepted = false) {
50
119
  const response = JSON.parse(raw);
51
120
  if (response.version !== 2 || response.id !== id
52
121
  || !['completed', 'rejected'].includes(response.phase)
@@ -54,12 +123,12 @@ function parseResponse(raw, id) {
54
123
  || (response.outputState !== undefined && response.outputState !== 'available' && response.outputState !== 'unavailable')
55
124
  || (response.outputState === 'available' && !response.payload)
56
125
  || (response.outputState === 'unavailable' && response.payload !== null && response.payload !== undefined)) {
57
- clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, false, id);
126
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, accepted, id);
58
127
  }
59
128
  return response;
60
129
  }
61
- function readPublishedResponse(raw, id, channelDir) {
62
- const response = parseResponse(raw, id);
130
+ function readPublishedResponse(raw, id, channelDir, accepted = false) {
131
+ const response = parseResponse(raw, id, accepted);
63
132
  if (response.outputState !== 'available')
64
133
  return response;
65
134
  const filePath = path.join(channelDir, 'responses', `${id}.payload.json`);
@@ -92,7 +161,8 @@ function cancelPendingRequest(requestPath) {
92
161
  function exchangeSandboxControl(request, params) {
93
162
  const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
94
163
  const statusDir = params.statusDir ?? process.env.AGENT_INFRA_CONTROL_STATUS_DIR ?? '/run/agent-infra/control-status';
95
- preflight(statusDir, request.generation);
164
+ const taskViewEffect = taskViewEffectForRequest(request);
165
+ preflight(statusDir, request.generation, taskViewEffect);
96
166
  const encoded = `${JSON.stringify(request)}\n`;
97
167
  if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
98
168
  clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false, false, request.id);
@@ -127,7 +197,9 @@ function exchangeSandboxControl(request, params) {
127
197
  }
128
198
  let response;
129
199
  try {
130
- response = readPublishedResponse(raw, request.id, channelDir);
200
+ // The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
201
+ accepted = true;
202
+ response = readPublishedResponse(raw, request.id, channelDir, accepted);
131
203
  malformedResponseRaw = null;
132
204
  }
133
205
  catch (error) {
@@ -153,7 +225,7 @@ function exchangeSandboxControl(request, params) {
153
225
  }
154
226
  if (!accepted) {
155
227
  try {
156
- preflight(statusDir, request.generation);
228
+ preflight(statusDir, request.generation, taskViewEffect);
157
229
  }
158
230
  catch (error) {
159
231
  if (cancelPendingRequest(requestPath))
@@ -196,7 +268,9 @@ export function recoverSandboxControl(requestId, params = {}) {
196
268
  }
197
269
  let response;
198
270
  try {
199
- response = readPublishedResponse(raw, requestId, channelDir);
271
+ // The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
272
+ accepted = true;
273
+ response = readPublishedResponse(raw, requestId, channelDir, accepted);
200
274
  malformedResponseRaw = null;
201
275
  }
202
276
  catch (error) {
@@ -0,0 +1,85 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { resolveTaskWorkspace } from '../task-resolver.js';
4
+ import { assertGitWorktreeBinding } from '../../git/worktree-identity.js';
5
+ import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
6
+ import { readTaskFinalizationReceipt } from '../../task/finalization.js';
7
+ import { inspectSandboxControlContainer } from './container-identity.js';
8
+ import { atomicWriteJson, readSandboxControlStatus } from './state.js';
9
+ import { taskViewForManifest } from './task-view.js';
10
+ function evidencePath(manifest) {
11
+ return path.join(path.dirname(manifest.processingDir), 'completed-reentry.json');
12
+ }
13
+ function invalid() {
14
+ return new Error('SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID');
15
+ }
16
+ export async function prepareCompletedReentry(manifest, inspect = inspectSandboxControlContainer) {
17
+ if (manifest.mode !== 'task-bound' || !manifest.taskId)
18
+ throw invalid();
19
+ assertGitWorktreeBinding(manifest.repoRoot, manifest.worktreeRoot, manifest.branch);
20
+ const container = await inspect(manifest);
21
+ if (container.state !== 'found' || container.id !== manifest.containerIdentity.id)
22
+ throw invalid();
23
+ const task = resolveTaskWorkspace(manifest.taskId, manifest.repoRoot);
24
+ const metadata = parseTypedTaskFrontmatter(fs.readFileSync(task.taskMd, 'utf8'));
25
+ if (task.state !== 'completed' || task.branch !== manifest.branch
26
+ || metadata.id !== manifest.taskId || metadata.status !== 'completed')
27
+ throw invalid();
28
+ const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
29
+ if (!receipt || receipt.taskId !== manifest.taskId)
30
+ throw invalid();
31
+ const view = taskViewForManifest({ ...manifest, receipt });
32
+ if (view.state !== 'current' || view.observedSource !== 'completed' || !view.receipt)
33
+ throw invalid();
34
+ const status = readSandboxControlStatus(manifest.publicStatusDir);
35
+ const previous = status.taskView;
36
+ if (status.generation !== manifest.generation || status.activeRequestId !== null
37
+ || previous.taskId !== manifest.taskId || !previous.receipt
38
+ || previous.receipt.receiptId !== view.receipt.receiptId
39
+ || previous.receipt.generation !== view.receipt.generation
40
+ || previous.receipt.requestId !== view.receipt.requestId
41
+ || previous.receipt.revision > view.receipt.revision)
42
+ throw invalid();
43
+ const completedRoot = fs.realpathSync.native(path.join(manifest.repoRoot, '.agents', 'workspace', 'completed'));
44
+ const source = fs.realpathSync.native(path.join(completedRoot, manifest.taskId));
45
+ if (source !== path.join(completedRoot, manifest.taskId))
46
+ throw invalid();
47
+ const stat = fs.statSync(source, { bigint: true });
48
+ return {
49
+ taskId: manifest.taskId, generation: manifest.generation,
50
+ containerId: container.id, branch: manifest.branch,
51
+ worktreeRoot: fs.realpathSync.native(manifest.worktreeRoot),
52
+ source, sourceDevice: String(stat.dev), sourceInode: String(stat.ino), receipt: view.receipt
53
+ };
54
+ }
55
+ export function publishCompletedReentry(manifest, evidence) {
56
+ atomicWriteJson(evidencePath(manifest), evidence);
57
+ }
58
+ export async function completedReentryView(manifest, inspect = inspectSandboxControlContainer) {
59
+ const file = evidencePath(manifest);
60
+ if (!fs.existsSync(file))
61
+ return null;
62
+ const evidence = JSON.parse(fs.readFileSync(file, 'utf8'));
63
+ const current = await prepareCompletedReentry(manifest, inspect);
64
+ if (JSON.stringify(evidence) !== JSON.stringify(current))
65
+ throw invalid();
66
+ return {
67
+ state: 'current', taskId: current.taskId, observedSource: 'completed',
68
+ receipt: current.receipt, reasonCode: null
69
+ };
70
+ }
71
+ export async function waitForCompletedReentry(manifest, evidence) {
72
+ const deadline = Date.now() + 10_000;
73
+ while (Date.now() < deadline) {
74
+ const status = readSandboxControlStatus(manifest.publicStatusDir);
75
+ if (status.generation !== evidence.generation)
76
+ throw invalid();
77
+ if (status.taskView.state === 'current' && status.taskView.observedSource === 'completed'
78
+ && status.taskView.taskId === evidence.taskId
79
+ && JSON.stringify(status.taskView.receipt) === JSON.stringify(evidence.receipt))
80
+ return;
81
+ await new Promise((resolve) => setTimeout(resolve, 100));
82
+ }
83
+ throw new Error('SANDBOX_COMPLETED_REENTRY_STATUS_TIMEOUT');
84
+ }
85
+ //# sourceMappingURL=completed-reentry.js.map
@@ -1,15 +1,11 @@
1
- import { commandForEngine, runEngine, runProbe } from '../shell.js';
2
- function isAuthoritativeNotFound(error) {
3
- const message = error instanceof Error ? error.message : String(error);
4
- return /(?:no such (?:container|object)|container .* not found|cannot find container)/i.test(message);
5
- }
1
+ import { runProbe } from '../shell.js';
2
+ import { captureSandboxAuthority, isSandboxAuthorityEvidence, verifySandboxAuthority } from '../engines/authority.js';
3
+ import { discoverExactContainer } from '../engines/docker-exact-discovery.js';
6
4
  export function classifySandboxContainerInspection(expected, inspection) {
7
5
  if ('notFound' in inspection)
8
6
  return { state: 'absent', id: expected.id };
9
7
  if ('error' in inspection) {
10
- return isAuthoritativeNotFound(inspection.error)
11
- ? { state: 'absent', id: expected.id }
12
- : { state: 'unknown', reason: `container inspection failed: ${inspection.error instanceof Error ? inspection.error.message : String(inspection.error)}` };
8
+ return { state: 'unknown', reason: `container inspection failed: ${inspection.error instanceof Error ? inspection.error.message : String(inspection.error)}` };
13
9
  }
14
10
  const response = inspection;
15
11
  if (typeof response.id !== 'string' || response.id !== expected.id
@@ -30,32 +26,33 @@ export function classifySandboxContainerInspection(expected, inspection) {
30
26
  };
31
27
  }
32
28
  export async function inspectSandboxControlContainer(manifest, options = {}) {
29
+ if (!isSandboxAuthorityEvidence(manifest.authorityEvidence)) {
30
+ return { state: 'unknown', reason: 'SANDBOX_AUTHORITY_EVIDENCE_MISSING' };
31
+ }
33
32
  try {
34
- const args = ['inspect', '--format', '{{json .}}', manifest.containerIdentity.id];
35
- let raw;
36
- if (options.run) {
37
- raw = options.run(manifest.engine, 'docker', args, options.timeoutMs === undefined ? {} : { timeout: options.timeoutMs });
38
- }
39
- else {
40
- const command = commandForEngine(manifest.engine, 'docker', args);
41
- const result = (options.probe ?? runProbe)(command.cmd, command.args, options.timeoutMs === undefined ? {} : { timeout: options.timeoutMs });
42
- if (result.status !== 0) {
43
- const stderr = typeof result.stderr === 'string' ? result.stderr : result.stderr?.toString('utf8') ?? '';
44
- return classifySandboxContainerInspection(manifest.containerIdentity, {
45
- error: new Error(stderr || `container inspection exited with ${result.status ?? 'unknown'}`)
46
- });
47
- }
48
- raw = typeof result.stdout === 'string' ? result.stdout : result.stdout?.toString('utf8') ?? '';
33
+ const authority = captureSandboxAuthority(manifest.engine, {
34
+ probe: options.probe,
35
+ timeoutMs: options.timeoutMs,
36
+ lockDomain: manifest.authorityEvidence.lockDomain,
37
+ route: manifest.authorityEvidence
38
+ });
39
+ const authorityState = verifySandboxAuthority(manifest.authorityEvidence, authority);
40
+ if (authorityState.state !== 'verified') {
41
+ return { state: 'unknown', reason: authorityState.reason ?? 'SANDBOX_AUTHORITY_UNAVAILABLE' };
49
42
  }
50
- const parsed = JSON.parse(raw);
51
- const value = Array.isArray(parsed) && parsed.length === 1 ? parsed[0] : parsed;
52
- if (!value || Array.isArray(value))
53
- return { state: 'unknown', reason: 'container inspection response is incomplete' };
54
- return classifySandboxContainerInspection(manifest.containerIdentity, {
55
- id: value.Id,
56
- running: value.State?.Running,
57
- labels: value.Config?.Labels
43
+ const discovered = discoverExactContainer(manifest.engine, manifest.containerIdentity.id, {
44
+ probe: options.probe,
45
+ timeoutMs: options.timeoutMs,
46
+ authority: manifest.authorityEvidence
58
47
  });
48
+ if (discovered.state === 'absent')
49
+ return { state: 'absent', id: discovered.id };
50
+ if (discovered.state === 'conflict') {
51
+ return { state: 'unknown', reason: `SANDBOX_CONTROL_CONTAINER_CONFLICT: ${discovered.reason}` };
52
+ }
53
+ if (discovered.state === 'unknown')
54
+ return discovered;
55
+ return classifySandboxContainerInspection(manifest.containerIdentity, discovered);
59
56
  }
60
57
  catch (error) {
61
58
  return classifySandboxContainerInspection(manifest.containerIdentity, { error });
@@ -1,12 +1,12 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { randomUUID } from 'node:crypto';
4
+ import { createHash, randomUUID } from 'node:crypto';
5
5
  import { getProcessStartTime } from '../../server/process-state.js';
6
- import { createTask } from '../../task/create-service.js';
6
+ import { createTask, projectTaskCreateResult, taskCreateExitCode, taskCreateFailure } from '../../task/create-service.js';
7
7
  import { applyTaskFinalization } from '../../task/finalization.js';
8
8
  import { bindSandboxControlTask, validateSandboxControlRequest } from './protocol.js';
9
- import { atomicWriteJson, executionPath, readActiveLease, terminateSandboxControlExecution } from './state.js';
9
+ import { appendSandboxControlAudit, atomicWriteJson, executionPath, readActiveLease, terminateSandboxControlExecution } from './state.js';
10
10
  import { createSandboxExecutorExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../../task/control-authority.js';
11
11
  import { assertSandboxControlBrokerOwner, readSandboxControlManifest } from './lifecycle.js';
12
12
  import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
@@ -30,6 +30,57 @@ export function assertSandboxControlExecutorAuthority(manifest, gateOwner) {
30
30
  function safeEnv(env) {
31
31
  return Object.fromEntries(Object.entries(env).filter(([key]) => !key.toUpperCase().startsWith('AGENT_INFRA_CONTROL_')));
32
32
  }
33
+ function safeRealpath(value) {
34
+ try {
35
+ return fs.realpathSync.native(value);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ function appendExecutorAudit(manifest, event, fields = {}) {
42
+ try {
43
+ appendSandboxControlAudit(manifest, event, { source: 'executor', ...fields });
44
+ }
45
+ catch {
46
+ // Diagnostics must never change the control protocol.
47
+ }
48
+ }
49
+ function requestAuditFields(manifest, manifestPath, request) {
50
+ const args = 'args' in request ? request.args : [];
51
+ const encodedArgs = JSON.stringify(args);
52
+ return {
53
+ requestId: request.id,
54
+ requestFamily: request.family,
55
+ sandboxTaskId: manifest.taskId,
56
+ requestGeneration: request.generation,
57
+ requestIssuedAt: request.issuedAt,
58
+ requestExpiresAt: request.expiresAt,
59
+ requestArgCount: args.length,
60
+ requestArgsSha256: createHash('sha256').update(encodedArgs, 'utf8').digest('hex'),
61
+ requestTaskRef: args[0] ?? null,
62
+ requestCommand: args[1] ?? null,
63
+ controllerProofPresent: request.controllerProof !== null,
64
+ hostCwd: process.cwd(),
65
+ manifestPath,
66
+ manifestPathRealpath: safeRealpath(manifestPath),
67
+ repoRoot: manifest.repoRoot,
68
+ repoRootRealpath: safeRealpath(manifest.repoRoot),
69
+ worktreeRoot: manifest.worktreeRoot,
70
+ worktreeRootRealpath: safeRealpath(manifest.worktreeRoot),
71
+ runtimeDir: manifest.runtimeDir,
72
+ runtimeDirRealpath: safeRealpath(manifest.runtimeDir)
73
+ };
74
+ }
75
+ function resultAuditFields(result) {
76
+ return {
77
+ exitCode: result.exitCode,
78
+ stdoutBytes: Buffer.byteLength(result.stdout, 'utf8'),
79
+ stderrBytes: Buffer.byteLength(result.stderr, 'utf8'),
80
+ stdoutSha256: createHash('sha256').update(result.stdout, 'utf8').digest('hex'),
81
+ stderrSha256: createHash('sha256').update(result.stderr, 'utf8').digest('hex')
82
+ };
83
+ }
33
84
  export function nodeEntryArgs(entry, args) {
34
85
  return path.extname(entry) === '.ts'
35
86
  ? ['--experimental-strip-types', '--no-warnings', entry, ...args]
@@ -174,6 +225,20 @@ function lifecycleFailure(code, message) {
174
225
  stderr: ''
175
226
  };
176
227
  }
228
+ function taskCreateExecutionResult(result) {
229
+ return {
230
+ exitCode: taskCreateExitCode(result),
231
+ stdout: `${JSON.stringify(result)}\n`,
232
+ stderr: ''
233
+ };
234
+ }
235
+ function taskCreateExecutionFailure(requestId, code, message) {
236
+ return taskCreateExecutionResult(taskCreateFailure({ code, message, retryable: false }, {
237
+ requestId,
238
+ accepted: true,
239
+ recovery: 'inspect-domain-state'
240
+ }));
241
+ }
177
242
  function finalizationResult(result) {
178
243
  const payload = {
179
244
  version: 1,
@@ -189,14 +254,21 @@ function finalizationResult(result) {
189
254
  stderr: ''
190
255
  };
191
256
  }
192
- export async function executeRequest(manifest, manifestPath, request, options = {}) {
257
+ async function executeRequestInner(manifest, manifestPath, request, options = {}) {
193
258
  if (request.family === 'task-create') {
194
- const result = await createTask(request.candidate, { repoRoot: manifest.repoRoot });
195
- return {
196
- exitCode: result.status === 'blocked' ? 2 : result.status === 'failed' ? 1 : 0,
197
- stdout: `${JSON.stringify(result)}\n`,
198
- stderr: ''
199
- };
259
+ try {
260
+ const result = await createTask(request.candidate, { repoRoot: manifest.repoRoot });
261
+ return taskCreateExecutionResult(projectTaskCreateResult(result, {
262
+ requestId: request.id,
263
+ accepted: true,
264
+ recovery: 'none'
265
+ }));
266
+ }
267
+ catch (error) {
268
+ const detail = error instanceof Error ? error.message : String(error);
269
+ const code = /^([A-Z][A-Z0-9_]+)/u.exec(detail)?.[1] ?? 'TASK_CREATE_EXECUTION_FAILED';
270
+ return taskCreateExecutionFailure(request.id, code, detail);
271
+ }
200
272
  }
201
273
  if (request.family === 'codex-controller') {
202
274
  try {
@@ -239,6 +311,16 @@ export async function executeRequest(manifest, manifestPath, request, options =
239
311
  return { exitCode: 0, stdout: `${JSON.stringify(result)}\n`, stderr: '' };
240
312
  }
241
313
  catch (error) {
314
+ const errorCode = error instanceof CodexControllerRegistrationError
315
+ ? error.code
316
+ : /^([A-Z][A-Z0-9_]+)/u.exec(error instanceof Error ? error.message : String(error))?.[1]
317
+ ?? 'CODEX_SANDBOX_CONTROLLER_FAILED';
318
+ appendExecutorAudit(manifest, 'controller-operation-failed', {
319
+ ...requestAuditFields(manifest, manifestPath, request),
320
+ controllerCommand: request.command,
321
+ errorCode,
322
+ errorType: error instanceof Error ? error.name : typeof error,
323
+ });
242
324
  return controllerFailure(error);
243
325
  }
244
326
  }
@@ -300,6 +382,14 @@ export async function executeRequest(manifest, manifestPath, request, options =
300
382
  return orchestrationFailure('CODEX_SANDBOX_CONTROLLER_PROOF_INVALID', 'Controller proof is only accepted for canonical Codex prepare');
301
383
  }
302
384
  }
385
+ const diagnosticLog = (event, fields) => {
386
+ appendExecutorAudit(manifest, event.startsWith('orchestration-') ? event : `orchestration-${event}`, {
387
+ requestId: request.id,
388
+ requestFamily: request.family,
389
+ sandboxTaskId: manifest.taskId,
390
+ ...fields
391
+ });
392
+ };
303
393
  const context = createSandboxExecutorExecutionContext({
304
394
  repoRoot: manifest.repoRoot,
305
395
  worktreeRoot: manifest.worktreeRoot,
@@ -308,6 +398,7 @@ export async function executeRequest(manifest, manifestPath, request, options =
308
398
  generation: manifest.generation,
309
399
  manifestPath,
310
400
  requestId: request.id,
401
+ diagnosticLog,
311
402
  ...(controllerBinding ? { controllerBinding } : {})
312
403
  });
313
404
  const format = (value) => {
@@ -327,6 +418,29 @@ export async function executeRequest(manifest, manifestPath, request, options =
327
418
  throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
328
419
  return format(await dispatchTaskControlOperation(context, operation));
329
420
  }
421
+ export async function executeRequest(manifest, manifestPath, request, options = {}) {
422
+ const fields = requestAuditFields(manifest, manifestPath, request);
423
+ appendExecutorAudit(manifest, 'executor-request-start', fields);
424
+ try {
425
+ const result = await executeRequestInner(manifest, manifestPath, request, options);
426
+ appendExecutorAudit(manifest, 'executor-request-finished', {
427
+ requestId: request.id,
428
+ requestFamily: request.family,
429
+ sandboxTaskId: manifest.taskId,
430
+ ...resultAuditFields(result)
431
+ });
432
+ return result;
433
+ }
434
+ catch (error) {
435
+ appendExecutorAudit(manifest, 'executor-request-failed', {
436
+ requestId: request.id,
437
+ requestFamily: request.family,
438
+ sandboxTaskId: manifest.taskId,
439
+ errorType: error instanceof Error ? error.name : typeof error,
440
+ });
441
+ throw error;
442
+ }
443
+ }
330
444
  export async function runSandboxControlExecutor(requestPath, nonce) {
331
445
  const gateOwner = await waitForGate(nonce);
332
446
  process.disconnect?.();
@@ -335,14 +449,29 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
335
449
  throw new Error('SANDBOX_CONTROL_EXECUTOR_MANIFEST_MISSING');
336
450
  const manifest = readSandboxControlManifest(manifestPath);
337
451
  const root = fs.realpathSync.native(process.cwd());
338
- if (fs.realpathSync.native(manifest.repoRoot) !== root)
452
+ const expectedRoot = safeRealpath(manifest.repoRoot);
453
+ if (expectedRoot !== root) {
454
+ appendExecutorAudit(manifest, 'executor-root-validation-failed', {
455
+ hostCwd: process.cwd(),
456
+ observedRoot: root,
457
+ expectedRoot
458
+ });
339
459
  throw new Error('SANDBOX_CONTROL_EXECUTOR_ROOT_INVALID');
460
+ }
340
461
  const raw = JSON.parse(fs.readFileSync(requestPath, 'utf8'));
341
462
  const request = validateSandboxControlRequest(raw, manifest);
463
+ appendExecutorAudit(manifest, 'executor-request-validated', requestAuditFields(manifest, manifestPath, request));
342
464
  const requestDirectory = path.resolve(path.dirname(requestPath));
343
465
  const processingDirectory = path.resolve(path.join(manifest.processingDir, request.id));
344
466
  const channelRequestDirectory = path.resolve(path.join(manifest.channelDir, 'requests'));
345
467
  if (requestDirectory !== processingDirectory && requestDirectory !== channelRequestDirectory) {
468
+ appendExecutorAudit(manifest, 'executor-request-path-validation-failed', {
469
+ ...requestAuditFields(manifest, manifestPath, request),
470
+ requestPath,
471
+ requestDirectory,
472
+ processingDirectory,
473
+ channelRequestDirectory
474
+ });
346
475
  throw new Error('SANDBOX_CONTROL_EXECUTOR_REQUEST_INVALID');
347
476
  }
348
477
  let result;
@@ -351,6 +480,10 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
351
480
  result = await executeRequest(manifest, manifestPath, request);
352
481
  }
353
482
  catch (error) {
483
+ appendExecutorAudit(manifest, 'executor-authority-or-dispatch-failed', {
484
+ ...requestAuditFields(manifest, manifestPath, request),
485
+ errorType: error instanceof Error ? error.name : typeof error,
486
+ });
354
487
  const detail = error instanceof Error ? error.message : String(error);
355
488
  const code = /^([A-Z][A-Z0-9_]+)/u.exec(detail)?.[1] ?? 'SANDBOX_CONTROL_EXECUTOR_FAILED';
356
489
  result = request.family === 'task-finalization'
@@ -362,9 +495,11 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
362
495
  })}\n`,
363
496
  stderr: ''
364
497
  }
365
- : request.family === 'task-lifecycle'
366
- ? lifecycleFailure(code, detail)
367
- : orchestrationFailure(code, detail);
498
+ : request.family === 'task-create'
499
+ ? taskCreateExecutionFailure(request.id, code, detail)
500
+ : request.family === 'task-lifecycle'
501
+ ? lifecycleFailure(code, detail)
502
+ : orchestrationFailure(code, detail);
368
503
  }
369
504
  process.stdout.write(result.stdout);
370
505
  process.stderr.write(result.stderr);