@fitlab-ai/agent-infra 0.11.11 → 0.11.13

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 (154) hide show
  1. package/dist/bin/internal-cli.js +2 -2
  2. package/dist/lib/agent-clients/adapter.js +7 -1
  3. package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
  9. package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
  12. package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
  13. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
  14. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
  15. package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
  16. package/dist/lib/agent-clients/adapters/codex.js +40 -1
  17. package/dist/lib/agent-clients/adapters/index.js +2 -10
  18. package/dist/lib/agent-clients/tokens.js +3 -2
  19. package/dist/lib/internal/agent-client.js +132 -72
  20. package/dist/lib/internal/cli-route-inventory.js +2 -2
  21. package/dist/lib/internal/sandbox-control.js +1 -81
  22. package/dist/lib/internal/task-finalization.js +26 -19
  23. package/dist/lib/internal/task-lifecycle.js +1 -1
  24. package/dist/lib/internal/task-operation-registry.js +1 -1
  25. package/dist/lib/internal/task-validate.js +1 -1
  26. package/dist/lib/platform/github-client.js +33 -3
  27. package/dist/lib/platform/issues.js +18 -6
  28. package/dist/lib/runtime/agent-runtime.js +6 -29
  29. package/dist/lib/sandbox/commands/control-status.js +1 -0
  30. package/dist/lib/sandbox/commands/create.js +7 -2
  31. package/dist/lib/sandbox/commands/ls.js +4 -5
  32. package/dist/lib/sandbox/commands/rm.js +1 -1
  33. package/dist/lib/sandbox/control/agent-operation.js +34 -0
  34. package/dist/lib/sandbox/control/client.js +14 -201
  35. package/dist/lib/sandbox/control/executor.js +12 -125
  36. package/dist/lib/sandbox/control/lifecycle.js +66 -4
  37. package/dist/lib/sandbox/control/protocol.js +15 -54
  38. package/dist/lib/sandbox/control/server.js +216 -281
  39. package/dist/lib/sandbox/control/task-view.js +0 -13
  40. package/dist/lib/sandbox/mounts.js +9 -1
  41. package/dist/lib/sandbox/recovery.js +31 -10
  42. package/dist/lib/sandbox/removal.js +28 -5
  43. package/dist/lib/sandbox/workspace-identity.js +18 -6
  44. package/dist/lib/sandbox/workspace-view.js +56 -19
  45. package/dist/lib/task/commands/ls.js +3 -3
  46. package/dist/lib/task/control-authority.js +56 -264
  47. package/dist/lib/task/control-recovery.js +2 -3
  48. package/dist/lib/task/create.js +2 -2
  49. package/dist/lib/task/delegation-receipts.js +74 -161
  50. package/dist/lib/task/events.js +88 -44
  51. package/dist/lib/task/finalization.js +35 -6
  52. package/dist/lib/task/lifecycle.js +274 -115
  53. package/dist/lib/task/orchestration.js +201 -194
  54. package/dist/lib/task/runtime-paths.js +10 -0
  55. package/dist/lib/task/short-id.js +37 -27
  56. package/dist/lib/task/verification-engine.js +32 -26
  57. package/dist/lib/task/workspace-snapshot.js +1 -1
  58. package/dist/lifecycle-build-manifest.json +3 -3
  59. package/lib/agent-clients/adapter.ts +39 -0
  60. package/lib/agent-clients/adapters/claude-code.ts +5 -1
  61. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
  62. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
  63. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
  64. package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
  65. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
  66. package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
  67. package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
  68. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
  69. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
  70. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
  71. package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
  72. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
  73. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
  74. package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
  75. package/lib/agent-clients/adapters/codex.ts +44 -1
  76. package/lib/agent-clients/adapters/index.ts +7 -20
  77. package/lib/agent-clients/tokens.ts +4 -3
  78. package/lib/internal/agent-client.ts +142 -73
  79. package/lib/internal/cli-route-inventory.ts +2 -2
  80. package/lib/internal/sandbox-control.ts +0 -97
  81. package/lib/internal/task-finalization.ts +30 -18
  82. package/lib/internal/task-lifecycle.ts +1 -1
  83. package/lib/internal/task-operation-registry.ts +1 -1
  84. package/lib/internal/task-validate.ts +1 -1
  85. package/lib/platform/github-client.ts +34 -3
  86. package/lib/platform/issues.ts +18 -6
  87. package/lib/runtime/agent-runtime.ts +6 -41
  88. package/lib/sandbox/commands/control-status.ts +1 -0
  89. package/lib/sandbox/commands/create.ts +7 -1
  90. package/lib/sandbox/commands/ls.ts +4 -5
  91. package/lib/sandbox/commands/rm.ts +1 -1
  92. package/lib/sandbox/control/agent-operation.ts +49 -0
  93. package/lib/sandbox/control/client.ts +20 -254
  94. package/lib/sandbox/control/executor.ts +13 -136
  95. package/lib/sandbox/control/lifecycle.ts +67 -4
  96. package/lib/sandbox/control/protocol.ts +22 -66
  97. package/lib/sandbox/control/server.ts +21 -95
  98. package/lib/sandbox/control/task-view.ts +0 -19
  99. package/lib/sandbox/mounts.ts +10 -1
  100. package/lib/sandbox/recovery.ts +38 -17
  101. package/lib/sandbox/removal.ts +33 -7
  102. package/lib/sandbox/workspace-identity.ts +16 -6
  103. package/lib/sandbox/workspace-view.ts +50 -18
  104. package/lib/task/commands/ls.ts +3 -3
  105. package/lib/task/control-authority.ts +66 -378
  106. package/lib/task/control-recovery.ts +3 -4
  107. package/lib/task/create.ts +3 -3
  108. package/lib/task/delegation-receipts.ts +87 -231
  109. package/lib/task/events.ts +100 -42
  110. package/lib/task/finalization.ts +37 -7
  111. package/lib/task/lifecycle.ts +257 -109
  112. package/lib/task/orchestration.ts +260 -237
  113. package/lib/task/runtime-paths.ts +12 -0
  114. package/lib/task/short-id.ts +33 -40
  115. package/lib/task/verification-engine.ts +30 -25
  116. package/lib/task/workspace-snapshot.ts +1 -1
  117. package/package.json +1 -1
  118. package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
  119. package/templates/.agents/rules/README.en.md +2 -1
  120. package/templates/.agents/rules/README.zh-CN.md +2 -1
  121. package/templates/.agents/rules/next-step-output.en.md +6 -19
  122. package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
  123. package/templates/.agents/rules/strategy-pattern.en.md +25 -0
  124. package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
  125. package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
  126. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
  127. package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
  128. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
  129. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  130. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
  131. package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
  132. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
  133. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
  134. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
  135. package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
  136. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
  137. package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
  138. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
  139. package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
  140. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
  141. package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
  142. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
  143. package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
  144. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
  145. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  146. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  147. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  148. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  149. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
  150. package/dist/lib/internal/codex-lifecycle.js +0 -385
  151. package/dist/lib/task/codex-orchestration.js +0 -206
  152. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
  153. package/lib/internal/codex-lifecycle.ts +0 -390
  154. package/lib/task/codex-orchestration.ts +0 -276
