@fitlab-ai/agent-infra 0.11.1 → 0.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/bin/internal-cli.js +7 -2
  2. package/dist/lib/agent-clients/adapter.js +3 -0
  3. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
  6. package/dist/lib/agent-clients/adapters/codex.js +10 -0
  7. package/dist/lib/internal/cli-route-inventory.js +1 -1
  8. package/dist/lib/internal/manual-validation.js +22 -27
  9. package/dist/lib/internal/platform-comment.js +59 -0
  10. package/dist/lib/internal/sandbox-control.js +2 -171
  11. package/dist/lib/internal/task-artifact.js +1 -1
  12. package/dist/lib/internal/task-lifecycle.js +1 -1
  13. package/dist/lib/internal/task-operation-registry.js +7 -2
  14. package/dist/lib/internal/task-review.js +5 -5
  15. package/dist/lib/internal/task-validate.js +0 -6
  16. package/dist/lib/platform/comment-safety.js +7 -1
  17. package/dist/lib/platform/github-provider.js +13 -3
  18. package/dist/lib/platform/issue-comments.js +93 -15
  19. package/dist/lib/platform/pr-summary.js +7 -2
  20. package/dist/lib/platform/provider-contract.d.ts +2 -0
  21. package/dist/lib/platform/provider-validation.js +7 -2
  22. package/dist/lib/platform/verification-sync.js +19 -2
  23. package/dist/lib/sandbox/control/client.js +3 -44
  24. package/dist/lib/sandbox/control/executor.js +14 -117
  25. package/dist/lib/sandbox/control/finalization-response.js +8 -2
  26. package/dist/lib/sandbox/control/protocol.js +5 -64
  27. package/dist/lib/sandbox/control/server.js +29 -206
  28. package/dist/lib/sandbox/control/state.js +1 -7
  29. package/dist/lib/sandbox/control/task-view.js +2 -1
  30. package/dist/lib/task/artifact-command.js +6 -6
  31. package/dist/lib/task/codex-orchestration.js +10 -208
  32. package/dist/lib/task/commit-operation.js +3 -18
  33. package/dist/lib/task/control-authority.js +35 -44
  34. package/dist/lib/task/control-recovery.js +3 -42
  35. package/dist/lib/task/delegation-receipts.js +16 -78
  36. package/dist/lib/task/events.js +89 -237
  37. package/dist/lib/task/finalization-artifacts.js +1 -3
  38. package/dist/lib/task/finalization-state.js +2 -1
  39. package/dist/lib/task/finalization.js +208 -57
  40. package/dist/lib/task/lifecycle-execution.js +2 -11
  41. package/dist/lib/task/lifecycle.js +1 -1
  42. package/dist/lib/task/local-artifact-finalization.js +25 -242
  43. package/dist/lib/task/manual-validation-receipt.js +8 -3
  44. package/dist/lib/task/orchestration.js +12 -24
  45. package/dist/lib/task/review-command.js +0 -1
  46. package/dist/lib/task/review-finalization.js +46 -211
  47. package/dist/lifecycle-build-manifest.json +3 -3
  48. package/lib/agent-clients/adapter.ts +46 -0
  49. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
  50. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
  51. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
  52. package/lib/agent-clients/adapters/codex.ts +10 -0
  53. package/lib/internal/cli-route-inventory.ts +1 -1
  54. package/lib/internal/manual-validation.ts +22 -26
  55. package/lib/internal/platform-comment.ts +38 -0
  56. package/lib/internal/sandbox-control.ts +1 -185
  57. package/lib/internal/task-artifact.ts +1 -1
  58. package/lib/internal/task-lifecycle.ts +2 -2
  59. package/lib/internal/task-operation-registry.ts +8 -4
  60. package/lib/internal/task-review.ts +5 -5
  61. package/lib/internal/task-validate.ts +0 -5
  62. package/lib/platform/comment-safety.ts +8 -0
  63. package/lib/platform/github-provider.ts +11 -2
  64. package/lib/platform/issue-comments.ts +102 -17
  65. package/lib/platform/pr-summary.ts +10 -2
  66. package/lib/platform/provider-contract.ts +2 -0
  67. package/lib/platform/provider-validation.ts +6 -2
  68. package/lib/platform/verification-sync.ts +19 -2
  69. package/lib/sandbox/control/client.ts +5 -68
  70. package/lib/sandbox/control/executor.ts +14 -125
  71. package/lib/sandbox/control/finalization-response.ts +8 -2
  72. package/lib/sandbox/control/protocol.ts +7 -83
  73. package/lib/sandbox/control/server.ts +30 -220
  74. package/lib/sandbox/control/state.ts +1 -9
  75. package/lib/sandbox/control/task-view.ts +2 -1
  76. package/lib/task/artifact-command.ts +6 -7
  77. package/lib/task/codex-orchestration.ts +8 -241
  78. package/lib/task/commit-operation.ts +3 -23
  79. package/lib/task/control-authority.ts +39 -55
  80. package/lib/task/control-recovery.ts +3 -43
  81. package/lib/task/delegation-receipts.ts +14 -81
  82. package/lib/task/events.ts +106 -255
  83. package/lib/task/finalization-artifacts.ts +1 -3
  84. package/lib/task/finalization-state.ts +2 -1
  85. package/lib/task/finalization.ts +221 -65
  86. package/lib/task/lifecycle-execution.ts +10 -21
  87. package/lib/task/lifecycle.ts +2 -2
  88. package/lib/task/local-artifact-finalization.ts +23 -281
  89. package/lib/task/manual-validation-receipt.ts +8 -2
  90. package/lib/task/orchestration.ts +30 -53
  91. package/lib/task/review-command.ts +0 -1
  92. package/lib/task/review-finalization.ts +45 -193
  93. package/package.json +2 -2
  94. package/templates/.agents/rules/lifecycle-orchestration.en.md +23 -9
  95. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +23 -9
  96. package/templates/.agents/rules/local-artifact-repair.en.md +14 -60
  97. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -60
  98. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
  99. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  100. package/templates/.agents/skills/code-task/SKILL.en.md +3 -3
  101. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
  102. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
  103. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
  104. package/templates/.agents/skills/complete-task/SKILL.en.md +7 -8
  105. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +7 -8
  106. package/templates/.agents/skills/plan-task/SKILL.en.md +3 -3
  107. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
  108. package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
  109. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
  110. package/templates/.agents/skills/review-code/SKILL.en.md +3 -3
  111. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +3 -3
  112. package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
  113. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
  114. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
  115. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
  116. package/templates/.agents/skills/run-task/SKILL.en.md +10 -9
  117. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +10 -9
  118. package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
  119. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
  120. package/dist/lib/sandbox/control/task-workflow.js +0 -74
  121. package/dist/lib/sandbox/control/workflow-executor.js +0 -157
  122. package/dist/lib/task/lifecycle-recovery.js +0 -549
  123. package/dist/lib/task/recovery-warning.js +0 -19
  124. package/dist/lib/task/workflow-command.js +0 -18
  125. package/dist/lib/task/workflow-dispatch.js +0 -161
  126. package/lib/sandbox/control/task-workflow.ts +0 -95
  127. package/lib/sandbox/control/workflow-executor.ts +0 -183
  128. package/lib/task/lifecycle-recovery.ts +0 -691
  129. package/lib/task/recovery-warning.ts +0 -21
  130. package/lib/task/workflow-command.ts +0 -19
  131. package/lib/task/workflow-dispatch.ts +0 -171
