@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
|
@@ -81,10 +81,6 @@ function preflight(statusDir, generation, taskViewEffect = null, now = Date.now(
|
|
|
81
81
|
function taskViewEffectForRequest(request) {
|
|
82
82
|
if (request.family === 'task-lifecycle' || request.family === 'task-finalization')
|
|
83
83
|
return 'progress';
|
|
84
|
-
if (request.family === 'task-orchestration')
|
|
85
|
-
return request.args[1] === 'status' ? 'diagnostic' : 'progress';
|
|
86
|
-
if (request.family === 'task-workflow')
|
|
87
|
-
return request.workflow.operation === 'artifact-inspect' || request.workflow.operation === 'decision-next-id' ? 'diagnostic' : 'progress';
|
|
88
84
|
return null;
|
|
89
85
|
}
|
|
90
86
|
function parseResponse(raw, id, accepted = false) {
|
|
@@ -152,9 +148,7 @@ export function resolveVisibleActiveShortId(ref) {
|
|
|
152
148
|
// may be a documented short id. Compare canonical ids so a bound sandbox keeps
|
|
153
149
|
// refusing other tasks without rejecting its own task's short id.
|
|
154
150
|
function canonicalRequestTask(request) {
|
|
155
|
-
const ref = request.
|
|
156
|
-
? request.workflow.taskId
|
|
157
|
-
: 'args' in request ? request.args[0] ?? null : null;
|
|
151
|
+
const ref = 'args' in request ? request.args[0] ?? null : null;
|
|
158
152
|
if (!ref)
|
|
159
153
|
return { kind: 'none' };
|
|
160
154
|
if (CONTROL_TASK_ID_RE.test(ref))
|
|
@@ -179,7 +173,7 @@ function exchangeSandboxControl(request, params) {
|
|
|
179
173
|
|| (requestTask.kind === 'task' && requestTask.taskId !== identity.taskId))) {
|
|
180
174
|
clientError('SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH', 'request task does not match the sandbox identity', false);
|
|
181
175
|
}
|
|
182
|
-
if (identity.mode === 'branch-only' &&
|
|
176
|
+
if (identity.mode === 'branch-only' && request.family === 'task-finalization') {
|
|
183
177
|
clientError('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot finalize tasks', false);
|
|
184
178
|
}
|
|
185
179
|
}
|
|
@@ -326,7 +320,7 @@ function authority(params) {
|
|
|
326
320
|
export function requestSandboxControl(params) {
|
|
327
321
|
if (!isSandboxControlFamily(params.family))
|
|
328
322
|
clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' is not allowed`, false);
|
|
329
|
-
if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization'
|
|
323
|
+
if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization') {
|
|
330
324
|
clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' requires a typed request`, false);
|
|
331
325
|
}
|
|
332
326
|
const auth = authority(params);
|
|
@@ -340,23 +334,6 @@ export function requestSandboxControl(params) {
|
|
|
340
334
|
};
|
|
341
335
|
return exchangeSandboxControl(request, params);
|
|
342
336
|
}
|
|
343
|
-
export function requestSandboxTaskControl(params) {
|
|
344
|
-
const auth = authority(params);
|
|
345
|
-
const issuedAt = Date.now();
|
|
346
|
-
const request = {
|
|
347
|
-
version: 3,
|
|
348
|
-
id: params.authority?.requestId ?? randomUUID(),
|
|
349
|
-
...auth,
|
|
350
|
-
issuedAt,
|
|
351
|
-
expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
|
|
352
|
-
family: params.family,
|
|
353
|
-
args: params.args,
|
|
354
|
-
controllerProcess: null,
|
|
355
|
-
controllerProof: params.authority ? null : params.controllerProof,
|
|
356
|
-
...(params.authority ? { authority: params.authority } : {})
|
|
357
|
-
};
|
|
358
|
-
return exchangeSandboxControl(request, params);
|
|
359
|
-
}
|
|
360
337
|
export function requestSandboxTaskFinalization(params) {
|
|
361
338
|
const agent = normalizeAgentToken(params.agent);
|
|
362
339
|
if (!agent)
|
|
@@ -378,24 +355,6 @@ export function requestSandboxTaskFinalization(params) {
|
|
|
378
355
|
};
|
|
379
356
|
return exchangeSandboxControl(request, params);
|
|
380
357
|
}
|
|
381
|
-
export function requestSandboxTaskWorkflow(params) {
|
|
382
|
-
const auth = authority(params);
|
|
383
|
-
const issuedAt = Date.now();
|
|
384
|
-
const request = {
|
|
385
|
-
version: 3,
|
|
386
|
-
id: params.workflow.id,
|
|
387
|
-
...auth,
|
|
388
|
-
issuedAt,
|
|
389
|
-
expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
|
|
390
|
-
family: 'task-workflow',
|
|
391
|
-
args: [],
|
|
392
|
-
controllerProcess: null,
|
|
393
|
-
controllerProof: null,
|
|
394
|
-
workflow: params.workflow,
|
|
395
|
-
...(params.authority ? { authority: params.authority } : {})
|
|
396
|
-
};
|
|
397
|
-
return exchangeSandboxControl(request, params);
|
|
398
|
-
}
|
|
399
358
|
function exactKeys(value, keys) {
|
|
400
359
|
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
401
360
|
}
|
|
@@ -9,8 +9,7 @@ import { bindSandboxControlTask, validateSandboxControlRequest } from './protoco
|
|
|
9
9
|
import { atomicWriteJson, executionPath, readActiveLease, terminateSandboxControlExecution } from './state.js';
|
|
10
10
|
import { requestAuditFields, resultAuditFields, appendDiagnosticAudit, createSandboxControlAuditContext, appendCriticalAudit, writeSandboxControlTransition } from './audit.js';
|
|
11
11
|
import { validateSandboxControlIdentity } from './identity-sentinel.js';
|
|
12
|
-
import { createSandboxExecutorExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation
|
|
13
|
-
import { executeTaskWorkflow } from './workflow-executor.js';
|
|
12
|
+
import { createSandboxExecutorExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../../task/control-authority.js';
|
|
14
13
|
import { assertSandboxControlBrokerOwner, readSandboxControlManifest } from './lifecycle.js';
|
|
15
14
|
import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
|
|
16
15
|
import { closeCodexControllerRegistration, CodexControllerRegistrationError, openCodexControllerRegistration, resolveCodexControllerBinding } from './controller-registration.js';
|
|
@@ -118,17 +117,11 @@ export async function prepareSandboxControlExecution(params) {
|
|
|
118
117
|
atomicWriteJson(executionPath(params.manifest, params.request.id), { ...execution, phase: 'running', updatedAt: Date.now() });
|
|
119
118
|
if (!canWrite())
|
|
120
119
|
throw new Error('SANDBOX_CONTROL_OWNER_LOST');
|
|
121
|
-
const authority = params.lifecycleAuthority?.attestation ?? null;
|
|
122
120
|
child.send({
|
|
123
121
|
version: 2,
|
|
124
122
|
nonce,
|
|
125
123
|
owner: gateOwner,
|
|
126
|
-
requestId: params.request.id
|
|
127
|
-
operationId: authority?.operationId ?? params.request.id,
|
|
128
|
-
phase: authority?.phase ?? (params.request.family === 'task-workflow'
|
|
129
|
-
&& params.request.workflow?.operation === 'artifact-preflight' ? 'artifact.preflight'
|
|
130
|
-
: params.request.family === 'task-workflow' ? 'artifact.finalize-local' : 'orchestration.prepare'),
|
|
131
|
-
authority
|
|
124
|
+
requestId: params.request.id
|
|
132
125
|
});
|
|
133
126
|
},
|
|
134
127
|
completion,
|
|
@@ -155,7 +148,7 @@ function waitForGate(nonce, timeoutMs = 2_000) {
|
|
|
155
148
|
process.off('disconnect', onDisconnect);
|
|
156
149
|
const value = message;
|
|
157
150
|
const owner = value?.owner;
|
|
158
|
-
if (!value || Object.keys(value).sort().join(',') !== '
|
|
151
|
+
if (!value || Object.keys(value).sort().join(',') !== 'nonce,owner,requestId,version'
|
|
159
152
|
|| value.version !== 2 || value.nonce !== nonce
|
|
160
153
|
|| !owner || owner.version !== 3 || !Number.isSafeInteger(owner.pid) || (owner.pid ?? 0) <= 0
|
|
161
154
|
|| typeof owner.startTime !== 'number' || !Number.isSafeInteger(owner.startTime)
|
|
@@ -164,26 +157,13 @@ function waitForGate(nonce, timeoutMs = 2_000) {
|
|
|
164
157
|
reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_INVALID'));
|
|
165
158
|
return;
|
|
166
159
|
}
|
|
167
|
-
if (typeof value.requestId !== 'string'
|
|
168
|
-
|| !['orchestration.prepare', 'artifact.preflight', 'artifact.finalize-local', 'task-event.completed'].includes(value.phase)
|
|
169
|
-
|| (value.authority !== null && value.authority !== undefined && (() => {
|
|
170
|
-
try {
|
|
171
|
-
validateLifecycleRecoveryAttestation(value.authority);
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
catch {
|
|
175
|
-
return true;
|
|
176
|
-
}
|
|
177
|
-
})())) {
|
|
160
|
+
if (typeof value.requestId !== 'string') {
|
|
178
161
|
reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_INVALID'));
|
|
179
162
|
return;
|
|
180
163
|
}
|
|
181
164
|
resolve({
|
|
182
165
|
owner: owner,
|
|
183
|
-
|
|
184
|
-
requestId: value.requestId,
|
|
185
|
-
operationId: value.operationId,
|
|
186
|
-
phase: value.phase
|
|
166
|
+
requestId: value.requestId
|
|
187
167
|
});
|
|
188
168
|
});
|
|
189
169
|
});
|
|
@@ -205,16 +185,6 @@ function controllerFailure(error) {
|
|
|
205
185
|
};
|
|
206
186
|
return { exitCode: 1, stdout: `${JSON.stringify(payload)}\n`, stderr: '' };
|
|
207
187
|
}
|
|
208
|
-
function orchestrationFailure(code, message) {
|
|
209
|
-
return {
|
|
210
|
-
exitCode: 1,
|
|
211
|
-
stdout: `${JSON.stringify({
|
|
212
|
-
status: 'failed', changed: false, taskId: null, run: null, next: null,
|
|
213
|
-
error: { code, message }
|
|
214
|
-
})}\n`,
|
|
215
|
-
stderr: ''
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
188
|
function lifecycleFailure(code, message) {
|
|
219
189
|
return {
|
|
220
190
|
exitCode: 1,
|
|
@@ -324,8 +294,6 @@ async function executeRequestInner(manifest, manifestPath, request, options = {}
|
|
|
324
294
|
return controllerFailure(error);
|
|
325
295
|
}
|
|
326
296
|
}
|
|
327
|
-
if (request.family === 'task-workflow')
|
|
328
|
-
return executeTaskWorkflow(manifest, request.workflow, options.lifecycleRecoveryAttestation ?? null);
|
|
329
297
|
if (request.family === 'task-finalization') {
|
|
330
298
|
const operation = parseTaskControlOperation('task-finalization', [manifest.taskId, 'complete', '--agent', request.agent]);
|
|
331
299
|
if (operation.family !== 'task-finalization')
|
|
@@ -337,15 +305,11 @@ async function executeRequestInner(manifest, manifestPath, request, options = {}
|
|
|
337
305
|
taskId: manifest.taskId,
|
|
338
306
|
generation: manifest.generation,
|
|
339
307
|
manifestPath,
|
|
340
|
-
requestId: request.id
|
|
341
|
-
lifecycleRecoveryAttestation: options.lifecycleRecoveryAttestation ?? null
|
|
308
|
+
requestId: request.id
|
|
342
309
|
}), operation);
|
|
343
310
|
return finalizationResult(await result);
|
|
344
311
|
}
|
|
345
312
|
const boundArgs = bindSandboxControlTask(request, manifest.taskId);
|
|
346
|
-
if (request.family === 'task-orchestration') {
|
|
347
|
-
boundArgs.push('--git-worktree-root', manifest.worktreeRoot);
|
|
348
|
-
}
|
|
349
313
|
let operation;
|
|
350
314
|
try {
|
|
351
315
|
operation = parseTaskControlOperation(request.family, boundArgs);
|
|
@@ -353,18 +317,8 @@ async function executeRequestInner(manifest, manifestPath, request, options = {}
|
|
|
353
317
|
catch (error) {
|
|
354
318
|
const message = error instanceof Error ? error.message : String(error);
|
|
355
319
|
const code = /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_CONTROL_OPERATION_INVALID';
|
|
356
|
-
return
|
|
357
|
-
? lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', message)
|
|
358
|
-
: orchestrationFailure(code, message);
|
|
320
|
+
return lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', `${code}: ${message}`);
|
|
359
321
|
}
|
|
360
|
-
const diagnosticLog = (event, fields) => {
|
|
361
|
-
appendExecutorAudit(manifest, event.startsWith('orchestration-') ? event : `orchestration-${event}`, {
|
|
362
|
-
requestId: request.id,
|
|
363
|
-
requestFamily: request.family,
|
|
364
|
-
sandboxTaskId: manifest.taskId,
|
|
365
|
-
...fields
|
|
366
|
-
});
|
|
367
|
-
};
|
|
368
322
|
const context = createSandboxExecutorExecutionContext({
|
|
369
323
|
repoRoot: manifest.repoRoot,
|
|
370
324
|
worktreeRoot: manifest.worktreeRoot,
|
|
@@ -372,9 +326,7 @@ async function executeRequestInner(manifest, manifestPath, request, options = {}
|
|
|
372
326
|
taskId: manifest.taskId,
|
|
373
327
|
generation: manifest.generation,
|
|
374
328
|
manifestPath,
|
|
375
|
-
requestId: request.id
|
|
376
|
-
diagnosticLog,
|
|
377
|
-
lifecycleRecoveryAttestation: options.lifecycleRecoveryAttestation ?? null
|
|
329
|
+
requestId: request.id
|
|
378
330
|
});
|
|
379
331
|
const format = (value) => {
|
|
380
332
|
const result = value;
|
|
@@ -384,11 +336,6 @@ async function executeRequestInner(manifest, manifestPath, request, options = {}
|
|
|
384
336
|
stderr: ''
|
|
385
337
|
};
|
|
386
338
|
};
|
|
387
|
-
if (request.family === 'task-orchestration') {
|
|
388
|
-
if (operation.family !== 'task-orchestration')
|
|
389
|
-
throw new Error('SANDBOX_CONTROL_ORCHESTRATION_OPERATION_INVALID');
|
|
390
|
-
return format(await dispatchTaskControlOperation(context, operation));
|
|
391
|
-
}
|
|
392
339
|
if (operation.family !== 'task-lifecycle')
|
|
393
340
|
throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
|
|
394
341
|
return format(await dispatchTaskControlOperation(context, operation));
|
|
@@ -449,12 +396,6 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
|
449
396
|
const request = validateSandboxControlRequest(raw, manifest);
|
|
450
397
|
if (gate.requestId !== request.id)
|
|
451
398
|
throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_REQUEST_MISMATCH');
|
|
452
|
-
if (gate.authority !== null
|
|
453
|
-
&& (gate.authority.requestId !== request.id
|
|
454
|
-
|| gate.authority.operationId !== gate.operationId
|
|
455
|
-
|| gate.authority.phase !== gate.phase)) {
|
|
456
|
-
throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_AUTHORITY_MISMATCH');
|
|
457
|
-
}
|
|
458
399
|
appendExecutorAudit(manifest, 'executor-request-validated', requestAuditFields(manifest, request));
|
|
459
400
|
const requestDirectory = path.resolve(path.dirname(requestPath));
|
|
460
401
|
const processingDirectory = path.resolve(path.join(manifest.processingDir, request.id));
|
|
@@ -472,7 +413,7 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
|
472
413
|
let result;
|
|
473
414
|
try {
|
|
474
415
|
assertSandboxControlExecutorAuthority(manifest, gateOwner);
|
|
475
|
-
const operation = request.family === 'task-lifecycle'
|
|
416
|
+
const operation = request.family === 'task-lifecycle'
|
|
476
417
|
? (() => {
|
|
477
418
|
try {
|
|
478
419
|
return parseTaskControlOperation(request.family, request.args);
|
|
@@ -485,11 +426,9 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
|
485
426
|
const context = createSandboxControlAuditContext(manifest, {
|
|
486
427
|
requestId: request.id,
|
|
487
428
|
family: request.family,
|
|
488
|
-
operation: operation?.family === 'task-
|
|
489
|
-
? operation.intent
|
|
490
|
-
:
|
|
491
|
-
? operation.request.intent
|
|
492
|
-
: request.family === 'task-finalization' ? request.operation : null,
|
|
429
|
+
operation: operation?.family === 'task-lifecycle'
|
|
430
|
+
? operation.request.intent
|
|
431
|
+
: request.family === 'task-finalization' ? request.operation : null,
|
|
493
432
|
phase: 'started-committed',
|
|
494
433
|
outcome: 'in-progress'
|
|
495
434
|
});
|
|
@@ -497,9 +436,7 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
|
497
436
|
writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'started-committed' });
|
|
498
437
|
const execution = readJsonExecution(executionPath(manifest, request.id));
|
|
499
438
|
atomicWriteJson(executionPath(manifest, request.id), { ...execution, phase: 'running', updatedAt: Date.now() });
|
|
500
|
-
result = await executeRequest(manifest, manifestPath, request
|
|
501
|
-
lifecycleRecoveryAttestation: gate.authority
|
|
502
|
-
});
|
|
439
|
+
result = await executeRequest(manifest, manifestPath, request);
|
|
503
440
|
}
|
|
504
441
|
catch (error) {
|
|
505
442
|
appendExecutorAudit(manifest, 'executor-authority-or-dispatch-failed', {
|
|
@@ -519,9 +456,7 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
|
519
456
|
}
|
|
520
457
|
: request.family === 'task-create'
|
|
521
458
|
? taskCreateExecutionFailure(request.id, code, detail)
|
|
522
|
-
:
|
|
523
|
-
? lifecycleFailure(code, detail)
|
|
524
|
-
: orchestrationFailure(code, detail);
|
|
459
|
+
: lifecycleFailure(code, detail);
|
|
525
460
|
}
|
|
526
461
|
process.stdout.write(result.stdout);
|
|
527
462
|
process.stderr.write(result.stderr);
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { validateTaskCreateCandidate } from '../../task/create.js';
|
|
3
3
|
import { normalizeAgentToken } from '../../agent-clients/tokens.js';
|
|
4
|
-
import { validateTaskWorkflowRequest } from './task-workflow.js';
|
|
5
|
-
import { validateLifecycleAuthorityRequest } from '../../task/control-authority.js';
|
|
6
4
|
export const SANDBOX_CONTROL_MAX_BYTES = 64 * 1024;
|
|
7
5
|
export const SANDBOX_CONTROL_MAX_LOGICAL_RECORDS = 1024;
|
|
8
6
|
export const SANDBOX_CONTROL_MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
|
|
@@ -12,7 +10,7 @@ export const SANDBOX_CONTROL_ADMISSION_WINDOW_MS = 2_000;
|
|
|
12
10
|
export const SANDBOX_CONTROL_STATUS_INTERVAL_MS = 250;
|
|
13
11
|
export const SANDBOX_CONTROL_STATUS_STALE_MS = 1_500;
|
|
14
12
|
export const SANDBOX_CONTROL_FUTURE_SKEW_MS = 1_000;
|
|
15
|
-
export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-
|
|
13
|
+
export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-finalization', 'task-create', 'codex-controller'];
|
|
16
14
|
export const DEFAULT_SANDBOX_CONTROL_TIMING = Object.freeze({
|
|
17
15
|
controlTickMs: 250,
|
|
18
16
|
parkedBindingInitialMs: 1_000,
|
|
@@ -173,78 +171,21 @@ export function validateSandboxControlRequest(value, manifest, options = {}) {
|
|
|
173
171
|
}
|
|
174
172
|
return request;
|
|
175
173
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const baseExpected = expected.filter((key) => key !== 'authority');
|
|
179
|
-
if (![baseExpected, expected].some((keys) => Object.keys(request).sort().join(',') === keys.sort().join(','))
|
|
180
|
-
|| !Array.isArray(request.args) || request.args.length !== 0
|
|
181
|
-
|| request.controllerProcess !== null || request.controllerProof !== null) {
|
|
182
|
-
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'task-workflow request schema is invalid');
|
|
183
|
-
}
|
|
184
|
-
if (manifest.mode !== 'task-bound' || !manifest.taskId)
|
|
185
|
-
fail('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot use task-workflow');
|
|
186
|
-
const workflow = validateTaskWorkflowRequest(request.workflow);
|
|
187
|
-
if (workflow.taskId !== manifest.taskId || workflow.generation !== manifest.generation || workflow.id !== request.id) {
|
|
188
|
-
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'task-workflow binding does not match the sandbox manifest');
|
|
189
|
-
}
|
|
190
|
-
if (request.authority !== undefined)
|
|
191
|
-
validateAuthoritySelector(request.authority, request, manifest);
|
|
192
|
-
return { ...request, workflow };
|
|
193
|
-
}
|
|
194
|
-
const expected = ['args', 'authority', 'controllerProcess', 'controllerProof', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version'];
|
|
195
|
-
const baseExpected = expected.filter((key) => key !== 'authority');
|
|
196
|
-
if (![baseExpected, expected].some((keys) => Object.keys(request).sort().join(',') === keys.sort().join(','))
|
|
174
|
+
const expected = ['args', 'controllerProcess', 'controllerProof', 'expiresAt', 'family', 'generation', 'id', 'issuedAt', 'token', 'version'];
|
|
175
|
+
if (Object.keys(request).sort().join(',') !== expected.sort().join(',')
|
|
197
176
|
|| !Array.isArray(request.args) || !request.args.every((arg) => typeof arg === 'string')) {
|
|
198
177
|
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'request schema or authorization is invalid');
|
|
199
178
|
}
|
|
200
179
|
if (manifest.mode !== 'task-bound' || !manifest.taskId) {
|
|
201
180
|
fail('SANDBOX_CONTROL_BRANCH_ONLY', "branch-only sandboxes cannot coordinate tasks; return to the host and run 'ai sandbox start --recreate <task-ref-or-correct-branch>'");
|
|
202
181
|
}
|
|
203
|
-
if (request.
|
|
204
|
-
&& request.args.some((arg) => arg === '--git-worktree-root' || arg.startsWith('--git-worktree-root='))) {
|
|
205
|
-
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'worktree binding is reserved for the control broker');
|
|
206
|
-
}
|
|
207
|
-
if (request.controllerProcess !== null
|
|
208
|
-
|| (request.controllerProof !== null && !validControllerProof(request.controllerProof))) {
|
|
182
|
+
if (request.controllerProcess !== null || request.controllerProof !== null) {
|
|
209
183
|
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'controller authority is invalid');
|
|
210
184
|
}
|
|
211
|
-
if (request.family !== 'task-orchestration' && request.controllerProof !== null) {
|
|
212
|
-
fail('SANDBOX_CONTROL_REQUEST_INVALID', 'controller proof is not allowed for this family');
|
|
213
|
-
}
|
|
214
|
-
if (request.authority !== undefined)
|
|
215
|
-
validateAuthoritySelector(request.authority, request, manifest);
|
|
216
185
|
return request;
|
|
217
186
|
}
|
|
218
|
-
function validateAuthoritySelector(value, request, manifest) {
|
|
219
|
-
let authority;
|
|
220
|
-
try {
|
|
221
|
-
authority = validateLifecycleAuthorityRequest(value);
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'lifecycle authority selector is invalid');
|
|
225
|
-
}
|
|
226
|
-
if (authority.requestId !== request.id
|
|
227
|
-
|| authority.taskId !== manifest.taskId
|
|
228
|
-
|| authority.expectedControlGeneration !== manifest.generation) {
|
|
229
|
-
fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'lifecycle authority selector is not bound to this request');
|
|
230
|
-
}
|
|
231
|
-
const args = Array.isArray(request.args) ? request.args : [];
|
|
232
|
-
if (request.family === 'task-orchestration'
|
|
233
|
-
&& (args[1] !== 'prepare' || authority.phase !== 'orchestration.prepare')) {
|
|
234
|
-
fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'orchestration authority is only valid for prepare');
|
|
235
|
-
}
|
|
236
|
-
const workflow = request.workflow;
|
|
237
|
-
if (request.family === 'task-workflow') {
|
|
238
|
-
const expectedPhase = workflow?.operation === 'artifact-finalize-local'
|
|
239
|
-
? 'artifact.finalize-local'
|
|
240
|
-
: workflow?.operation === 'event' ? 'task-event.completed' : null;
|
|
241
|
-
if (authority.phase !== expectedPhase) {
|
|
242
|
-
fail('SANDBOX_CONTROL_AUTHORITY_INVALID', 'workflow authority phase does not match the workflow operation');
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
187
|
export function bindSandboxControlTask(request, taskId) {
|
|
247
|
-
if (request.family === 'task-create' || request.family === 'codex-controller' || request.family === 'task-finalization'
|
|
188
|
+
if (request.family === 'task-create' || request.family === 'codex-controller' || request.family === 'task-finalization') {
|
|
248
189
|
fail('SANDBOX_CONTROL_REQUEST_INVALID', `${request.family} requests do not bind a current task`);
|
|
249
190
|
}
|
|
250
191
|
if (request.args.length === 0)
|