@@ -18,47 +18,18 @@ import { inspectSandboxControlContainer } from './container-identity.js';
18
18
  import { acquireSandboxControlBrokerStartup, garbageCollectSandboxControlRoot, isSandboxControlRootQuiescing, readSandboxControlManifest } from './lifecycle.js';
19
19
  import { nextSandboxControlBackoff } from './timing.js';
20
20
  import { readTaskFinalizationReceipt } from '../../task/finalization.js';
21
- import { readCodexControllerRegistration } from './controller-registration.js';
21
+ import { recoverAgentClientOperation, validateAgentClientOperation } from './agent-operation.js';
22
22
  import { validateSandboxControlIdentity } from './identity-sentinel.js';
23
- import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
23
+ import { mergeSandboxTaskView, taskViewForManifest } from './task-view.js';
24
24
  import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
25
- async function taskViewForFinalizationResult(manifest, request, result) {
26
- if (request.family !== 'task-finalization' || result?.exitCode !== 0 || !manifest.taskId)
27
- return null;
28
- let receipt = null;
29
- for (let attempt = 0; attempt < 100 && !receipt; attempt += 1) {
30
- try {
31
- receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
32
- }
33
- catch {
34
- // A missing or temporarily moving task directory is retried before publishing completion.
35
- }
36
- if (!receipt)
37
- await new Promise((resolve) => setTimeout(resolve, 10));
38
- }
39
- const finalized = taskViewAfterFinalization({
40
- taskId: manifest.taskId,
41
- generation: manifest.generation,
42
- requestId: request.id,
43
- receipt
44
- });
45
- if (finalized.state === 'unknown')
46
- return finalized;
47
- const canonical = taskViewForManifest({ ...manifest, receipt });
48
- return canonical.state === 'current' && canonical.observedSource === 'completed'
49
- ? canonical
50
- : finalized;
51
- }
52
25
  function appendBrokerAudit(manifest, event, fields = {}) {
53
26
  appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
54
27
  }