@@ -13,7 +13,7 @@ import { planInLabelUpdate, validateInLabelMapping } from "./in-label-sync.ts";
13
13
  import { readPrDeliveryFact } from "../task/pr-delivery-fact.ts";
14
14
  import { providerError, providerOperationContext, resourceIdentityNumber, unsupportedProviderOperation } from "./provider-bridge.ts";
15
15
  import { taskIssueIdentity } from "./task-identities.ts";
16
- import { renderTaskCommentResult } from "./issue-comments.ts";
16
+ import { MARKERS, renderTaskCommentResult } from "./issue-comments.ts";
17
17
  import {
18
18
  CONTROL_MARKER_PATTERN,
19
19
  canonicalizeCommentBody,
@@ -279,7 +279,9 @@ async function fetchRemoteData(context: any, shared: VerificationShared): Promis
279
279
  }
280
280
  return {
281
281
  issue,
282
- comments: facts.value.comments.map((comment: { id: string; body: string }) => ({ id: comment.id, body: comment.body })),
282
+ comments: facts.value.comments.map((comment: { id: string; body: string; createdSequence: number | null }) => ({
283
+ id: comment.id, body: comment.body, createdSequence: comment.createdSequence
284
+ })),
283
285
  prComments,
284
286
  prLabels: changeRequest?.labels || null,
285
287
  issueType: issueSnapshot
@@ -361,6 +363,21 @@ function checkCommentMarker(context: any, remoteData: any, shared: VerificationS
361
363
 
362
364
  const comment = findCommentByMarker(remoteData.comments, context.marker);
363
365
  if (comment) {
366
+ if (context.marker === MARKERS.summary(context.task.metadata.id)) {
367
+ const managed = remoteData.comments.filter((candidate: any) => {
368
+ const first = String(candidate.body || '').replace(/\r\n/g, '\n').split('\n', 1)[0] || '';
369
+ return first === MARKERS.task(context.task.metadata.id)
370
+ || first === MARKERS.summary(context.task.metadata.id)
371
+ || first.startsWith(`<!-- sync-issue:${context.task.metadata.id}:`);
372
+ });
373
+ if (managed.some((candidate: any) => !Number.isSafeInteger(candidate.createdSequence) || candidate.createdSequence < 1)) {
374
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} summary comment ordering is not provable`, "check_failed");
375
+ }
376
+ const summarySequence = comment.createdSequence;
377
+ if (!managed.every((candidate: any) => summarySequence >= candidate.createdSequence)) {
378
+ return shared.failResult(CHECK_TYPE, `Issue #${context.issueNumber} summary comment is not last among task-managed comments`, "check_failed");
379
+ }
380
+ }
364
381
  return shared.passResult(CHECK_TYPE, `Issue #${context.issueNumber} has expected comment marker`);
365
382
  }
366
383
 
@@ -11,10 +11,8 @@ import {
11
11
  type SandboxControlRequest,
12
12
  type SandboxControlResponse,
13
13
  type SandboxTaskCreateRequest,
14
- type SandboxTaskCommandRequest,
15
14
  type SandboxTaskFinalizationRequest,
16
- type SandboxCodexControllerRequest,
17
- type SandboxTaskWorkflowRequest
15
+ type SandboxCodexControllerRequest
18
16
  } from './protocol.ts';
19
17
  import type {
20
18
  CodexControllerLeaseProofV1,
@@ -26,8 +24,6 @@ import { readSandboxControlPayload, readSandboxControlStatus } from './state.ts'
26
24
  import type { TaskCreateCandidateV1 } from '../../task/create.ts';
27
25
  import { accessSandboxTaskView, taskViewFromStatus, type TaskViewAccessEffect } from './task-view.ts';
28
26
  import { readSandboxControlIdentitySentinel } from './identity-sentinel.ts';
29
- import type { TaskWorkflowRequest } from './task-workflow.ts';
30
- import type { LifecycleAuthorityRequestV1 } from '../../task/control-authority.ts';
31
27
  import { configuredShortIdLength, resolveShortIdReadOnly } from '../../task/short-id.ts';
32
28
 
33
29
  const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
@@ -119,8 +115,6 @@ function preflight(
119
115
 
120
116
  function taskViewEffectForRequest(request: SandboxControlRequest): TaskViewAccessEffect | null {
121
117
  if (request.family === 'task-lifecycle' || request.family === 'task-finalization') return 'progress';
122
- if (request.family === 'task-orchestration') return request.args[1] === 'status' ? 'diagnostic' : 'progress';
123
- if (request.family === 'task-workflow') return request.workflow.operation === 'artifact-inspect' || request.workflow.operation === 'decision-next-id' ? 'diagnostic' : 'progress';
124
118
  return null;
125
119
  }
126
120
 
@@ -194,9 +188,7 @@ export function resolveVisibleActiveShortId(ref: string): string | null {
194
188
  // may be a documented short id. Compare canonical ids so a bound sandbox keeps
195
189
  // refusing other tasks without rejecting its own task's short id.
196
190
  function canonicalRequestTask(request: SandboxControlRequest): CanonicalRequestTask {
197
- const ref = request.family === 'task-workflow'
198
- ? request.workflow.taskId
199
- : 'args' in request ? request.args[0] ?? null : null;
191
+ const ref = 'args' in request ? request.args[0] ?? null : null;
200
192
  if (!ref) return { kind: 'none' };
201
193
  if (CONTROL_TASK_ID_RE.test(ref)) return { kind: 'task', taskId: ref };
202
194
  if (!CONTROL_SHORT_ID_RE.test(ref)) return { kind: 'none' };
@@ -223,7 +215,7 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
223
215
  || (requestTask.kind === 'task' && requestTask.taskId !== identity.taskId))) {
224
216
  clientError('SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH', 'request task does not match the sandbox identity', false);
225
217
  }
226
- if (identity.mode === 'branch-only' && (request.family === 'task-finalization' || request.family === 'task-workflow')) {
218
+ if (identity.mode === 'branch-only' && request.family === 'task-finalization') {
227
219
  clientError('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot finalize tasks', false);
228
220
  }
229
221
  }
@@ -384,7 +376,7 @@ export function requestSandboxControl(params: Readonly<{
384
376
  token?: string; generation?: string; timeoutMs?: number;
385
377
  }>): SandboxControlResponse {
386
378
  if (!isSandboxControlFamily(params.family)) clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' is not allowed`, false);
387
- if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization' || params.family === 'task-workflow') {
379
+ if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization') {
388
380
  clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' requires a typed request`, false);
389
381
  }
390
382
  const auth = authority(params);
@@ -392,41 +384,13 @@ export function requestSandboxControl(params: Readonly<{
392
384
  const request: SandboxControlRequest = {
393
385
  version: 3, id: randomUUID(), ...auth, issuedAt,
394
386
  expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
395
- family: params.family as 'task-lifecycle' | 'task-orchestration', args: params.args,
387
+ family: params.family as 'task-lifecycle', args: params.args,
396
388
  controllerProcess: null,
397
389
  controllerProof: null
398
390
  };
399
391
  return exchangeSandboxControl(request, params);
400
392
  }
401
393
 
402
- export function requestSandboxTaskControl(params: Readonly<{
403
- family: 'task-lifecycle' | 'task-orchestration';
404
- args: string[];
405
- controllerProof: CodexControllerLeaseProofV1 | null;
406
- authority?: LifecycleAuthorityRequestV1;
407
- channelDir?: string;
408
- statusDir?: string;
409
- token?: string;
410
- generation?: string;
411
- timeoutMs?: number;
412
- }>): SandboxControlResponse {
413
- const auth = authority(params);
414
- const issuedAt = Date.now();
415
- const request: SandboxTaskCommandRequest = {
416
- version: 3,
417
- id: params.authority?.requestId ?? randomUUID(),
418
- ...auth,
419
- issuedAt,
420
- expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
421
- family: params.family,
422
- args: params.args,
423
- controllerProcess: null,
424
- controllerProof: params.authority ? null : params.controllerProof,
425
- ...(params.authority ? { authority: params.authority } : {})
426
- };
427
- return exchangeSandboxControl(request, params);
428
- }
429
-
430
394
  export function requestSandboxTaskFinalization(params: Readonly<{
431
395
  agent: string;
432
396
  channelDir?: string;
@@ -455,33 +419,6 @@ export function requestSandboxTaskFinalization(params: Readonly<{
455
419
  return exchangeSandboxControl(request, params);
456
420
  }
457
421
 
458
- export function requestSandboxTaskWorkflow(params: Readonly<{
459
- workflow: TaskWorkflowRequest;
460
- channelDir?: string;
461
- statusDir?: string;
462
- token?: string;
463
- generation?: string;
464
- timeoutMs?: number;
465
- authority?: LifecycleAuthorityRequestV1;
466
- }>): SandboxControlResponse {
467
- const auth = authority(params);
468
- const issuedAt = Date.now();
469
- const request: SandboxTaskWorkflowRequest = {
470
- version: 3,
471
- id: params.workflow.id,
472
- ...auth,
473
- issuedAt,
474
- expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
475
- family: 'task-workflow',
476
- args: [],
477
- controllerProcess: null,
478
- controllerProof: null,
479
- workflow: params.workflow,
480
- ...(params.authority ? { authority: params.authority } : {})
481
- };
482
- return exchangeSandboxControl(request, params);
483
- }
484
-
485
422
  type CodexControllerClosed = Readonly<{
486
423
  version: 1;
487
424
  status: 'closed';
@@ -31,12 +31,8 @@ import {
31
31
  createSandboxExecutorExecutionContext,
32
32
  dispatchTaskControlOperation,
33
33
  parseTaskControlOperation,
34
- validateLifecycleRecoveryAttestation,
35
- type LifecycleAuthorityResponseV1,
36
- type LifecycleRecoveryAttestationV1,
37
34
  type TaskControlOperation
38
35
  } from '../../task/control-authority.ts';
39
- import { executeTaskWorkflow } from './workflow-executor.ts';
40
36
  import { assertSandboxControlBrokerOwner, readSandboxControlManifest, type BrokerOwner } from './lifecycle.ts';
41
37
  import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.ts';
42
38
  import {
@@ -45,7 +41,6 @@ import {
45
41
  openCodexControllerRegistration,
46
42
  resolveCodexControllerBinding
47
43
  } from './controller-registration.ts';
48
- import type { OrchestrationDiagnosticLogger } from '../../task/orchestration.ts';
49
44
 
50
45
  export type SandboxControlExecutionResult = {
51
46
  exitCode: number;
@@ -127,7 +122,6 @@ export async function prepareSandboxControlExecution(params: {
127
122
  request: SandboxControlRequest;
128
123
  requestPath: string;
129
124
  internalCliPath: string;
130
- lifecycleAuthority?: LifecycleAuthorityResponseV1;
131
125
  }): Promise<PreparedSandboxControlExecution> {
132
126
  const nonce = randomUUID();
133
127
  const child = spawn(
@@ -181,17 +175,11 @@ export async function prepareSandboxControlExecution(params: {
181
175
  if (!canWrite()) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
182
176
  atomicWriteJson(executionPath(params.manifest, params.request.id), { ...execution, phase: 'running', updatedAt: Date.now() });
183
177
  if (!canWrite()) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
184
- const authority = params.lifecycleAuthority?.attestation ?? null;
185
178
  child.send({
186
179
  version: 2,
187
180
  nonce,
188
181
  owner: gateOwner,
189
- requestId: params.request.id,
190
- operationId: authority?.operationId ?? params.request.id,
191
- phase: authority?.phase ?? (params.request.family === 'task-workflow'
192
- && params.request.workflow?.operation === 'artifact-preflight' ? 'artifact.preflight'
193
- : params.request.family === 'task-workflow' ? 'artifact.finalize-local' : 'orchestration.prepare'),
194
- authority
182
+ requestId: params.request.id
195
183
  } satisfies SandboxControlExecutorGateV2);
196
184
  },
197
185
  completion,
@@ -206,7 +194,7 @@ export async function prepareSandboxControlExecution(params: {
206
194
  };
207
195
  }
208
196
 
209
- function waitForGate(nonce: string, timeoutMs = 2_000): Promise<Readonly<{ owner: BrokerOwner; authority: LifecycleRecoveryAttestationV1 | null; requestId: string; operationId: string; phase: SandboxControlExecutorGateV2['phase'] }>> {
197
+ function waitForGate(nonce: string, timeoutMs = 2_000): Promise<Readonly<{ owner: BrokerOwner; requestId: string }>> {
210
198
  return new Promise((resolve, reject) => {
211
199
  const timer = setTimeout(() => reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_TIMEOUT')), timeoutMs);
212
200
  const onDisconnect = () => {
@@ -222,12 +210,9 @@ function waitForGate(nonce: string, timeoutMs = 2_000): Promise<Readonly<{ owner
222
210
  nonce?: unknown;
223
211
  owner?: Partial<BrokerOwner> | null;
224
212
  requestId?: unknown;
225
- operationId?: unknown;
226
- phase?: unknown;
227
- authority?: unknown;
228
213
  } | null;
229
214
  const owner = value?.owner;
230
- if (!value || Object.keys(value).sort().join(',') !== 'authority,nonce,operationId,owner,phase,requestId,version'
215
+ if (!value || Object.keys(value).sort().join(',') !== 'nonce,owner,requestId,version'
231
216
  || value.version !== 2 || value.nonce !== nonce
232
217
  || !owner || owner.version !== 3 || !Number.isSafeInteger(owner.pid) || (owner.pid ?? 0) <= 0
233
218
  || typeof owner.startTime !== 'number' || !Number.isSafeInteger(owner.startTime)
@@ -236,20 +221,13 @@ function waitForGate(nonce: string, timeoutMs = 2_000): Promise<Readonly<{ owner
236
221
  reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_INVALID'));
237
222
  return;
238
223
  }
239
- if (typeof value.requestId !== 'string' || typeof value.operationId !== 'string'
240
- || !['orchestration.prepare', 'artifact.preflight', 'artifact.finalize-local', 'task-event.completed'].includes(value.phase as string)
241
- || (value.authority !== null && value.authority !== undefined && (() => {
242
- try { validateLifecycleRecoveryAttestation(value.authority); return false; } catch { return true; }
243
- })())) {
224
+ if (typeof value.requestId !== 'string') {
244
225
  reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_INVALID'));
245
226
  return;
246
227
  }
247
228
  resolve({
248
229
  owner: owner as BrokerOwner,
249
- authority: value.authority === null || value.authority === undefined ? null : value.authority as LifecycleRecoveryAttestationV1,
250
- requestId: value.requestId,
251
- operationId: value.operationId,
252
- phase: value.phase as SandboxControlExecutorGateV2['phase']
230
+ requestId: value.requestId
253
231
  });
254
232
  });
255
233
  });
@@ -273,17 +251,6 @@ function controllerFailure(error: unknown): SandboxControlExecutionResult {
273
251
  return { exitCode: 1, stdout: `${JSON.stringify(payload)}\n`, stderr: '' };
274
252
  }
275
253
 
276
- function orchestrationFailure(code: string, message: string): SandboxControlExecutionResult {
277
- return {
278
- exitCode: 1,
279
- stdout: `${JSON.stringify({
280
- status: 'failed', changed: false, taskId: null, run: null, next: null,
281
- error: { code, message }
282
- })}\n`,
283
- stderr: ''
284
- };
285
- }
286
-
287
254
  function lifecycleFailure(code: string, message: string): SandboxControlExecutionResult {
288
255
  return {
289
256
  exitCode: 1,
@@ -330,7 +297,6 @@ function finalizationResult(result: Awaited<ReturnType<typeof applyTaskFinalizat
330
297
  type ExecuteRequestOptions = Readonly<{
331
298
  buildIdentity?: typeof computeLifecycleBuildIdentity;
332
299
  resolveControllerBinding?: typeof resolveCodexControllerBinding;
333
- lifecycleRecoveryAttestation?: LifecycleRecoveryAttestationV1 | null;
334
300
  }>;
335
301
 
336
302
  async function executeRequestInner(
@@ -406,7 +372,6 @@ async function executeRequestInner(
406
372
  return controllerFailure(error);
407
373
  }
408
374
  }
409
- if (request.family === 'task-workflow') return executeTaskWorkflow(manifest, request.workflow, options.lifecycleRecoveryAttestation ?? null);
410
375
  if (request.family === 'task-finalization') {
411
376
  const operation = parseTaskControlOperation(
412
377
  'task-finalization', [manifest.taskId!, 'complete', '--agent', request.agent]
@@ -420,78 +385,21 @@ async function executeRequestInner(
420
385
  taskId: manifest.taskId!,
421
386
  generation: manifest.generation,
422
387
  manifestPath,
423
- requestId: request.id,
424
- lifecycleRecoveryAttestation: options.lifecycleRecoveryAttestation ?? null
388
+ requestId: request.id
425
389
  }),
426
390
  operation
427
391
  );
428
392
  return finalizationResult(await result);
429
393
  }
430
394
  const boundArgs = bindSandboxControlTask(request, manifest.taskId!);
431
- if (request.family === 'task-orchestration') {
432
- boundArgs.push('--git-worktree-root', manifest.worktreeRoot);
433
- }
434
395
  let operation: TaskControlOperation;
435
396
  try {
436
397
  operation = parseTaskControlOperation(request.family, boundArgs);
437
398
  } catch (error) {
438
399
  const message = error instanceof Error ? error.message : String(error);
439
400
  const code = /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_CONTROL_OPERATION_INVALID';
440
- return request.family === 'task-lifecycle'
441
- ? lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', message)
442
- : orchestrationFailure(code, message);
401
+ return lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', `${code}: ${message}`);
443
402
  }
444
- let controllerBinding: Readonly<{
445
- instanceDigest: string;
446
- controlGeneration: string;
447
- }> | null = null;
448
- if (request.family === 'task-orchestration') {
449
- const codexPrepare = operation.family === 'task-orchestration'
450
- && operation.intent === 'prepare'
451
- && operation.input.client === 'codex';
452
- if (codexPrepare) {
453
- if (!request.controllerProof) {
454
- const authority = options.lifecycleRecoveryAttestation;
455
- if (!authority || authority.phase !== 'orchestration.prepare') {
456
- return orchestrationFailure(
457
- 'CODEX_SANDBOX_CONTROLLER_PROOF_REQUIRED',
458
- 'Codex prepare requires a current controller lease proof'
459
- );
460
- }
461
- controllerBinding = {
462
- instanceDigest: authority.controllerInstanceDigest,
463
- controlGeneration: authority.controlGeneration
464
- };
465
- } else {
466
- try {
467
- controllerBinding = (options.resolveControllerBinding ?? resolveCodexControllerBinding)({
468
- manifest,
469
- manifestPath,
470
- proof: request.controllerProof,
471
- buildIdentity: (options.buildIdentity ?? computeLifecycleBuildIdentity)(manifest.repoRoot)
472
- });
473
- } catch (error) {
474
- const code = error instanceof CodexControllerRegistrationError
475
- ? error.code
476
- : 'CODEX_SANDBOX_CONTROLLER_PROOF_INVALID';
477
- return orchestrationFailure(code, `${code}: Codex controller proof was rejected`);
478
- }
479
- }
480
- } else if (request.controllerProof !== null) {
481
- return orchestrationFailure(
482
- 'CODEX_SANDBOX_CONTROLLER_PROOF_INVALID',
483
- 'Controller proof is only accepted for canonical Codex prepare'
484
- );
485
- }
486
- }
487
- const diagnosticLog: OrchestrationDiagnosticLogger = (event, fields): void => {
488
- appendExecutorAudit(manifest, event.startsWith('orchestration-') ? event : `orchestration-${event}`, {
489
- requestId: request.id,
490
- requestFamily: request.family,
491
- sandboxTaskId: manifest.taskId,
492
- ...fields
493
- });
494
- };
495
403
  const context = createSandboxExecutorExecutionContext({
496
404
  repoRoot: manifest.repoRoot,
497
405
  worktreeRoot: manifest.worktreeRoot,
@@ -499,10 +407,7 @@ async function executeRequestInner(
499
407
  taskId: manifest.taskId!,
500
408
  generation: manifest.generation,
501
409
  manifestPath,
502
- requestId: request.id,
503
- diagnosticLog,
504
- ...(controllerBinding ? { controllerBinding } : {}),
505
- lifecycleRecoveryAttestation: options.lifecycleRecoveryAttestation ?? null
410
+ requestId: request.id
506
411
  });
507
412
  const format = (value: unknown): SandboxControlExecutionResult => {
508
413
  const result = value as { status?: string };
@@ -512,12 +417,8 @@ async function executeRequestInner(
512
417
  stderr: ''
513
418
  };
514
419
  };
515
- if (request.family === 'task-orchestration') {
516
- if (operation.family !== 'task-orchestration') throw new Error('SANDBOX_CONTROL_ORCHESTRATION_OPERATION_INVALID');
517
- return format(await dispatchTaskControlOperation(context, operation));
518
- }
519
420
  if (operation.family !== 'task-lifecycle') throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
520
- return format(await dispatchTaskControlOperation(context, operation));
421
+ return format(await dispatchTaskControlOperation(context, operation));
521
422
  }
522
423
 
523
424
  export async function executeRequest(
@@ -579,12 +480,6 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
579
480
  const raw = JSON.parse(fs.readFileSync(requestPath, 'utf8')) as unknown;
580
481
  const request = validateSandboxControlRequest(raw, manifest);
581
482
  if (gate.requestId !== request.id) throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_REQUEST_MISMATCH');
582
- if (gate.authority !== null
583
- && (gate.authority.requestId !== request.id
584
- || gate.authority.operationId !== gate.operationId
585
- || gate.authority.phase !== gate.phase)) {
586
- throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_AUTHORITY_MISMATCH');
587
- }
588
483
  appendExecutorAudit(manifest, 'executor-request-validated', requestAuditFields(manifest, request));
589
484
  const requestDirectory = path.resolve(path.dirname(requestPath));
590
485
  const processingDirectory = path.resolve(path.join(manifest.processingDir, request.id));
@@ -602,7 +497,7 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
602
497
  let result: SandboxControlExecutionResult;
603
498
  try {
604
499
  assertSandboxControlExecutorAuthority(manifest, gateOwner);
605
- const operation = request.family === 'task-lifecycle' || request.family === 'task-orchestration'
500
+ const operation = request.family === 'task-lifecycle'
606
501
  ? (() => {
607
502
  try { return parseTaskControlOperation(request.family, request.args); } catch { return null; }
608
503
  })()
@@ -610,10 +505,8 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
610
505
  const context = createSandboxControlAuditContext(manifest, {
611
506
  requestId: request.id,
612
507
  family: request.family,
613
- operation: operation?.family === 'task-orchestration'
614
- ? operation.intent
615
- : operation?.family === 'task-lifecycle'
616
- ? operation.request.intent
508
+ operation: operation?.family === 'task-lifecycle'
509
+ ? operation.request.intent
617
510
  : request.family === 'task-finalization' ? request.operation : null,
618
511
  phase: 'started-committed',
619
512
  outcome: 'in-progress'
@@ -622,9 +515,7 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
622
515
  writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'started-committed' });
623
516
  const execution = readJsonExecution(executionPath(manifest, request.id));
624
517
  atomicWriteJson(executionPath(manifest, request.id), { ...execution, phase: 'running', updatedAt: Date.now() });
625
- result = await executeRequest(manifest, manifestPath, request, {
626
- lifecycleRecoveryAttestation: gate.authority
627
- });
518
+ result = await executeRequest(manifest, manifestPath, request);
628
519
  } catch (error) {
629
520
  appendExecutorAudit(manifest, 'executor-authority-or-dispatch-failed', {
630
521
  ...requestAuditFields(manifest, request),
@@ -643,9 +534,7 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
643
534
  }
644
535
  : request.family === 'task-create'
645
536
  ? taskCreateExecutionFailure(request.id, code, detail)
646
- : request.family === 'task-lifecycle'
647
- ? lifecycleFailure(code, detail)
648
- : orchestrationFailure(code, detail);
537
+ : lifecycleFailure(code, detail);
649
538
  }
650
539
  process.stdout.write(result.stdout);
651
540
  process.stderr.write(result.stderr);
@@ -4,18 +4,24 @@ import type { SandboxControlResponse } from './protocol.ts';
4
4
  export function finalizationTerminalResponse(taskId: string, requestId: string, receipt: TaskFinalizationReceipt): SandboxControlResponse {
5
5
  const pendingSteps = [
6
6
  receipt.taskComment === 'pending' ? 'task-comment' : null,
7
- receipt.verification === 'pending' ? 'verification' : null
7
+ receipt.verification === 'pending' ? 'verification' : null,
8
+ receipt.summary === 'pending' ? 'summary' : null
8
9
  ].filter((step): step is string => step !== null);
9
- const completedSteps = ['lifecycle', receipt.taskComment === 'pending' ? null : 'task-comment', receipt.verification === 'pending' ? null : 'verification']
10
+ const completedSteps = [
11
+ 'lifecycle', receipt.taskComment === 'pending' ? null : 'task-comment',
12
+ receipt.verification === 'pending' ? null : 'verification', receipt.summary === 'pending' ? null : 'summary'
13
+ ]
10
14
  .filter((step): step is string => step !== null);
11
15
  const warnings = receipt.warnings
12
16
  .filter((warning) => warning.status === 'open')
13
17
  .map(({ status: _status, resolvedAt: _resolvedAt, ...warning }) => warning);
14
18
  const result = {
15
19
  status: 'completed', changed: false, taskId,
20
+ backfill: { status: 'no-op', changed: false, error: null },
16
21
  lifecycle: { status: 'no-op', changed: false, error: null },
17
22
  taskComment: receipt.taskComment === 'pending' ? null : { status: 'no-op', changed: false, error: null },
18
23
  verification: receipt.verification === 'pending' ? null : { status: 'no-op', changed: false, error: null },
24
+ summary: receipt.summary === 'pending' ? null : { status: 'no-op', changed: false, error: null },
19
25
  completedSteps, pendingSteps,
20
26
  result: pendingSteps.length > 0 || receipt.warningProjection === 'pending' || warnings.length > 0
21
27
  ? 'completed_with_warnings' : 'completed',
@@ -5,12 +5,6 @@ import type { ProcessIdentity } from '../../server/process-state.ts';
5
5
  import type { CodexControllerLeaseProofV1 } from './controller-registration.ts';
6
6
  import type { SandboxAuthorityEvidenceV1 } from '../engines/authority.ts';
7
7
  import type { SandboxTaskView } from './task-view.ts';
8
- import { validateTaskWorkflowRequest, type TaskWorkflowRequest } from './task-workflow.ts';
9
- import {
10
- validateLifecycleAuthorityRequest,
11
- type LifecycleAuthorityRequestV1,
12
- type LifecycleRecoveryAttestationV1
13
- } from '../../task/control-authority.ts';
14
8
 
15
9
  export const SANDBOX_CONTROL_MAX_BYTES = 64 * 1024;
16
10
  export const SANDBOX_CONTROL_MAX_LOGICAL_RECORDS = 1024;
@@ -21,7 +15,7 @@ export const SANDBOX_CONTROL_ADMISSION_WINDOW_MS = 2_000;
21
15
  export const SANDBOX_CONTROL_STATUS_INTERVAL_MS = 250;
22
16
  export const SANDBOX_CONTROL_STATUS_STALE_MS = 1_500;
23
17
  export const SANDBOX_CONTROL_FUTURE_SKEW_MS = 1_000;
24
- export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-orchestration', 'task-finalization', 'task-create', 'codex-controller', 'task-workflow'] as const;
18
+ export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-finalization', 'task-create', 'codex-controller'] as const;
25
19
  export type SandboxControlTimingPolicy = Readonly<{
26
20
  controlTickMs: number;
27
21
  parkedBindingInitialMs: number;
@@ -61,9 +55,6 @@ export type SandboxControlExecutorGateV2 = Readonly<{
61
55
  nonce: string;
62
56
  owner: SandboxControlBrokerOwner;
63
57
  requestId: string;
64
- operationId: string;
65
- phase: 'orchestration.prepare' | 'artifact.preflight' | 'artifact.finalize-local' | 'task-event.completed';
66
- authority: LifecycleRecoveryAttestationV1 | null;
67
58
  }>;
68
59
  type RequestBase = Readonly<{
69
60
  version: 3; id: string; token: string; generation: string; issuedAt: number; expiresAt: number;
@@ -71,8 +62,7 @@ type RequestBase = Readonly<{
71
62
  controllerProof: CodexControllerLeaseProofV1 | null;
72
63
  }>;
73
64
  export type SandboxTaskCommandRequest = RequestBase & Readonly<{
74
- family: 'task-lifecycle' | 'task-orchestration'; args: string[];
75
- authority?: LifecycleAuthorityRequestV1;
65
+ family: 'task-lifecycle'; args: string[];
76
66
  }>;
77
67
  export type SandboxTaskFinalizationRequest = RequestBase & Readonly<{
78
68
  family: 'task-finalization'; operation: 'complete'; agent: string; args: [];
@@ -85,13 +75,7 @@ export type SandboxCodexControllerRequest = RequestBase & Readonly<{
85
75
  command: 'open' | 'close' | 'verify';
86
76
  args: [];
87
77
  }>;
88
- export type SandboxTaskWorkflowRequest = RequestBase & Readonly<{
89
- family: 'task-workflow';
90
- args: [];
91
- workflow: TaskWorkflowRequest;
92
- authority?: LifecycleAuthorityRequestV1;
93
- }>;
94
- export type SandboxControlRequest = SandboxTaskCommandRequest | SandboxTaskFinalizationRequest | SandboxTaskCreateRequest | SandboxCodexControllerRequest | SandboxTaskWorkflowRequest;
78
+ export type SandboxControlRequest = SandboxTaskCommandRequest | SandboxTaskFinalizationRequest | SandboxTaskCreateRequest | SandboxCodexControllerRequest;
95
79
  export type SandboxControlError = Readonly<{ code: string; message: string; retryable: boolean }>;
96
80
  export type SandboxControlResultEvidence = Readonly<{
97
81
  version: 1;
@@ -131,11 +115,6 @@ export type SandboxControlResponse = Readonly<{
131
115
  outputState?: 'available' | 'unavailable';
132
116
  payload?: SandboxControlPayloadReference | null;
133
117
  }>;
134
- export type SandboxControlRecoveryWarning = Readonly<{
135
- code: string;
136
- message: string;
137
- action: string;
138
- }>;
139
118
  export type SandboxControlStatus = Readonly<{
140
119
  version: 3; generation: string; broker: ProcessIdentity & { brokerId: string };
141
120
  state: 'starting' | 'healthy' | 'busy' | 'parked'; reasonCode: string | null;
@@ -313,25 +292,8 @@ export function validateSandboxControlRequest(
313
292
  }
314
293
  return request as SandboxTaskFinalizationRequest;
315
294
  }
316
- if (request.family === 'task-workflow') {
317
- const expected = ['args', 'authority', 'controllerProcess', 'controllerProof', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version', 'workflow'];
318
- const baseExpected = expected.filter((key) => key !== 'authority');
319
- if (![baseExpected, expected].some((keys) => Object.keys(request).sort().join(',') === keys.sort().join(','))
320
- || !Array.isArray(request.args) || request.args.length !== 0
321
- || request.controllerProcess !== null || request.controllerProof !== null) {
322
- fail('SANDBOX_CONTROL_REQUEST_INVALID', 'task-workflow request schema is invalid');
323
- }
324
- if (manifest.mode !== 'task-bound' || !manifest.taskId) fail('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot use task-workflow');
325
- const workflow = validateTaskWorkflowRequest(request.workflow);
326
- if (workflow.taskId !== manifest.taskId || workflow.generation !== manifest.generation || workflow.id !== request.id) {
327
- fail('SANDBOX_CONTROL_REQUEST_INVALID', 'task-workflow binding does not match the sandbox manifest');
328
- }
329
- if (request.authority !== undefined) validateAuthoritySelector(request.authority, request, manifest);
330
- return { ...request, workflow } as SandboxTaskWorkflowRequest;
331
- }
332
- const expected = ['args', 'authority', 'controllerProcess', 'controllerProof', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version'];
333
- const baseExpected = expected.filter((key) => key !== 'authority');
334
- if (![baseExpected, expected].some((keys) => Object.keys(request).sort().join(',') === keys.sort().join(','))
295
+ const expected = ['args', 'controllerProcess', 'controllerProof', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version'];
296
+ if (Object.keys(request).sort().join(',') !== expected.sort().join(',')
335
297
  || !Array.isArray(request.args) || !request.args.every((arg) => typeof arg === 'string')) {
336
298
  fail('SANDBOX_CONTROL_REQUEST_INVALID', 'request schema or authorization is invalid');
337
299
  }
@@ -341,52 +303,14 @@ export function validateSandboxControlRequest(
341
303
  "branch-only sandboxes cannot coordinate tasks; return to the host and run 'ai sandbox start --recreate <task-ref-or-correct-branch>'"
342
304
  );
343
305
  }
344
- if (request.family === 'task-orchestration'
345
- && request.args.some((arg) => arg === '--git-worktree-root' || arg.startsWith('--git-worktree-root='))) {
346
- fail('SANDBOX_CONTROL_REQUEST_INVALID', 'worktree binding is reserved for the control broker');
347
- }
348
- if (request.controllerProcess !== null
349
- || (request.controllerProof !== null && !validControllerProof(request.controllerProof))) {
306
+ if (request.controllerProcess !== null || request.controllerProof !== null) {
350
307
  fail('SANDBOX_CONTROL_REQUEST_INVALID', 'controller authority is invalid');
351
308
  }
352
- if (request.family !== 'task-orchestration' && request.controllerProof !== null) {
353
- fail('SANDBOX_CONTROL_REQUEST_INVALID', 'controller proof is not allowed for this family');
354
- }
355
- if (request.authority !== undefined) validateAuthoritySelector(request.authority, request, manifest);
356
309
  return request as SandboxTaskCommandRequest;
357
310
  }
358
311
 
359
- function validateAuthoritySelector(
360
- value: unknown,
361
- request: Record<string, unknown>,
362
- manifest: SandboxControlManifest
363
- ): asserts value is LifecycleAuthorityRequestV1 {
364
- let authority: LifecycleAuthorityRequestV1;
365
- try { authority = validateLifecycleAuthorityRequest(value); }
366
- catch { fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'lifecycle authority selector is invalid'); }
367
- if (authority.requestId !== request.id
368
- || authority.taskId !== manifest.taskId
369
- || authority.expectedControlGeneration !== manifest.generation) {
370
- fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'lifecycle authority selector is not bound to this request');
371
- }
372
- const args = Array.isArray(request.args) ? request.args : [];
373
- if (request.family === 'task-orchestration'
374
- && (args[1] !== 'prepare' || authority.phase !== 'orchestration.prepare')) {
375
- fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'orchestration authority is only valid for prepare');
376
- }
377
- const workflow = request.workflow as TaskWorkflowRequest | undefined;
378
- if (request.family === 'task-workflow') {
379
- const expectedPhase = workflow?.operation === 'artifact-finalize-local'
380
- ? 'artifact.finalize-local'
381
- : workflow?.operation === 'event' ? 'task-event.completed' : null;
382
- if (authority.phase !== expectedPhase) {
383
- fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'workflow authority phase does not match the workflow operation');
384
- }
385
- }
386
- }
387
-
388
312
  export function bindSandboxControlTask(request: SandboxControlRequest, taskId: string): string[] {
389
- if (request.family === 'task-create' || request.family === 'codex-controller' || request.family === 'task-finalization' || request.family === 'task-workflow') {
313
+ if (request.family === 'task-create' || request.family === 'codex-controller' || request.family === 'task-finalization') {
390
314
  fail('SANDBOX_CONTROL_REQUEST_INVALID', `${request.family} requests do not bind a current task`);
391
315
  }
392
316
  if (request.args.length === 0) fail('SANDBOX_CONTROL_REQUEST_INVALID', 'command arguments are required');