@fitlab-ai/agent-infra 0.11.2 → 0.11.4
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.
- package/README.md +17 -8
- package/README.zh-CN.md +17 -8
- package/dist/bin/internal-cli.js +7 -2
- package/dist/lib/agent-clients/adapter.js +3 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +234 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +8 -35
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +1 -34
- package/dist/lib/agent-clients/adapters/codex.js +10 -0
- package/dist/lib/internal/cli-route-inventory.js +1 -1
- package/dist/lib/internal/manual-validation.js +22 -27
- package/dist/lib/internal/sandbox-control.js +1 -43
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-finalization.js +41 -1
- package/dist/lib/internal/task-ledger.js +13 -5
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +7 -2
- package/dist/lib/platform/completion-backfill.js +63 -17
- package/dist/lib/platform/issue-comments.js +2 -31
- package/dist/lib/platform/pr-summary.js +7 -2
- package/dist/lib/sandbox/control/client.js +3 -44
- package/dist/lib/sandbox/control/executor.js +14 -79
- package/dist/lib/sandbox/control/protocol.js +5 -64
- package/dist/lib/sandbox/control/server.js +49 -343
- package/dist/lib/sandbox/control/state.js +1 -7
- package/dist/lib/sandbox/control/task-view.js +1 -2
- package/dist/lib/sandbox/task-cleanup.js +13 -20
- package/dist/lib/task/artifact-command.js +11 -4
- package/dist/lib/task/artifact-lifecycle.js +217 -85
- package/dist/lib/task/artifact-operations.js +1 -26
- package/dist/lib/task/artifact-receipts.js +4 -1
- package/dist/lib/task/artifact-schema.js +1 -0
- package/dist/lib/task/artifact-selection.js +25 -0
- package/dist/lib/task/capabilities.js +158 -39
- package/dist/lib/task/commands/status.js +9 -2
- package/dist/lib/task/control-authority.js +37 -38
- package/dist/lib/task/control-recovery.js +3 -42
- package/dist/lib/task/decision-intents.js +12 -0
- package/dist/lib/task/delegation-receipts.js +2 -2
- package/dist/lib/task/delivery.js +1 -43
- package/dist/lib/task/events.js +80 -64
- package/dist/lib/task/finalization-artifacts.js +1 -3
- package/dist/lib/task/finalization-state.js +1 -1
- package/dist/lib/task/finalization.js +319 -234
- package/dist/lib/task/ledger-intents.js +157 -15
- package/dist/lib/task/lifecycle-path.js +75 -0
- package/dist/lib/task/lifecycle.js +11 -1
- package/dist/lib/task/local-artifact-finalization.js +1 -20
- package/dist/lib/task/manual-validation-receipt.js +8 -3
- package/dist/lib/task/orchestration.js +29 -13
- package/dist/lib/task/review-finalization.js +3 -16
- package/dist/lib/task/rework-intent.js +70 -4
- package/dist/lib/task/verification-engine.js +20 -30
- package/dist/lib/task/verification.js +1 -0
- package/dist/lib/task/workflow-warning-intents.js +21 -6
- package/dist/lib/task/workspace-snapshot.js +7 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +46 -0
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +297 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +13 -33
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -44
- package/lib/agent-clients/adapters/codex.ts +10 -0
- package/lib/internal/cli-route-inventory.ts +1 -1
- package/lib/internal/manual-validation.ts +22 -26
- package/lib/internal/sandbox-control.ts +0 -42
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-finalization.ts +40 -1
- package/lib/internal/task-ledger.ts +12 -5
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +8 -4
- package/lib/platform/completion-backfill.ts +78 -16
- package/lib/platform/issue-comments.ts +1 -35
- package/lib/platform/pr-summary.ts +10 -2
- package/lib/sandbox/control/client.ts +5 -68
- package/lib/sandbox/control/executor.ts +14 -81
- package/lib/sandbox/control/protocol.ts +7 -83
- package/lib/sandbox/control/server.ts +47 -362
- package/lib/sandbox/control/state.ts +1 -9
- package/lib/sandbox/control/task-view.ts +1 -2
- package/lib/sandbox/task-cleanup.ts +12 -20
- package/lib/task/artifact-command.ts +14 -4
- package/lib/task/artifact-lifecycle.ts +236 -85
- package/lib/task/artifact-operations.ts +1 -26
- package/lib/task/artifact-receipts.ts +4 -1
- package/lib/task/artifact-schema.ts +1 -0
- package/lib/task/artifact-selection.ts +53 -0
- package/lib/task/capabilities.ts +154 -40
- package/lib/task/commands/status.ts +11 -2
- package/lib/task/control-authority.ts +41 -48
- package/lib/task/control-recovery.ts +3 -43
- package/lib/task/decision-intents.ts +9 -0
- package/lib/task/delegation-receipts.ts +2 -2
- package/lib/task/delivery.ts +1 -40
- package/lib/task/events.ts +82 -64
- package/lib/task/finalization-artifacts.ts +1 -3
- package/lib/task/finalization-state.ts +1 -1
- package/lib/task/finalization.ts +310 -216
- package/lib/task/ledger-intents.ts +155 -15
- package/lib/task/lifecycle-path.ts +98 -0
- package/lib/task/lifecycle.ts +15 -2
- package/lib/task/local-artifact-finalization.ts +2 -25
- package/lib/task/manual-validation-receipt.ts +8 -2
- package/lib/task/orchestration.ts +56 -27
- package/lib/task/review-finalization.ts +3 -16
- package/lib/task/rework-intent.ts +75 -5
- package/lib/task/verification-engine.ts +19 -30
- package/lib/task/verification.ts +2 -1
- package/lib/task/workflow-warning-intents.ts +18 -6
- package/lib/task/workspace-snapshot.ts +3 -2
- package/package.json +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +19 -5
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +19 -5
- package/templates/.agents/rules/review-handshake.en.md +4 -4
- package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
- package/templates/.agents/skills/analyze-task/SKILL.en.md +11 -4
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -4
- package/templates/.agents/skills/code-task/SKILL.en.md +14 -8
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +18 -13
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +2 -2
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +4 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +4 -4
- package/templates/.agents/skills/code-task/reference/report-template.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.en.md +7 -8
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +7 -8
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
- package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +2 -2
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +9 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -8
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -2
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +11 -6
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/feature-development.en.yaml +11 -6
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +11 -6
- package/templates/.agents/workflows/refactoring.en.yaml +11 -6
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +11 -6
- package/dist/lib/sandbox/control/finalization-response.js +0 -35
- package/dist/lib/sandbox/control/task-workflow.js +0 -74
- package/dist/lib/sandbox/control/workflow-executor.js +0 -142
- package/dist/lib/task/lifecycle-recovery.js +0 -549
- package/dist/lib/task/recovery-warning.js +0 -19
- package/dist/lib/task/workflow-command.js +0 -21
- package/dist/lib/task/workflow-dispatch.js +0 -213
- package/lib/sandbox/control/finalization-response.ts +0 -37
- package/lib/sandbox/control/task-workflow.ts +0 -95
- package/lib/sandbox/control/workflow-executor.ts +0 -156
- package/lib/task/lifecycle-recovery.ts +0 -691
- package/lib/task/recovery-warning.ts +0 -21
- package/lib/task/workflow-command.ts +0 -22
- package/lib/task/workflow-dispatch.ts +0 -214
|
@@ -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;
|
|
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(',') !== '
|
|
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'
|
|
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
|
-
|
|
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,35 +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
|
|
441
|
-
? lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', message)
|
|
442
|
-
: orchestrationFailure(code, message);
|
|
401
|
+
return lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', `${code}: ${message}`);
|
|
443
402
|
}
|
|
444
|
-
const diagnosticLog: OrchestrationDiagnosticLogger = (event, fields): void => {
|
|
445
|
-
appendExecutorAudit(manifest, event.startsWith('orchestration-') ? event : `orchestration-${event}`, {
|
|
446
|
-
requestId: request.id,
|
|
447
|
-
requestFamily: request.family,
|
|
448
|
-
sandboxTaskId: manifest.taskId,
|
|
449
|
-
...fields
|
|
450
|
-
});
|
|
451
|
-
};
|
|
452
403
|
const context = createSandboxExecutorExecutionContext({
|
|
453
404
|
repoRoot: manifest.repoRoot,
|
|
454
405
|
worktreeRoot: manifest.worktreeRoot,
|
|
@@ -456,9 +407,7 @@ async function executeRequestInner(
|
|
|
456
407
|
taskId: manifest.taskId!,
|
|
457
408
|
generation: manifest.generation,
|
|
458
409
|
manifestPath,
|
|
459
|
-
requestId: request.id
|
|
460
|
-
diagnosticLog,
|
|
461
|
-
lifecycleRecoveryAttestation: options.lifecycleRecoveryAttestation ?? null
|
|
410
|
+
requestId: request.id
|
|
462
411
|
});
|
|
463
412
|
const format = (value: unknown): SandboxControlExecutionResult => {
|
|
464
413
|
const result = value as { status?: string };
|
|
@@ -468,12 +417,8 @@ async function executeRequestInner(
|
|
|
468
417
|
stderr: ''
|
|
469
418
|
};
|
|
470
419
|
};
|
|
471
|
-
if (request.family === 'task-orchestration') {
|
|
472
|
-
if (operation.family !== 'task-orchestration') throw new Error('SANDBOX_CONTROL_ORCHESTRATION_OPERATION_INVALID');
|
|
473
|
-
return format(await dispatchTaskControlOperation(context, operation));
|
|
474
|
-
}
|
|
475
420
|
if (operation.family !== 'task-lifecycle') throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
|
|
476
|
-
|
|
421
|
+
return format(await dispatchTaskControlOperation(context, operation));
|
|
477
422
|
}
|
|
478
423
|
|
|
479
424
|
export async function executeRequest(
|
|
@@ -535,12 +480,6 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
|
|
|
535
480
|
const raw = JSON.parse(fs.readFileSync(requestPath, 'utf8')) as unknown;
|
|
536
481
|
const request = validateSandboxControlRequest(raw, manifest);
|
|
537
482
|
if (gate.requestId !== request.id) throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_REQUEST_MISMATCH');
|
|
538
|
-
if (gate.authority !== null
|
|
539
|
-
&& (gate.authority.requestId !== request.id
|
|
540
|
-
|| gate.authority.operationId !== gate.operationId
|
|
541
|
-
|| gate.authority.phase !== gate.phase)) {
|
|
542
|
-
throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_AUTHORITY_MISMATCH');
|
|
543
|
-
}
|
|
544
483
|
appendExecutorAudit(manifest, 'executor-request-validated', requestAuditFields(manifest, request));
|
|
545
484
|
const requestDirectory = path.resolve(path.dirname(requestPath));
|
|
546
485
|
const processingDirectory = path.resolve(path.join(manifest.processingDir, request.id));
|
|
@@ -558,7 +497,7 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
|
|
|
558
497
|
let result: SandboxControlExecutionResult;
|
|
559
498
|
try {
|
|
560
499
|
assertSandboxControlExecutorAuthority(manifest, gateOwner);
|
|
561
|
-
const operation = request.family === 'task-lifecycle'
|
|
500
|
+
const operation = request.family === 'task-lifecycle'
|
|
562
501
|
? (() => {
|
|
563
502
|
try { return parseTaskControlOperation(request.family, request.args); } catch { return null; }
|
|
564
503
|
})()
|
|
@@ -566,10 +505,8 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
|
|
|
566
505
|
const context = createSandboxControlAuditContext(manifest, {
|
|
567
506
|
requestId: request.id,
|
|
568
507
|
family: request.family,
|
|
569
|
-
operation: operation?.family === 'task-
|
|
570
|
-
? operation.intent
|
|
571
|
-
: operation?.family === 'task-lifecycle'
|
|
572
|
-
? operation.request.intent
|
|
508
|
+
operation: operation?.family === 'task-lifecycle'
|
|
509
|
+
? operation.request.intent
|
|
573
510
|
: request.family === 'task-finalization' ? request.operation : null,
|
|
574
511
|
phase: 'started-committed',
|
|
575
512
|
outcome: 'in-progress'
|
|
@@ -578,9 +515,7 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
|
|
|
578
515
|
writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'started-committed' });
|
|
579
516
|
const execution = readJsonExecution(executionPath(manifest, request.id));
|
|
580
517
|
atomicWriteJson(executionPath(manifest, request.id), { ...execution, phase: 'running', updatedAt: Date.now() });
|
|
581
|
-
result = await executeRequest(manifest, manifestPath, request
|
|
582
|
-
lifecycleRecoveryAttestation: gate.authority
|
|
583
|
-
});
|
|
518
|
+
result = await executeRequest(manifest, manifestPath, request);
|
|
584
519
|
} catch (error) {
|
|
585
520
|
appendExecutorAudit(manifest, 'executor-authority-or-dispatch-failed', {
|
|
586
521
|
...requestAuditFields(manifest, request),
|
|
@@ -599,9 +534,7 @@ export async function runSandboxControlExecutor(requestPath: string, nonce: stri
|
|
|
599
534
|
}
|
|
600
535
|
: request.family === 'task-create'
|
|
601
536
|
? taskCreateExecutionFailure(request.id, code, detail)
|
|
602
|
-
:
|
|
603
|
-
? lifecycleFailure(code, detail)
|
|
604
|
-
: orchestrationFailure(code, detail);
|
|
537
|
+
: lifecycleFailure(code, detail);
|
|
605
538
|
}
|
|
606
539
|
process.stdout.write(result.stdout);
|
|
607
540
|
process.stderr.write(result.stderr);
|
|
@@ -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-
|
|
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'
|
|
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
|
|
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
|
-
|
|
317
|
-
|
|
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.
|
|
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'
|
|
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');
|