55
28
  function operationKey(request) {
56
- if (request.family === 'task-finalization')
57
- return request.operation;
58
29
  if (request.family === 'task-create')
59
30
  return 'create';
60
- if (request.family === 'codex-controller')
61
- return request.command;
31
+ if (request.family === 'agent-client')
32
+ return request.operation;
62
33
  if (request.family !== 'task-lifecycle')
63
34
  return null;
64
35
  try {
@@ -217,51 +188,12 @@ function taskCreateDomainEvidence(manifest, output) {
217
188
  return { consistent: false };
218
189
  }
219
190
  }
220
- function controllerDomainEvidence(manifest, manifestPath, request, output) {
221
- if (!output || output.error !== null || typeof output.status !== 'string')
222
- return { consistent: false };
223
- try {
224
- const registration = readCodexControllerRegistration(manifestPath);
225
- if (request.family !== 'codex-controller')
226
- return { consistent: false };
227
- if (request.command === 'open') {
228
- const lease = output.lease && typeof output.lease === 'object' && !Array.isArray(output.lease)
229
- ? output.lease : null;
230
- return {
231
- consistent: output.status === 'opened' && output.changed === true
232
- && lease?.taskId === registration.taskId
233
- && lease.controlGeneration === registration.controlGeneration
234
- && lease.controllerInstanceDigest === registration.controllerInstanceDigest
235
- && registration.taskId === manifest.taskId
236
- && registration.controlGeneration === manifest.generation
237
- };
238
- }
239
- if (request.command === 'verify') {
240
- const binding = output.binding && typeof output.binding === 'object' && !Array.isArray(output.binding)
241
- ? output.binding : null;
242
- return {
243
- consistent: output.status === 'verified' && output.changed === false
244
- && binding?.taskId === registration.taskId
245
- && binding.controlGeneration === registration.controlGeneration
246
- && binding.controllerInstanceDigest === registration.controllerInstanceDigest
247
- };
248
- }
249
- return { consistent: false };
250
- }
251
- catch (error) {
252
- return { consistent: request.family === 'codex-controller' && request.command === 'close'
253
- && error.code === 'CODEX_SANDBOX_CONTROLLER_REGISTRATION_MISSING'
254
- && output?.status === 'closed' && typeof output.changed === 'boolean' };
255
- }
256
- }
257
191
  async function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
258
192
  if (!operation)
259
193
  return { domain: null };
260
- const taskRef = request.family === 'task-finalization'
261
- ? manifest.taskId
262
- : 'args' in request ? request.args[0] ?? null : manifest.taskId;
194
+ const taskRef = 'args' in request ? request.args[0] ?? null : manifest.taskId;
263
195
  const output = parseControlOutput(payloadOutput);
264
- if (!taskRef && operation.family !== 'task-create' && operation.family !== 'codex-controller') {
196
+ if (!taskRef && operation.family !== 'task-create' && operation.family !== 'agent-client') {
265
197
  return { domain: null };
266
198
  }
267
199
  if (operation.family === 'task-lifecycle') {
@@ -289,8 +221,13 @@ async function readRecoveryDomain(manifest, manifestPath, request, operation, te
289
221
  if (operation.family === 'task-create') {
290
222
  return { domain: taskCreateDomainEvidence(manifest, output) };
291
223
  }
292
- if (operation.family === 'codex-controller') {
293
- return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
224
+ if (operation.family === 'agent-client' && request.family === 'agent-client') {
225
+ return { domain: recoverAgentClientOperation({
226
+ request,
227
+ manifest,
228
+ manifestPath,
229
+ stdout: payloadOutput
230
+ }) };
294
231
  }
295
232
  return { domain: null };
296
233
  }
@@ -509,6 +446,8 @@ async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
509
446
  request = validateSandboxControlRequest(rawRequest, manifest, {
510
447
  now: typeof rawRequest.issuedAt === 'number' ? rawRequest.issuedAt : undefined
511
448
  });
449
+ if (request.family === 'agent-client')
450
+ validateAgentClientOperation(request, manifest);
512
451
  }
513
452
  catch {
514
453
  // Missing or malformed request evidence remains fail-closed below.
@@ -760,6 +699,8 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
760
699
  try {
761
700
  if (!brokerOwns())
762
701
  return;
702
+ if (isSandboxControlRootQuiescing(root))
703
+ return;
763
704
  if (taskView.state === 'current' && taskView.observedSource === 'completed') {
764
705
  // Recheck durable re-entry evidence before publishing a trusted startup view.
765
706
  const unverified = { ...taskView, state: 'unknown',
@@ -866,11 +807,6 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
866
807
  settledExecution = active;
867
808
  active = null;
868
809
  }
869
- const finalizationView = settledExecution
870
- ? await taskViewForFinalizationResult(manifest, settledExecution.request, settledExecution.result)
871
- : null;
872
- if (finalizationView)
873
- taskView = finalizationView;
874
810
  let reasonCode = null;
875
811
  try {
876
812
  if (!brokerOwns())
@@ -948,228 +884,232 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
948
884
  fs.rmSync(path.join(manifest.processingDir, settledExecution.request.id), { recursive: true, force: true });
949
885
  }
950
886
  let retiring = false;
951
- for (const name of fs.readdirSync(requestsDir).sort()) {
952
- if (!/^[a-f0-9-]{16,64}\.json$/.test(name))
953
- continue;
954
- const id = name.slice(0, -5);
955
- const source = path.join(requestsDir, name);
956
- let claimed = null;
957
- let validatedRequest = null;
958
- let prepared = null;
959
- try {
960
- if (!brokerOwns()) {
961
- retiring = true;
962
- break;
963
- }
964
- claimed = claimRequest(manifest, source, id);
965
- appendBrokerAudit(manifest, 'request-claimed', {
966
- requestId: id,
967
- requestSourcePath: source,
968
- requestClaimedPath: claimed,
969
- requestProcessingDir: path.dirname(claimed)
970
- });
971
- if (!brokerOwns()) {
972
- retiring = true;
973
- break;
974
- }
975
- consumeRequest(consumedDir, id);
976
- if (!brokerOwns()) {
977
- retiring = true;
978
- break;
979
- }
980
- if (reasonCode)
981
- throw new Error(reasonCode);
982
- if (active)
983
- throw new Error('SANDBOX_CONTROL_BUSY');
984
- const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
985
- validatedRequest = request;
986
- criticalRequestPhase(manifest, request, 'validated', 'in-progress');
987
- appendBrokerAudit(manifest, 'request-validated', {
988
- ...requestAuditFields(manifest, request),
989
- requestPath: claimed
990
- });
991
- if (bindingCheck(manifest))
992
- throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
993
- if (readActiveLease(manifest))
994
- throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
995
- criticalRequestPhase(manifest, request, 'gated', 'in-progress');
996
- appendBrokerAudit(manifest, 'request-gates-passed', {
997
- ...requestAuditFields(manifest, request),
998
- requestPath: claimed,
999
- bindingChecked: true,
1000
- handoffLeaseChecked: true
1001
- });
1002
- criticalRequestPhase(manifest, request, 'reserved', 'in-progress');
1003
- writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
1004
- appendBrokerAudit(manifest, 'executor-reservation-written', {
1005
- ...requestAuditFields(manifest, request),
1006
- requestPath: claimed,
1007
- executionPath: executionPath(manifest, request.id)
1008
- });
1009
- appendBrokerAudit(manifest, 'executor-prepare-start', {
1010
- ...requestAuditFields(manifest, request),
1011
- requestPath: claimed,
1012
- executorCwd: manifest.repoRoot,
1013
- executorEntry: options.internalCliPath ?? process.argv[1] ?? null
1014
- });
1015
- prepared = await prepareExecution({
1016
- manifest, manifestPath, request, requestPath: claimed,
1017
- internalCliPath: options.internalCliPath ?? process.argv[1]
1018
- });
1019
- const preparedExecution = prepared;
1020
- if (!preparedExecution)
1021
- throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
1022
- criticalRequestPhase(manifest, request, 'prepared', 'in-progress', `${preparedExecution.execution.child.pid}`);
1023
- appendBrokerAudit(manifest, 'executor-prepared', {
1024
- ...requestAuditFields(manifest, request),
1025
- requestPath: claimed,
1026
- executorCwd: manifest.repoRoot,
1027
- childPid: preparedExecution.execution.child.pid,
1028
- childStartTime: preparedExecution.execution.child.startTime,
1029
- childProcessGroupId: preparedExecution.execution.child.processGroupId,
1030
- executionPath: executionPath(manifest, request.id)
1031
- });
1032
- const execution = {
1033
- request, prepared: preparedExecution, result: null,
1034
- resultEvidenceWritten: false, failure: null, settled: false
1035
- };
1036
- active = execution;
1037
- preparedExecution.completion.then((result) => {
1038
- execution.result = sanitizeSandboxControlResult(manifest, result);
1039
- appendBrokerAudit(manifest, 'executor-completed', {
887
+ const quiescing = isSandboxControlRootQuiescing(root);
888
+ if (quiescing && !active)
889
+ break;
890
+ if (!quiescing)
891
+ for (const name of fs.readdirSync(requestsDir).sort()) {
892
+ if (!/^[a-f0-9-]{16,64}\.json$/.test(name))
893
+ continue;
894
+ const id = name.slice(0, -5);
895
+ const source = path.join(requestsDir, name);
896
+ let claimed = null;
897
+ let validatedRequest = null;
898
+ let prepared = null;
899
+ try {
900
+ if (isSandboxControlRootQuiescing(root)) {
901
+ retiring = true;
902
+ break;
903
+ }
904
+ if (!brokerOwns()) {
905
+ retiring = true;
906
+ break;
907
+ }
908
+ claimed = claimRequest(manifest, source, id);
909
+ appendBrokerAudit(manifest, 'request-claimed', {
910
+ requestId: id,
911
+ requestSourcePath: source,
912
+ requestClaimedPath: claimed,
913
+ requestProcessingDir: path.dirname(claimed)
914
+ });
915
+ if (!brokerOwns()) {
916
+ retiring = true;
917
+ break;
918
+ }
919
+ consumeRequest(consumedDir, id);
920
+ if (!brokerOwns()) {
921
+ retiring = true;
922
+ break;
923
+ }
924
+ if (reasonCode)
925
+ throw new Error(reasonCode);
926
+ if (active)
927
+ throw new Error('SANDBOX_CONTROL_BUSY');
928
+ const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
929
+ if (request.family === 'agent-client')
930
+ validateAgentClientOperation(request, manifest);
931
+ validatedRequest = request;
932
+ criticalRequestPhase(manifest, request, 'validated', 'in-progress');
933
+ appendBrokerAudit(manifest, 'request-validated', {
934
+ ...requestAuditFields(manifest, request),
935
+ requestPath: claimed
936
+ });
937
+ if (bindingCheck(manifest))
938
+ throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
939
+ if (readActiveLease(manifest))
940
+ throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
941
+ criticalRequestPhase(manifest, request, 'gated', 'in-progress');
942
+ appendBrokerAudit(manifest, 'request-gates-passed', {
943
+ ...requestAuditFields(manifest, request),
944
+ requestPath: claimed,
945
+ bindingChecked: true,
946
+ handoffLeaseChecked: true
947
+ });
948
+ criticalRequestPhase(manifest, request, 'reserved', 'in-progress');
949
+ writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
950
+ appendBrokerAudit(manifest, 'executor-reservation-written', {
951
+ ...requestAuditFields(manifest, request),
952
+ requestPath: claimed,
953
+ executionPath: executionPath(manifest, request.id)
954
+ });
955
+ appendBrokerAudit(manifest, 'executor-prepare-start', {
1040
956
  ...requestAuditFields(manifest, request),
1041
- ...resultAuditFields(execution.result),
957
+ requestPath: claimed,
958
+ executorCwd: manifest.repoRoot,
959
+ executorEntry: options.internalCliPath ?? process.argv[1] ?? null
960
+ });
961
+ prepared = await prepareExecution({
962
+ manifest, manifestPath, request, requestPath: claimed,
963
+ internalCliPath: options.internalCliPath ?? process.argv[1]
964
+ });
965
+ const preparedExecution = prepared;
966
+ if (!preparedExecution)
967
+ throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
968
+ criticalRequestPhase(manifest, request, 'prepared', 'in-progress', `${preparedExecution.execution.child.pid}`);
969
+ appendBrokerAudit(manifest, 'executor-prepared', {
970
+ ...requestAuditFields(manifest, request),
971
+ requestPath: claimed,
972
+ executorCwd: manifest.repoRoot,
1042
973
  childPid: preparedExecution.execution.child.pid,
1043
- childStartTime: preparedExecution.execution.child.startTime
974
+ childStartTime: preparedExecution.execution.child.startTime,
975
+ childProcessGroupId: preparedExecution.execution.child.processGroupId,
976
+ executionPath: executionPath(manifest, request.id)
1044
977
  });
1045
- try {
1046
- writeSandboxControlResultEvidence(manifest, request.id, execution.result);
1047
- execution.resultEvidenceWritten = true;
1048
- execution.settled = true;
1049
- appendBrokerAudit(manifest, 'executor-result-evidence-written', {
978
+ const execution = {
979
+ request, prepared: preparedExecution, result: null,
980
+ resultEvidenceWritten: false, failure: null, settled: false
981
+ };
982
+ active = execution;
983
+ preparedExecution.completion.then((result) => {
984
+ execution.result = sanitizeSandboxControlResult(manifest, result);
985
+ appendBrokerAudit(manifest, 'executor-completed', {
1050
986
  ...requestAuditFields(manifest, request),
1051
- resultPath: path.join(manifest.processingDir, request.id, 'result.json'),
1052
- ...resultAuditFields(execution.result)
987
+ ...resultAuditFields(execution.result),
988
+ childPid: preparedExecution.execution.child.pid,
989
+ childStartTime: preparedExecution.execution.child.startTime
1053
990
  });
1054
- }
1055
- catch (error) {
991
+ try {
992
+ writeSandboxControlResultEvidence(manifest, request.id, execution.result);
993
+ execution.resultEvidenceWritten = true;
994
+ execution.settled = true;
995
+ appendBrokerAudit(manifest, 'executor-result-evidence-written', {
996
+ ...requestAuditFields(manifest, request),
997
+ resultPath: path.join(manifest.processingDir, request.id, 'result.json'),
998
+ ...resultAuditFields(execution.result)
999
+ });
1000
+ }
1001
+ catch (error) {
1002
+ execution.failure = error;
1003
+ appendBrokerAudit(manifest, 'executor-result-evidence-failed', {
1004
+ ...requestAuditFields(manifest, request),
1005
+ errorType: error instanceof Error ? error.name : typeof error,
1006
+ });
1007
+ }
1008
+ }, (error) => {
1056
1009
  execution.failure = error;
1057
- appendBrokerAudit(manifest, 'executor-result-evidence-failed', {
1010
+ execution.settled = true;
1011
+ appendBrokerAudit(manifest, 'executor-failed', {
1058
1012
  ...requestAuditFields(manifest, request),
1013
+ childPid: preparedExecution.execution.child.pid,
1014
+ childStartTime: preparedExecution.execution.child.startTime,
1059
1015
  errorType: error instanceof Error ? error.name : typeof error,
1060
1016
  });
1061
- }
1062
- }, (error) => {
1063
- execution.failure = error;
1064
- execution.settled = true;
1065
- appendBrokerAudit(manifest, 'executor-failed', {
1066
- ...requestAuditFields(manifest, request),
1067
- childPid: preparedExecution.execution.child.pid,
1068
- childStartTime: preparedExecution.execution.child.startTime,
1069
- errorType: error instanceof Error ? error.name : typeof error,
1070
1017
  });
1071
- });
1072
- if (!brokerOwns()) {
1073
- prepared.terminate(false);
1074
- active = null;
1075
- retiring = true;
1076
- continue;
1077
- }
1078
- if (!brokerOwns()) {
1079
- prepared.terminate(false);
1080
- active = null;
1081
- retiring = true;
1082
- continue;
1083
- }
1084
- criticalRequestPhase(manifest, request, 'accepted-authorized', 'in-progress');
1085
- writeAcceptedResponse(manifest, {
1086
- version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
1087
- });
1088
- writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'accepted-committed' });
1089
- appendBrokerAudit(manifest, 'request-accepted', {
1090
- ...requestAuditFields(manifest, request),
1091
- acceptedPath: acceptedResponsePath(manifest, id),
1092
- executionPhase: 'prepared'
1093
- });
1094
- if (!brokerOwns()) {
1095
- prepared.terminate(false);
1096
- active = null;
1097
- retiring = true;
1098
- break;
1099
- }
1100
- try {
1101
- criticalRequestPhase(manifest, request, 'start-authorized', 'in-progress', `${prepared.execution.child.pid}`);
1102
- appendBrokerAudit(manifest, 'executor-start', {
1103
- ...requestAuditFields(manifest, request),
1104
- childPid: prepared.execution.child.pid,
1105
- childStartTime: prepared.execution.child.startTime,
1106
- executionPath: executionPath(manifest, request.id)
1018
+ if (!brokerOwns()) {
1019
+ prepared.terminate(false);
1020
+ active = null;
1021
+ retiring = true;
1022
+ continue;
1023
+ }
1024
+ criticalRequestPhase(manifest, request, 'accepted-authorized', 'in-progress');
1025
+ writeAcceptedResponse(manifest, {
1026
+ version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
1107
1027
  });
1108
- prepared.start(brokerOwns);
1109
- }
1110
- catch (error) {
1111
- appendBrokerAudit(manifest, 'executor-start-failed', {
1028
+ writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'accepted-committed' });
1029
+ appendBrokerAudit(manifest, 'request-accepted', {
1112
1030
  ...requestAuditFields(manifest, request),
1113
- errorType: error instanceof Error ? error.name : typeof error,
1031
+ acceptedPath: acceptedResponsePath(manifest, id),
1032
+ executionPhase: 'prepared'
1114
1033
  });
1115
- const owned = brokerOwns();
1116
- prepared.terminate(owned);
1117
- if (!owned || !brokerOwns()) {
1034
+ if (!brokerOwns()) {
1035
+ prepared.terminate(false);
1118
1036
  active = null;
1119
1037
  retiring = true;
1120
1038
  break;
1121
1039
  }
1122
- writeSandboxControlResponse(manifest, unknown(id));
1123
- if (!brokerOwns()) {
1040
+ try {
1041
+ criticalRequestPhase(manifest, request, 'start-authorized', 'in-progress', `${prepared.execution.child.pid}`);
1042
+ appendBrokerAudit(manifest, 'executor-start', {
1043
+ ...requestAuditFields(manifest, request),
1044
+ childPid: prepared.execution.child.pid,
1045
+ childStartTime: prepared.execution.child.startTime,
1046
+ executionPath: executionPath(manifest, request.id)
1047
+ });
1048
+ prepared.start(brokerOwns);
1049
+ }
1050
+ catch (error) {
1051
+ appendBrokerAudit(manifest, 'executor-start-failed', {
1052
+ ...requestAuditFields(manifest, request),
1053
+ errorType: error instanceof Error ? error.name : typeof error,
1054
+ });
1055
+ const owned = brokerOwns();
1056
+ prepared.terminate(owned);
1057
+ if (!owned || !brokerOwns()) {
1058
+ active = null;
1059
+ retiring = true;
1060
+ break;
1061
+ }
1062
+ writeSandboxControlResponse(manifest, unknown(id));
1063
+ if (!brokerOwns()) {
1064
+ active = null;
1065
+ retiring = true;
1066
+ break;
1067
+ }
1068
+ removeAcceptedResponse(manifest, id);
1069
+ fs.rmSync(path.join(manifest.processingDir, id), { recursive: true, force: true });
1124
1070
  active = null;
1071
+ claimed = null;
1072
+ if (!brokerOwns()) {
1073
+ retiring = true;
1074
+ break;
1075
+ }
1076
+ appendBrokerAudit(manifest, 'executor-gate-failed', { requestId: id });
1077
+ continue;
1078
+ }
1079
+ }
1080
+ catch (error) {
1081
+ if (prepared) {
1082
+ try {
1083
+ prepared.terminate(brokerOwns());
1084
+ }
1085
+ catch { /* cleanup is best effort after a failed admission */ }
1086
+ prepared = null;
1087
+ active = null;
1088
+ }
1089
+ if (!brokerOwns()) {
1125
1090
  retiring = true;
1126
1091
  break;
1127
1092
  }
1128
- removeAcceptedResponse(manifest, id);
1129
- fs.rmSync(path.join(manifest.processingDir, id), { recursive: true, force: true });
1130
- active = null;
1131
- claimed = null;
1093
+ const detail = controlError(error);
1094
+ appendBrokerAudit(manifest, 'request-rejected', {
1095
+ requestId: id,
1096
+ requestFamily: validatedRequest?.family ?? null,
1097
+ sandboxTaskId: manifest.taskId,
1098
+ requestPath: claimed,
1099
+ errorCode: detail.code,
1100
+ errorRetryable: detail.retryable,
1101
+ errorType: error instanceof Error ? error.name : typeof error,
1102
+ });
1103
+ writeSandboxControlResponse(manifest, rejected(id, error));
1132
1104
  if (!brokerOwns()) {
1133
1105
  retiring = true;
1134
1106
  break;
1135
1107
  }
1136
- appendBrokerAudit(manifest, 'executor-gate-failed', { requestId: id });
1137
- continue;
1138
- }
1139
- }
1140
- catch (error) {
1141
- if (prepared) {
1142
- try {
1143
- prepared.terminate(brokerOwns());
1144
- }
1145
- catch { /* cleanup is best effort after a failed admission */ }
1146
- prepared = null;
1147
- active = null;
1148
- }
1149
- if (!brokerOwns()) {
1150
- retiring = true;
1151
- break;
1152
- }
1153
- const detail = controlError(error);
1154
- appendBrokerAudit(manifest, 'request-rejected', {
1155
- requestId: id,
1156
- requestFamily: validatedRequest?.family ?? null,
1157
- sandboxTaskId: manifest.taskId,
1158
- requestPath: claimed,
1159
- errorCode: detail.code,
1160
- errorRetryable: detail.retryable,
1161
- errorType: error instanceof Error ? error.name : typeof error,
1162
- });
1163
- writeSandboxControlResponse(manifest, rejected(id, error));
1164
- if (!brokerOwns()) {
1165
- retiring = true;
1166
- break;
1108
+ removeAcceptedResponse(manifest, id);
1109
+ if (claimed)
1110
+ fs.rmSync(path.dirname(claimed), { recursive: true, force: true });
1167
1111
  }
1168
- removeAcceptedResponse(manifest, id);
1169
- if (claimed)
1170
- fs.rmSync(path.dirname(claimed), { recursive: true, force: true });
1171
1112
  }
1172
- }
1173
1113
  if (retiring || !brokerOwns())
1174
1114
  break;
1175
1115
  await delay(timing.controlTickMs);
@@ -1191,11 +1131,6 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
1191
1131
  }
1192
1132
  }
1193
1133
  if (owned && active.result && active.resultEvidenceWritten) {
1194
- const finalizationView = await taskViewForFinalizationResult(manifest, active.request, active.result);
1195
- if (finalizationView) {
1196
- taskView = finalizationView;
1197
- writeSandboxControlStatus(manifest, broker, 'healthy', null, null, Date.now(), taskView);
1198
- }
1199
1134
  if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
1200
1135
  if (brokerOwns()) {
1201
1136
  removeAcceptedResponse(manifest, active.request.id);
@@ -130,19 +130,6 @@ export function projectSandboxTaskView(input) {
130
130
  reasonCode: receipt ? 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED' : 'SANDBOX_TASK_VIEW_EVIDENCE_UNAVAILABLE'
131
131
  };
132
132
  }
133
- export function taskViewAfterFinalization(params) {
134
- const receipt = receiptIdentity(params.receipt, params.generation, params.requestId);
135
- if (!receipt) {
136
- return {
137
- state: 'unknown', taskId: params.taskId, observedSource: 'unknown', receipt: null,
138
- reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
139
- };
140
- }
141
- return {
142
- state: 'finalized-stale', taskId: params.taskId, observedSource: 'active', receipt,
143
- reasonCode: 'SANDBOX_TASK_VIEW_FINALIZED'
144
- };
145
- }
146
133
  export function mergeSandboxTaskView(canonical, previous) {
147
134
  if (previous.state !== 'finalized-stale' && previous.state !== 'unknown')
148
135
  return canonical;
@@ -38,11 +38,19 @@ export function sandboxCoreBindMounts(config, branch, overrides) {
38
38
  ...workspaceMounts,
39
39
  ];
40
40
  if (taskBound) {
41
+ const taskMountPath = path.posix.join('/workspace/.agents/workspace/active', overrides.taskId);
41
42
  mounts.push({
42
43
  hostPaths: taskSources,
43
- containerPath: path.posix.join('/workspace/.agents/workspace/active', overrides.taskId),
44
+ containerPath: taskMountPath,
44
45
  readOnly: false
45
46
  });
47
+ if (overrides.completedTaskSource) {
48
+ mounts.push({
49
+ hostPaths: [overrides.completedTaskSource],
50
+ containerPath: path.posix.join('/workspace/.agents/workspace/completed', overrides.taskId),
51
+ readOnly: false
52
+ });
53
+ }
46
54
  }
47
55
  mounts.push({
48
56
  hostPaths: [shareCommonDir(config)],