@fitlab-ai/agent-infra 0.11.2 → 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.
- 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/manual-validation.js +22 -27
- package/dist/lib/internal/sandbox-control.js +1 -43
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +6 -2
- 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/finalization-response.js +3 -5
- package/dist/lib/sandbox/control/protocol.js +5 -64
- package/dist/lib/sandbox/control/server.js +29 -206
- package/dist/lib/sandbox/control/state.js +1 -7
- package/dist/lib/sandbox/control/task-view.js +1 -2
- package/dist/lib/task/control-authority.js +34 -36
- package/dist/lib/task/control-recovery.js +3 -42
- package/dist/lib/task/delegation-receipts.js +2 -2
- package/dist/lib/task/finalization-artifacts.js +1 -3
- package/dist/lib/task/finalization-state.js +1 -1
- package/dist/lib/task/finalization.js +53 -126
- package/dist/lib/task/lifecycle.js +1 -1
- package/dist/lib/task/manual-validation-receipt.js +8 -3
- package/dist/lib/task/orchestration.js +12 -6
- 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/manual-validation.ts +22 -26
- package/lib/internal/sandbox-control.ts +0 -42
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +7 -4
- 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/finalization-response.ts +3 -5
- package/lib/sandbox/control/protocol.ts +7 -83
- package/lib/sandbox/control/server.ts +30 -220
- package/lib/sandbox/control/state.ts +1 -9
- package/lib/sandbox/control/task-view.ts +1 -2
- package/lib/task/control-authority.ts +38 -47
- package/lib/task/control-recovery.ts +3 -43
- package/lib/task/delegation-receipts.ts +2 -2
- package/lib/task/finalization-artifacts.ts +1 -3
- package/lib/task/finalization-state.ts +1 -1
- package/lib/task/finalization.ts +57 -121
- package/lib/task/lifecycle.ts +2 -2
- package/lib/task/manual-validation-receipt.ts +8 -2
- package/lib/task/orchestration.ts +30 -19
- 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/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 +6 -7
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +6 -7
- 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/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/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
|
@@ -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');
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { finalizationTerminalResponse } from './finalization-response.ts';
|
|
2
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
3
2
|
import { completedReentryView } from './completed-reentry.ts';
|
|
4
3
|
import fs from 'node:fs';
|
|
5
4
|
import path from 'node:path';
|
|
@@ -12,7 +11,6 @@ import {
|
|
|
12
11
|
SANDBOX_CONTROL_MAX_RESPONSE_BYTES,
|
|
13
12
|
SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES,
|
|
14
13
|
type SandboxControlManifest,
|
|
15
|
-
type SandboxControlRecoveryWarning,
|
|
16
14
|
type SandboxControlRequest,
|
|
17
15
|
type SandboxControlResponse,
|
|
18
16
|
type SandboxControlTimingPolicy
|
|
@@ -62,10 +60,6 @@ import {
|
|
|
62
60
|
operationRecoveryBinding,
|
|
63
61
|
SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES
|
|
64
62
|
} from '../../task/control-recovery.ts';
|
|
65
|
-
import { readRun } from '../../task/orchestration.ts';
|
|
66
|
-
import { readLifecycleRecoveryDomainEvidence } from '../../task/lifecycle-recovery.ts';
|
|
67
|
-
import { captureRepositorySnapshot } from '../../task/workspace-snapshot.ts';
|
|
68
|
-
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.ts';
|
|
69
63
|
import { readLifecycleJournalEvidence } from '../../task/lifecycle.ts';
|
|
70
64
|
import { resolveTaskRef } from '../../task/resolve-ref.ts';
|
|
71
65
|
import { loadShortIdByTaskId } from '../../task/short-id.ts';
|
|
@@ -88,70 +82,16 @@ import {
|
|
|
88
82
|
type SandboxTaskView
|
|
89
83
|
} from './task-view.ts';
|
|
90
84
|
import { taskCreateOutputUnavailableResult } from '../../task/create-service.ts';
|
|
91
|
-
import {
|
|
92
|
-
consumeLifecycleRecoveryAttestation,
|
|
93
|
-
issueLifecycleRecoveryAttestation,
|
|
94
|
-
recoverLifecycleRecoveryOperation,
|
|
95
|
-
type LifecycleAuthorityResponseV1
|
|
96
|
-
} from '../../task/control-authority.ts';
|
|
97
|
-
import { createCodexCapabilityStore } from '../../agent-clients/adapters/codex-lifecycle/capability-store.ts';
|
|
98
|
-
import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.ts';
|
|
99
85
|
|
|
100
86
|
type ActiveExecution = {
|
|
101
87
|
request: SandboxControlRequest;
|
|
102
88
|
prepared: PreparedSandboxControlExecution;
|
|
103
|
-
lifecycleAuthority: LifecycleAuthorityResponseV1 | undefined;
|
|
104
89
|
result: SandboxControlExecutionResult | null;
|
|
105
90
|
resultEvidenceWritten: boolean;
|
|
106
91
|
failure: unknown;
|
|
107
92
|
settled: boolean;
|
|
108
93
|
};
|
|
109
94
|
|
|
110
|
-
function consumeLifecycleAuthorityForResult(
|
|
111
|
-
manifest: SandboxControlManifest,
|
|
112
|
-
execution: ActiveExecution
|
|
113
|
-
): boolean {
|
|
114
|
-
const authority = execution.lifecycleAuthority?.attestation;
|
|
115
|
-
if (!authority || execution.result?.exitCode !== 0) return true;
|
|
116
|
-
try {
|
|
117
|
-
consumeLifecycleRecoveryAttestation(authority);
|
|
118
|
-
return true;
|
|
119
|
-
} catch (error) {
|
|
120
|
-
appendBrokerAudit(manifest, 'lifecycle-authority-consume-retry', {
|
|
121
|
-
...requestAuditFields(manifest, execution.request),
|
|
122
|
-
errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_CONSUME_FAILED'
|
|
123
|
-
});
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function lifecycleAuthorityForRequest(
|
|
129
|
-
manifest: SandboxControlManifest,
|
|
130
|
-
manifestPath: string,
|
|
131
|
-
request: SandboxControlRequest
|
|
132
|
-
): LifecycleAuthorityResponseV1 | undefined {
|
|
133
|
-
const selector = request.family === 'task-workflow' || request.family === 'task-orchestration'
|
|
134
|
-
? request.authority
|
|
135
|
-
: undefined;
|
|
136
|
-
if (!selector) return undefined;
|
|
137
|
-
let controllerBinding: { instanceDigest: string; controlGeneration: string } | null = null;
|
|
138
|
-
try {
|
|
139
|
-
const registration = readCodexControllerRegistration(manifestPath);
|
|
140
|
-
controllerBinding = {
|
|
141
|
-
instanceDigest: registration.controllerInstanceDigest,
|
|
142
|
-
controlGeneration: registration.controlGeneration
|
|
143
|
-
};
|
|
144
|
-
} catch {
|
|
145
|
-
// The issuer reports the stable unavailable result; the broker never trusts
|
|
146
|
-
// a controller binding supplied by the request.
|
|
147
|
-
}
|
|
148
|
-
return issueLifecycleRecoveryAttestation(selector, {
|
|
149
|
-
controllerBinding,
|
|
150
|
-
capabilityStore: createCodexCapabilityStore(),
|
|
151
|
-
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
95
|
function appendBrokerAudit(
|
|
156
96
|
manifest: SandboxControlManifest,
|
|
157
97
|
event: string,
|
|
@@ -160,38 +100,21 @@ function appendBrokerAudit(
|
|
|
160
100
|
appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
|
|
161
101
|
}
|
|
162
102
|
|
|
163
|
-
function operationKey(request: SandboxControlRequest
|
|
103
|
+
function operationKey(request: SandboxControlRequest): string | null {
|
|
164
104
|
if (request.family === 'task-finalization') return request.operation;
|
|
165
105
|
if (request.family === 'task-create') return 'create';
|
|
166
106
|
if (request.family === 'codex-controller') return request.command;
|
|
167
|
-
if (request.family
|
|
168
|
-
if (request.family !== 'task-lifecycle' && request.family !== 'task-orchestration') return null;
|
|
169
|
-
if (request.family === 'task-orchestration' && request.args[1] === 'route') {
|
|
170
|
-
if (!output) return 'route';
|
|
171
|
-
const result = parseControlOutput(output);
|
|
172
|
-
return result?.changed === true && result.status === 'completed' ? 'route.clean-completion' : 'route.read';
|
|
173
|
-
}
|
|
107
|
+
if (request.family !== 'task-lifecycle') return null;
|
|
174
108
|
try {
|
|
175
109
|
const operation = parseTaskControlOperation(request.family, request.args);
|
|
176
|
-
return operation.family === 'task-
|
|
110
|
+
return operation.family === 'task-lifecycle' ? operation.request.intent : null;
|
|
177
111
|
} catch {
|
|
178
112
|
return null;
|
|
179
113
|
}
|
|
180
114
|
}
|
|
181
115
|
|
|
182
|
-
function recoveryOperationKey(
|
|
183
|
-
request
|
|
184
|
-
terminalResult: ReturnType<typeof readSandboxControlTerminalResult> | null,
|
|
185
|
-
output: string | undefined
|
|
186
|
-
): string | null {
|
|
187
|
-
const operation = operationKey(request, output);
|
|
188
|
-
if (operation !== 'route') return operation;
|
|
189
|
-
const digest = terminalResult?.intentDigest;
|
|
190
|
-
for (const candidate of ['route.read', 'route.clean-completion'] as const) {
|
|
191
|
-
const expected = operationRecoveryBinding(request.id, request.generation, null, request.family, candidate).intentDigest;
|
|
192
|
-
if (digest === expected) return candidate;
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
116
|
+
function recoveryOperationKey(request: SandboxControlRequest): string | null {
|
|
117
|
+
return operationKey(request);
|
|
195
118
|
}
|
|
196
119
|
|
|
197
120
|
function terminalResultMatchesRequest(
|
|
@@ -199,9 +122,7 @@ function terminalResultMatchesRequest(
|
|
|
199
122
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult>
|
|
200
123
|
): boolean {
|
|
201
124
|
const operation = operationKey(request);
|
|
202
|
-
const candidates = operation
|
|
203
|
-
? ['route.read', 'route.clean-completion']
|
|
204
|
-
: operation ? [operation] : [];
|
|
125
|
+
const candidates = operation ? [operation] : [];
|
|
205
126
|
return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
|
|
206
127
|
}
|
|
207
128
|
|
|
@@ -374,8 +295,7 @@ export function genericRecoveryResponse(
|
|
|
374
295
|
request: SandboxControlRequest,
|
|
375
296
|
exitCode: number,
|
|
376
297
|
payload: ReturnType<typeof readSandboxControlPayload> | null,
|
|
377
|
-
cause: 'publish' | 'recovery' = 'recovery'
|
|
378
|
-
warning: SandboxControlRecoveryWarning | null = null
|
|
298
|
+
cause: 'publish' | 'recovery' = 'recovery'
|
|
379
299
|
): SandboxControlResponse {
|
|
380
300
|
const outputUnavailable = request.family === 'task-create' && !payload;
|
|
381
301
|
return {
|
|
@@ -384,9 +304,7 @@ export function genericRecoveryResponse(
|
|
|
384
304
|
phase: 'completed',
|
|
385
305
|
exitCode,
|
|
386
306
|
stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
|
|
387
|
-
stderr: payload ? '' :
|
|
388
|
-
? `${warning.code}: ${warning.message}\nAction: ${warning.action}\n`
|
|
389
|
-
: cause === 'publish'
|
|
307
|
+
stderr: payload ? '' : cause === 'publish'
|
|
390
308
|
? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
|
|
391
309
|
: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
|
|
392
310
|
error: null,
|
|
@@ -465,42 +383,16 @@ function controllerDomainEvidence(
|
|
|
465
383
|
}
|
|
466
384
|
}
|
|
467
385
|
|
|
468
|
-
function
|
|
469
|
-
operation: ReturnType<typeof findSandboxControlRecoveryOperation>,
|
|
470
|
-
terminalResult: ReturnType<typeof readSandboxControlTerminalResult>,
|
|
471
|
-
output: Record<string, unknown> | null,
|
|
472
|
-
run: ReturnType<typeof readRun> | null
|
|
473
|
-
): Readonly<Record<string, unknown>> {
|
|
474
|
-
if (!operation || !output) return { consistent: false };
|
|
475
|
-
const outputRun = output.run && typeof output.run === 'object' && !Array.isArray(output.run)
|
|
476
|
-
? output.run as Record<string, unknown> : null;
|
|
477
|
-
const runMatches = outputRun === null ? run === null : run !== null && JSON.stringify(run) === JSON.stringify(outputRun);
|
|
478
|
-
if (operation.class === 'read-only') {
|
|
479
|
-
return {
|
|
480
|
-
consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
|
|
481
|
-
snapshotValid: true,
|
|
482
|
-
status: run?.status ?? terminalResult.status,
|
|
483
|
-
pendingDelegation: run?.pendingDelegation ?? null
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
if (!run || !outputRun) return { consistent: false };
|
|
487
|
-
return {
|
|
488
|
-
consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
|
|
489
|
-
status: run.status,
|
|
490
|
-
pendingDelegation: run.pendingDelegation
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
function readRecoveryDomain(
|
|
386
|
+
async function readRecoveryDomain(
|
|
495
387
|
manifest: SandboxControlManifest,
|
|
496
388
|
manifestPath: string,
|
|
497
389
|
request: SandboxControlRequest,
|
|
498
390
|
operation: ReturnType<typeof findSandboxControlRecoveryOperation>,
|
|
499
391
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult>,
|
|
500
392
|
payloadOutput: string | null
|
|
501
|
-
): RecoveryDomainEvidence {
|
|
393
|
+
): Promise<RecoveryDomainEvidence> {
|
|
502
394
|
if (!operation) return { domain: null };
|
|
503
|
-
const taskRef = request.family === 'task-finalization'
|
|
395
|
+
const taskRef = request.family === 'task-finalization'
|
|
504
396
|
? manifest.taskId
|
|
505
397
|
: 'args' in request ? request.args[0] ?? null : manifest.taskId;
|
|
506
398
|
const output = parseControlOutput(payloadOutput);
|
|
@@ -510,19 +402,7 @@ function readRecoveryDomain(
|
|
|
510
402
|
|
|
511
403
|
if (operation.family === 'task-lifecycle') {
|
|
512
404
|
if (operation.intent === 'recover-started') {
|
|
513
|
-
|
|
514
|
-
if (!('args' in request)) return { domain: null };
|
|
515
|
-
const parsed = parseTaskControlOperation('task-lifecycle', request.args);
|
|
516
|
-
if (parsed.family !== 'task-lifecycle' || parsed.request.intent !== 'recover-started') {
|
|
517
|
-
return { domain: null };
|
|
518
|
-
}
|
|
519
|
-
return {
|
|
520
|
-
domain: readLifecycleRecoveryDomainEvidence(manifest.repoRoot, parsed.request, terminalResult),
|
|
521
|
-
journal: readLifecycleJournalEvidence(manifest.repoRoot, taskRef!)
|
|
522
|
-
};
|
|
523
|
-
} catch {
|
|
524
|
-
return { domain: null };
|
|
525
|
-
}
|
|
405
|
+
return { domain: null };
|
|
526
406
|
}
|
|
527
407
|
if (!taskRef || !terminalResult.targetState) return { domain: null };
|
|
528
408
|
const journal = readLifecycleJournalEvidence(manifest.repoRoot, taskRef);
|
|
@@ -541,48 +421,12 @@ function readRecoveryDomain(
|
|
|
541
421
|
}
|
|
542
422
|
}
|
|
543
423
|
|
|
544
|
-
if (operation.family === 'task-orchestration') {
|
|
545
|
-
if (!taskRef) return { domain: null };
|
|
546
|
-
try {
|
|
547
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
|
|
548
|
-
if (!resolved.ok) return { domain: { consistent: false } };
|
|
549
|
-
const run = readRun(resolved.taskDir);
|
|
550
|
-
if (!run && operation.class !== 'read-only') return { domain: { consistent: false } };
|
|
551
|
-
if (operation.class === 'route.clean-completion') {
|
|
552
|
-
const snapshot = captureRepositorySnapshot(manifest.repoRoot);
|
|
553
|
-
const metadata = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
|
|
554
|
-
const completion = run?.completionEvidence ?? null;
|
|
555
|
-
const consistent = run !== null && completion !== null
|
|
556
|
-
&& terminalResult.completionEvidence !== null
|
|
557
|
-
&& isDeepStrictEqual(completion, terminalResult.completionEvidence);
|
|
558
|
-
return {
|
|
559
|
-
domain: {
|
|
560
|
-
consistent,
|
|
561
|
-
status: run?.status ?? null,
|
|
562
|
-
pendingDelegation: run?.pendingDelegation ?? null,
|
|
563
|
-
completionEvidence: completion,
|
|
564
|
-
snapshot,
|
|
565
|
-
lastReviewedCommit: metadata.last_reviewed_commit
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
return { domain: orchestrationDomainEvidence(operation, terminalResult, output, run) };
|
|
570
|
-
} catch {
|
|
571
|
-
return { domain: { consistent: false } };
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
424
|
if (operation.family === 'task-create') {
|
|
576
425
|
return { domain: taskCreateDomainEvidence(manifest, output) };
|
|
577
426
|
}
|
|
578
427
|
if (operation.family === 'codex-controller') {
|
|
579
428
|
return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
|
|
580
429
|
}
|
|
581
|
-
if (operation.family === 'task-workflow') {
|
|
582
|
-
return {
|
|
583
|
-
domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
430
|
return { domain: null };
|
|
587
431
|
}
|
|
588
432
|
|
|
@@ -597,15 +441,15 @@ function readCommittedCriticalPhases(manifest: SandboxControlManifest, requestId
|
|
|
597
441
|
});
|
|
598
442
|
}
|
|
599
443
|
|
|
600
|
-
export function recoveryResponse(
|
|
444
|
+
export async function recoveryResponse(
|
|
601
445
|
manifest: SandboxControlManifest,
|
|
602
446
|
manifestPath: string,
|
|
603
447
|
request: SandboxControlRequest,
|
|
604
448
|
evidence: ReturnType<typeof readSandboxControlResultEvidence>,
|
|
605
449
|
payload: ReturnType<typeof readSandboxControlPayload> | null,
|
|
606
450
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult>
|
|
607
|
-
): SandboxControlResponse | null {
|
|
608
|
-
const operationName = recoveryOperationKey(request
|
|
451
|
+
): Promise<SandboxControlResponse | null> {
|
|
452
|
+
const operationName = recoveryOperationKey(request);
|
|
609
453
|
const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
|
|
610
454
|
if (!operation) return null;
|
|
611
455
|
if (payload) {
|
|
@@ -616,30 +460,9 @@ export function recoveryResponse(
|
|
|
616
460
|
}
|
|
617
461
|
const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
|
|
618
462
|
if (evidence.exitCode === 0 && finalization?.status === 'deferred') return null;
|
|
619
|
-
if (evidence.exitCode === 0 && request.family === 'task-workflow' && request.authority?.phase === 'task-event.completed') {
|
|
620
|
-
try {
|
|
621
|
-
const registration = readCodexControllerRegistration(manifestPath);
|
|
622
|
-
const recovered = recoverLifecycleRecoveryOperation(request.authority, {
|
|
623
|
-
repoRoot: manifest.repoRoot,
|
|
624
|
-
capabilityStore: createCodexCapabilityStore(),
|
|
625
|
-
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot),
|
|
626
|
-
controllerBinding: {
|
|
627
|
-
instanceDigest: registration.controllerInstanceDigest,
|
|
628
|
-
controlGeneration: registration.controlGeneration
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
if (recovered.status !== 'committed') return null;
|
|
632
|
-
} catch (error) {
|
|
633
|
-
appendBrokerAudit(manifest, 'lifecycle-authority-recovery-failed', {
|
|
634
|
-
...requestAuditFields(manifest, request),
|
|
635
|
-
errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_RECOVERY_FAILED'
|
|
636
|
-
});
|
|
637
|
-
return null;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
463
|
const recovery: RecoveryDomainEvidence = finalization
|
|
641
464
|
? { domain: { consistent: finalization.status === 'matched' } }
|
|
642
|
-
: readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
465
|
+
: await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
643
466
|
const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName!);
|
|
644
467
|
const decision = classifySandboxControlRecovery({
|
|
645
468
|
operation,
|
|
@@ -657,10 +480,10 @@ export function recoveryResponse(
|
|
|
657
480
|
if (finalization?.status !== 'matched') return null;
|
|
658
481
|
return finalization.response ?? null;
|
|
659
482
|
}
|
|
660
|
-
return genericRecoveryResponse(request, evidence.exitCode, payload
|
|
483
|
+
return genericRecoveryResponse(request, evidence.exitCode, payload);
|
|
661
484
|
}
|
|
662
485
|
|
|
663
|
-
function terminalMatchesEvidence(
|
|
486
|
+
async function terminalMatchesEvidence(
|
|
664
487
|
manifest: SandboxControlManifest,
|
|
665
488
|
manifestPath: string,
|
|
666
489
|
request: SandboxControlRequest,
|
|
@@ -669,10 +492,10 @@ function terminalMatchesEvidence(
|
|
|
669
492
|
payload: ReturnType<typeof readSandboxControlPayload> | null,
|
|
670
493
|
payloadInvalid: boolean,
|
|
671
494
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult> | null
|
|
672
|
-
): { valid: boolean; payloadReferenced: boolean } {
|
|
495
|
+
): Promise<{ valid: boolean; payloadReferenced: boolean }> {
|
|
673
496
|
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
674
497
|
if (!terminalResult) return { valid: false, payloadReferenced: false };
|
|
675
|
-
const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
|
|
498
|
+
const expected = await recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
|
|
676
499
|
return {
|
|
677
500
|
valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
|
|
678
501
|
payloadReferenced: false
|
|
@@ -701,7 +524,7 @@ function terminalMatchesEvidence(
|
|
|
701
524
|
if (response.outputState === 'unavailable') {
|
|
702
525
|
const causes = ['recovery', 'publish'] as const;
|
|
703
526
|
const valid = causes.some((cause) => JSON.stringify(response)
|
|
704
|
-
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause
|
|
527
|
+
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
|
|
705
528
|
return { valid, payloadReferenced: false };
|
|
706
529
|
}
|
|
707
530
|
return {
|
|
@@ -723,7 +546,7 @@ function publishExecutionResult(
|
|
|
723
546
|
const terminalResult = writeSandboxControlTerminalResult(manifest, {
|
|
724
547
|
id: request.id,
|
|
725
548
|
family: request.family,
|
|
726
|
-
operation: operationKey(request
|
|
549
|
+
operation: operationKey(request)
|
|
727
550
|
}, normalized.stdout);
|
|
728
551
|
if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
|
|
729
552
|
criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
@@ -757,7 +580,7 @@ function publishExecutionResult(
|
|
|
757
580
|
} catch {
|
|
758
581
|
payload = null;
|
|
759
582
|
}
|
|
760
|
-
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish'
|
|
583
|
+
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
761
584
|
}
|
|
762
585
|
}
|
|
763
586
|
if (!brokerOwns()) return false;
|
|
@@ -848,7 +671,7 @@ function assertCurrentSandboxControlIdentity(manifest: SandboxControlManifest, m
|
|
|
848
671
|
}
|
|
849
672
|
}
|
|
850
673
|
|
|
851
|
-
function recoverProcessing(manifest: SandboxControlManifest, manifestPath: string, broker: BrokerOwner, brokerOwns: () => boolean): boolean {
|
|
674
|
+
async function recoverProcessing(manifest: SandboxControlManifest, manifestPath: string, broker: BrokerOwner, brokerOwns: () => boolean): Promise<boolean> {
|
|
852
675
|
for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
|
|
853
676
|
if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name)) continue;
|
|
854
677
|
if (!brokerOwns()) return false;
|
|
@@ -955,7 +778,7 @@ function recoverProcessing(manifest: SandboxControlManifest, manifestPath: strin
|
|
|
955
778
|
if (terminal && (!request || !resultEvidence)) continue;
|
|
956
779
|
if (terminal && request && resultEvidence) {
|
|
957
780
|
if (!terminalResult && request.family !== 'task-create') continue;
|
|
958
|
-
const reconciliation = terminalMatchesEvidence(
|
|
781
|
+
const reconciliation = await terminalMatchesEvidence(
|
|
959
782
|
manifest,
|
|
960
783
|
manifestPath,
|
|
961
784
|
request,
|
|
@@ -989,7 +812,7 @@ function recoverProcessing(manifest: SandboxControlManifest, manifestPath: strin
|
|
|
989
812
|
if (!terminal) {
|
|
990
813
|
if (!brokerOwns()) return false;
|
|
991
814
|
if (!resultEvidence || !request || payloadInvalid) continue;
|
|
992
|
-
const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult!);
|
|
815
|
+
const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult!);
|
|
993
816
|
if (!recovered) continue;
|
|
994
817
|
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
995
818
|
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
@@ -1140,7 +963,7 @@ export async function serveSandboxControl(
|
|
|
1140
963
|
pid: broker.pid,
|
|
1141
964
|
brokerId: broker.brokerId
|
|
1142
965
|
});
|
|
1143
|
-
if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns)) return;
|
|
966
|
+
if (!await recoverProcessing(manifest, manifestPath, broker, brokerOwns)) return;
|
|
1144
967
|
try {
|
|
1145
968
|
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
1146
969
|
if (published.generation === manifest.generation) taskView = published.taskView;
|
|
@@ -1263,10 +1086,6 @@ export async function serveSandboxControl(
|
|
|
1263
1086
|
if (!brokerOwns()) break;
|
|
1264
1087
|
let terminalCommitted = false;
|
|
1265
1088
|
if (settledExecution.result && settledExecution.resultEvidenceWritten) {
|
|
1266
|
-
if (!consumeLifecycleAuthorityForResult(manifest, settledExecution)) {
|
|
1267
|
-
active = settledExecution;
|
|
1268
|
-
continue;
|
|
1269
|
-
}
|
|
1270
1089
|
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
|
|
1271
1090
|
if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
|
|
1272
1091
|
try {
|
|
@@ -1351,17 +1170,9 @@ export async function serveSandboxControl(
|
|
|
1351
1170
|
executorCwd: manifest.repoRoot,
|
|
1352
1171
|
executorEntry: options.internalCliPath ?? process.argv[1] ?? null
|
|
1353
1172
|
});
|
|
1354
|
-
const lifecycleAuthority = lifecycleAuthorityForRequest(manifest, manifestPath, request);
|
|
1355
|
-
if (lifecycleAuthority?.status === 'rejected') {
|
|
1356
|
-
throw Object.assign(
|
|
1357
|
-
new Error(lifecycleAuthority.error?.message ?? 'lifecycle authority was rejected'),
|
|
1358
|
-
{ code: lifecycleAuthority.error?.code ?? 'LIFECYCLE_AUTHORITY_REJECTED' }
|
|
1359
|
-
);
|
|
1360
|
-
}
|
|
1361
1173
|
prepared = await prepareExecution({
|
|
1362
1174
|
manifest, manifestPath, request, requestPath: claimed,
|
|
1363
|
-
internalCliPath: options.internalCliPath ?? process.argv[1]
|
|
1364
|
-
...(lifecycleAuthority ? { lifecycleAuthority } : {})
|
|
1175
|
+
internalCliPath: options.internalCliPath ?? process.argv[1]!
|
|
1365
1176
|
});
|
|
1366
1177
|
const preparedExecution = prepared;
|
|
1367
1178
|
if (!preparedExecution) throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
|
|
@@ -1376,7 +1187,7 @@ export async function serveSandboxControl(
|
|
|
1376
1187
|
executionPath: executionPath(manifest, request.id)
|
|
1377
1188
|
});
|
|
1378
1189
|
const execution: ActiveExecution = {
|
|
1379
|
-
request, prepared: preparedExecution,
|
|
1190
|
+
request, prepared: preparedExecution, result: null,
|
|
1380
1191
|
resultEvidenceWritten: false, failure: null, settled: false
|
|
1381
1192
|
};
|
|
1382
1193
|
active = execution;
|
|
@@ -1530,8 +1341,7 @@ export async function serveSandboxControl(
|
|
|
1530
1341
|
}
|
|
1531
1342
|
}
|
|
1532
1343
|
if (owned && active.result && active.resultEvidenceWritten) {
|
|
1533
|
-
if (
|
|
1534
|
-
&& publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
|
|
1344
|
+
if (publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
|
|
1535
1345
|
if (brokerOwns()) {
|
|
1536
1346
|
removeAcceptedResponse(manifest, active.request.id);
|
|
1537
1347
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isCompletionEvidence, type CleanCompletionEvidence } from '../../task/orchestration.ts';
|
|
2
2
|
import { parseControlOutput } from '../../task/control-recovery.ts';
|
|
3
|
-
import { isRecoveryWarning } from '../../task/recovery-warning.ts';
|
|
4
3
|
import { execFileSync } from 'node:child_process';
|
|
5
4
|
import fs from 'node:fs';
|
|
6
5
|
import path from 'node:path';
|
|
@@ -15,7 +14,6 @@ import type {
|
|
|
15
14
|
SandboxControlPayload,
|
|
16
15
|
SandboxControlReservation,
|
|
17
16
|
SandboxControlResultEvidence,
|
|
18
|
-
SandboxControlRecoveryWarning,
|
|
19
17
|
SandboxControlStatus
|
|
20
18
|
} from './protocol.ts';
|
|
21
19
|
import {
|
|
@@ -115,7 +113,6 @@ export type SandboxControlTerminalResult = Readonly<{
|
|
|
115
113
|
status: string;
|
|
116
114
|
changed: boolean | null;
|
|
117
115
|
completionEvidence: CleanCompletionEvidence | null;
|
|
118
|
-
warning?: SandboxControlRecoveryWarning | null;
|
|
119
116
|
}>;
|
|
120
117
|
|
|
121
118
|
export function terminalResultPath(manifest: SandboxControlManifest, requestId: string): string {
|
|
@@ -133,7 +130,6 @@ export function createSandboxControlTerminalResult(
|
|
|
133
130
|
? nested.run as Record<string, unknown> : null;
|
|
134
131
|
const rawCompletion = nested.completionEvidence ?? run?.completionEvidence;
|
|
135
132
|
const operation = request.operation ?? (typeof nested.intent === 'string' ? nested.intent : null) ?? '';
|
|
136
|
-
const warning = isRecoveryWarning(nested.warning) ? nested.warning : null;
|
|
137
133
|
return {
|
|
138
134
|
version: 1,
|
|
139
135
|
requestId: request.id,
|
|
@@ -143,8 +139,7 @@ export function createSandboxControlTerminalResult(
|
|
|
143
139
|
targetState: typeof nested.targetState === 'string' ? nested.targetState : null,
|
|
144
140
|
status: typeof nested.status === 'string' ? nested.status : 'completed',
|
|
145
141
|
changed: typeof nested.changed === 'boolean' ? nested.changed : null,
|
|
146
|
-
completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
|
|
147
|
-
...(warning ? { warning } : {})
|
|
142
|
+
completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
|
|
148
143
|
};
|
|
149
144
|
}
|
|
150
145
|
|
|
@@ -178,9 +173,6 @@ export function readSandboxControlTerminalResult(filePath: string): SandboxContr
|
|
|
178
173
|
if (value.completionEvidence !== null && !isCompletionEvidence(value.completionEvidence)) {
|
|
179
174
|
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
180
175
|
}
|
|
181
|
-
if (value.warning !== undefined && value.warning !== null && !isRecoveryWarning(value.warning)) {
|
|
182
|
-
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
183
|
-
}
|
|
184
176
|
if (value.changed === undefined) {
|
|
185
177
|
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
186
178
|
}
|