@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
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { finalizationTerminalResponse } from './finalization-response.ts';
|
|
2
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
3
1
|
import { completedReentryView } from './completed-reentry.ts';
|
|
4
2
|
import fs from 'node:fs';
|
|
5
3
|
import path from 'node:path';
|
|
@@ -12,7 +10,6 @@ import {
|
|
|
12
10
|
SANDBOX_CONTROL_MAX_RESPONSE_BYTES,
|
|
13
11
|
SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES,
|
|
14
12
|
type SandboxControlManifest,
|
|
15
|
-
type SandboxControlRecoveryWarning,
|
|
16
13
|
type SandboxControlRequest,
|
|
17
14
|
type SandboxControlResponse,
|
|
18
15
|
type SandboxControlTimingPolicy
|
|
@@ -62,10 +59,6 @@ import {
|
|
|
62
59
|
operationRecoveryBinding,
|
|
63
60
|
SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES
|
|
64
61
|
} 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
62
|
import { readLifecycleJournalEvidence } from '../../task/lifecycle.ts';
|
|
70
63
|
import { resolveTaskRef } from '../../task/resolve-ref.ts';
|
|
71
64
|
import { loadShortIdByTaskId } from '../../task/short-id.ts';
|
|
@@ -83,75 +76,20 @@ import { readCodexControllerRegistration } from './controller-registration.ts';
|
|
|
83
76
|
import { validateSandboxControlIdentity } from './identity-sentinel.ts';
|
|
84
77
|
import {
|
|
85
78
|
mergeSandboxTaskView,
|
|
86
|
-
taskViewAfterFinalization,
|
|
87
79
|
taskViewForManifest,
|
|
88
80
|
type SandboxTaskView
|
|
89
81
|
} from './task-view.ts';
|
|
90
82
|
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
83
|
|
|
100
84
|
type ActiveExecution = {
|
|
101
85
|
request: SandboxControlRequest;
|
|
102
86
|
prepared: PreparedSandboxControlExecution;
|
|
103
|
-
lifecycleAuthority: LifecycleAuthorityResponseV1 | undefined;
|
|
104
87
|
result: SandboxControlExecutionResult | null;
|
|
105
88
|
resultEvidenceWritten: boolean;
|
|
106
89
|
failure: unknown;
|
|
107
90
|
settled: boolean;
|
|
108
91
|
};
|
|
109
92
|
|
|
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
93
|
function appendBrokerAudit(
|
|
156
94
|
manifest: SandboxControlManifest,
|
|
157
95
|
event: string,
|
|
@@ -160,38 +98,21 @@ function appendBrokerAudit(
|
|
|
160
98
|
appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
|
|
161
99
|
}
|
|
162
100
|
|
|
163
|
-
function operationKey(request: SandboxControlRequest
|
|
101
|
+
function operationKey(request: SandboxControlRequest): string | null {
|
|
164
102
|
if (request.family === 'task-finalization') return request.operation;
|
|
165
103
|
if (request.family === 'task-create') return 'create';
|
|
166
104
|
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
|
-
}
|
|
105
|
+
if (request.family !== 'task-lifecycle') return null;
|
|
174
106
|
try {
|
|
175
107
|
const operation = parseTaskControlOperation(request.family, request.args);
|
|
176
|
-
return operation.family === 'task-
|
|
108
|
+
return operation.family === 'task-lifecycle' ? operation.request.intent : null;
|
|
177
109
|
} catch {
|
|
178
110
|
return null;
|
|
179
111
|
}
|
|
180
112
|
}
|
|
181
113
|
|
|
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;
|
|
114
|
+
function recoveryOperationKey(request: SandboxControlRequest): string | null {
|
|
115
|
+
return operationKey(request);
|
|
195
116
|
}
|
|
196
117
|
|
|
197
118
|
function terminalResultMatchesRequest(
|
|
@@ -199,9 +120,7 @@ function terminalResultMatchesRequest(
|
|
|
199
120
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult>
|
|
200
121
|
): boolean {
|
|
201
122
|
const operation = operationKey(request);
|
|
202
|
-
const candidates = operation
|
|
203
|
-
? ['route.read', 'route.clean-completion']
|
|
204
|
-
: operation ? [operation] : [];
|
|
123
|
+
const candidates = operation ? [operation] : [];
|
|
205
124
|
return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
|
|
206
125
|
}
|
|
207
126
|
|
|
@@ -286,61 +205,6 @@ export function writeSandboxControlResponse(manifest: SandboxControlManifest, re
|
|
|
286
205
|
return true;
|
|
287
206
|
}
|
|
288
207
|
|
|
289
|
-
type FinalizationRecovery = Readonly<{
|
|
290
|
-
status: 'matched' | 'deferred' | 'not-applicable';
|
|
291
|
-
response?: SandboxControlResponse;
|
|
292
|
-
}>;
|
|
293
|
-
|
|
294
|
-
function finalizationRecoveryResponse(
|
|
295
|
-
manifest: SandboxControlManifest,
|
|
296
|
-
requestId: string,
|
|
297
|
-
exitCode: number
|
|
298
|
-
): FinalizationRecovery {
|
|
299
|
-
if (!manifest.taskId || exitCode !== 0) return { status: 'not-applicable' };
|
|
300
|
-
let receipt;
|
|
301
|
-
try {
|
|
302
|
-
receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
303
|
-
if (!receipt || !receipt.controlBinding
|
|
304
|
-
|| receipt.controlBinding.generation !== manifest.generation
|
|
305
|
-
|| receipt.controlBinding.requestId !== requestId) return { status: 'deferred' };
|
|
306
|
-
const resolved = resolveTaskRef(manifest.taskId, { repoRoot: manifest.repoRoot });
|
|
307
|
-
if (!resolved.ok || resolved.state !== 'completed' || receipt.lifecycle !== 'done') return { status: 'deferred' };
|
|
308
|
-
} catch {
|
|
309
|
-
return { status: 'deferred' };
|
|
310
|
-
}
|
|
311
|
-
return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function publishFinalizationTaskView(
|
|
315
|
-
manifest: SandboxControlManifest,
|
|
316
|
-
broker: BrokerOwner,
|
|
317
|
-
requestId: string,
|
|
318
|
-
state: 'starting' | 'healthy' | 'busy' | 'parked',
|
|
319
|
-
reasonCode: string | null,
|
|
320
|
-
activeRequestId: string | null
|
|
321
|
-
): SandboxTaskView {
|
|
322
|
-
let view: SandboxTaskView;
|
|
323
|
-
try {
|
|
324
|
-
const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
|
|
325
|
-
const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
|
|
326
|
-
view = resolved.ok && resolved.state === 'completed'
|
|
327
|
-
? taskViewAfterFinalization({
|
|
328
|
-
taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
|
|
329
|
-
})
|
|
330
|
-
: {
|
|
331
|
-
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
332
|
-
reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
|
|
333
|
-
};
|
|
334
|
-
} catch {
|
|
335
|
-
view = {
|
|
336
|
-
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
337
|
-
reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
|
|
341
|
-
return view;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
208
|
function payloadReference(payload: ReturnType<typeof createSandboxControlPayload>) {
|
|
345
209
|
return {
|
|
346
210
|
version: payload.version,
|
|
@@ -374,8 +238,7 @@ export function genericRecoveryResponse(
|
|
|
374
238
|
request: SandboxControlRequest,
|
|
375
239
|
exitCode: number,
|
|
376
240
|
payload: ReturnType<typeof readSandboxControlPayload> | null,
|
|
377
|
-
cause: 'publish' | 'recovery' = 'recovery'
|
|
378
|
-
warning: SandboxControlRecoveryWarning | null = null
|
|
241
|
+
cause: 'publish' | 'recovery' = 'recovery'
|
|
379
242
|
): SandboxControlResponse {
|
|
380
243
|
const outputUnavailable = request.family === 'task-create' && !payload;
|
|
381
244
|
return {
|
|
@@ -384,9 +247,7 @@ export function genericRecoveryResponse(
|
|
|
384
247
|
phase: 'completed',
|
|
385
248
|
exitCode,
|
|
386
249
|
stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
|
|
387
|
-
stderr: payload ? '' :
|
|
388
|
-
? `${warning.code}: ${warning.message}\nAction: ${warning.action}\n`
|
|
389
|
-
: cause === 'publish'
|
|
250
|
+
stderr: payload ? '' : cause === 'publish'
|
|
390
251
|
? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
|
|
391
252
|
: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
|
|
392
253
|
error: null,
|
|
@@ -465,42 +326,16 @@ function controllerDomainEvidence(
|
|
|
465
326
|
}
|
|
466
327
|
}
|
|
467
328
|
|
|
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(
|
|
329
|
+
async function readRecoveryDomain(
|
|
495
330
|
manifest: SandboxControlManifest,
|
|
496
331
|
manifestPath: string,
|
|
497
332
|
request: SandboxControlRequest,
|
|
498
333
|
operation: ReturnType<typeof findSandboxControlRecoveryOperation>,
|
|
499
334
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult>,
|
|
500
335
|
payloadOutput: string | null
|
|
501
|
-
): RecoveryDomainEvidence {
|
|
336
|
+
): Promise<RecoveryDomainEvidence> {
|
|
502
337
|
if (!operation) return { domain: null };
|
|
503
|
-
const taskRef = request.family === 'task-finalization'
|
|
338
|
+
const taskRef = request.family === 'task-finalization'
|
|
504
339
|
? manifest.taskId
|
|
505
340
|
: 'args' in request ? request.args[0] ?? null : manifest.taskId;
|
|
506
341
|
const output = parseControlOutput(payloadOutput);
|
|
@@ -510,19 +345,7 @@ function readRecoveryDomain(
|
|
|
510
345
|
|
|
511
346
|
if (operation.family === 'task-lifecycle') {
|
|
512
347
|
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
|
-
}
|
|
348
|
+
return { domain: null };
|
|
526
349
|
}
|
|
527
350
|
if (!taskRef || !terminalResult.targetState) return { domain: null };
|
|
528
351
|
const journal = readLifecycleJournalEvidence(manifest.repoRoot, taskRef);
|
|
@@ -541,48 +364,12 @@ function readRecoveryDomain(
|
|
|
541
364
|
}
|
|
542
365
|
}
|
|
543
366
|
|
|
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
367
|
if (operation.family === 'task-create') {
|
|
576
368
|
return { domain: taskCreateDomainEvidence(manifest, output) };
|
|
577
369
|
}
|
|
578
370
|
if (operation.family === 'codex-controller') {
|
|
579
371
|
return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
|
|
580
372
|
}
|
|
581
|
-
if (operation.family === 'task-workflow') {
|
|
582
|
-
return {
|
|
583
|
-
domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
373
|
return { domain: null };
|
|
587
374
|
}
|
|
588
375
|
|
|
@@ -597,15 +384,15 @@ function readCommittedCriticalPhases(manifest: SandboxControlManifest, requestId
|
|
|
597
384
|
});
|
|
598
385
|
}
|
|
599
386
|
|
|
600
|
-
export function recoveryResponse(
|
|
387
|
+
export async function recoveryResponse(
|
|
601
388
|
manifest: SandboxControlManifest,
|
|
602
389
|
manifestPath: string,
|
|
603
390
|
request: SandboxControlRequest,
|
|
604
391
|
evidence: ReturnType<typeof readSandboxControlResultEvidence>,
|
|
605
392
|
payload: ReturnType<typeof readSandboxControlPayload> | null,
|
|
606
393
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult>
|
|
607
|
-
): SandboxControlResponse | null {
|
|
608
|
-
const operationName = recoveryOperationKey(request
|
|
394
|
+
): Promise<SandboxControlResponse | null> {
|
|
395
|
+
const operationName = recoveryOperationKey(request);
|
|
609
396
|
const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
|
|
610
397
|
if (!operation) return null;
|
|
611
398
|
if (payload) {
|
|
@@ -614,32 +401,7 @@ export function recoveryResponse(
|
|
|
614
401
|
}, payload.stdout);
|
|
615
402
|
if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult)) return unknown(request.id);
|
|
616
403
|
}
|
|
617
|
-
const
|
|
618
|
-
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
|
-
const recovery: RecoveryDomainEvidence = finalization
|
|
641
|
-
? { domain: { consistent: finalization.status === 'matched' } }
|
|
642
|
-
: readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
404
|
+
const recovery = await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
643
405
|
const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName!);
|
|
644
406
|
const decision = classifySandboxControlRecovery({
|
|
645
407
|
operation,
|
|
@@ -653,31 +415,18 @@ export function recoveryResponse(
|
|
|
653
415
|
if (decision.outcome === 'unknown' || decision.outcome === 'in-progress') return unknown(request.id);
|
|
654
416
|
if (decision.outcome === 'not-executed') return notExecuted(request.id);
|
|
655
417
|
if (decision.outcome !== 'success' && decision.outcome !== 'failure') return null;
|
|
656
|
-
|
|
657
|
-
if (finalization?.status !== 'matched') return null;
|
|
658
|
-
return finalization.response ?? null;
|
|
659
|
-
}
|
|
660
|
-
return genericRecoveryResponse(request, evidence.exitCode, payload, 'recovery', terminalResult.warning ?? null);
|
|
418
|
+
return genericRecoveryResponse(request, evidence.exitCode, payload);
|
|
661
419
|
}
|
|
662
420
|
|
|
663
|
-
function terminalMatchesEvidence(
|
|
421
|
+
async function terminalMatchesEvidence(
|
|
664
422
|
manifest: SandboxControlManifest,
|
|
665
|
-
manifestPath: string,
|
|
666
423
|
request: SandboxControlRequest,
|
|
667
424
|
response: SandboxControlResponse,
|
|
668
425
|
evidence: ReturnType<typeof readSandboxControlResultEvidence>,
|
|
669
426
|
payload: ReturnType<typeof readSandboxControlPayload> | null,
|
|
670
427
|
payloadInvalid: boolean,
|
|
671
428
|
terminalResult: ReturnType<typeof readSandboxControlTerminalResult> | null
|
|
672
|
-
): { valid: boolean; payloadReferenced: boolean } {
|
|
673
|
-
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
674
|
-
if (!terminalResult) return { valid: false, payloadReferenced: false };
|
|
675
|
-
const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
|
|
676
|
-
return {
|
|
677
|
-
valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
|
|
678
|
-
payloadReferenced: false
|
|
679
|
-
};
|
|
680
|
-
}
|
|
429
|
+
): Promise<{ valid: boolean; payloadReferenced: boolean }> {
|
|
681
430
|
if (response.version !== 2 || response.id !== request.id || response.phase !== 'completed'
|
|
682
431
|
|| response.exitCode !== evidence.exitCode || response.error !== null) {
|
|
683
432
|
return { valid: false, payloadReferenced: false };
|
|
@@ -701,7 +450,7 @@ function terminalMatchesEvidence(
|
|
|
701
450
|
if (response.outputState === 'unavailable') {
|
|
702
451
|
const causes = ['recovery', 'publish'] as const;
|
|
703
452
|
const valid = causes.some((cause) => JSON.stringify(response)
|
|
704
|
-
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause
|
|
453
|
+
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
|
|
705
454
|
return { valid, payloadReferenced: false };
|
|
706
455
|
}
|
|
707
456
|
return {
|
|
@@ -716,55 +465,39 @@ function publishExecutionResult(
|
|
|
716
465
|
manifest: SandboxControlManifest,
|
|
717
466
|
request: SandboxControlRequest,
|
|
718
467
|
result: SandboxControlExecutionResult,
|
|
719
|
-
broker: BrokerOwner,
|
|
720
468
|
brokerOwns: () => boolean
|
|
721
469
|
): boolean {
|
|
722
470
|
const normalized = sanitizeSandboxControlResult(manifest, result);
|
|
723
471
|
const terminalResult = writeSandboxControlTerminalResult(manifest, {
|
|
724
472
|
id: request.id,
|
|
725
473
|
family: request.family,
|
|
726
|
-
operation: operationKey(request
|
|
474
|
+
operation: operationKey(request)
|
|
727
475
|
}, normalized.stdout);
|
|
728
476
|
if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
|
|
729
477
|
criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
730
478
|
criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
731
479
|
criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
732
480
|
}
|
|
733
|
-
let terminal: SandboxControlResponse
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
if (!terminal) {
|
|
740
|
-
const inline: SandboxControlResponse = {
|
|
741
|
-
version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
|
|
742
|
-
stdout: normalized.stdout, stderr: normalized.stderr, error: null
|
|
743
|
-
};
|
|
744
|
-
if (sandboxControlEncodedJsonBytes(inline) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
|
|
745
|
-
terminal = inline;
|
|
746
|
-
}
|
|
481
|
+
let terminal: SandboxControlResponse = {
|
|
482
|
+
version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
|
|
483
|
+
stdout: normalized.stdout, stderr: normalized.stderr, error: null
|
|
484
|
+
};
|
|
485
|
+
if (sandboxControlEncodedJsonBytes(terminal) > SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
|
|
747
486
|
let payload: ReturnType<typeof createSandboxControlPayload> | null = null;
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
} else {
|
|
755
|
-
payload = null;
|
|
756
|
-
}
|
|
757
|
-
} catch {
|
|
487
|
+
try {
|
|
488
|
+
payload = createSandboxControlPayload(manifest, request.id, normalized);
|
|
489
|
+
const usage = sandboxControlGenerationUsage(manifest);
|
|
490
|
+
if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
|
|
491
|
+
writeSandboxControlPayload(manifest, request.id, normalized);
|
|
492
|
+
} else {
|
|
758
493
|
payload = null;
|
|
759
494
|
}
|
|
760
|
-
|
|
495
|
+
} catch {
|
|
496
|
+
payload = null;
|
|
761
497
|
}
|
|
498
|
+
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
762
499
|
}
|
|
763
500
|
if (!brokerOwns()) return false;
|
|
764
|
-
if (request.family === 'task-finalization' && normalized.exitCode === 0) {
|
|
765
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
|
|
766
|
-
if (view.state === 'unknown') return false;
|
|
767
|
-
}
|
|
768
501
|
const committed = writeSandboxControlResponse(manifest, terminal);
|
|
769
502
|
if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
|
|
770
503
|
criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
@@ -848,7 +581,7 @@ function assertCurrentSandboxControlIdentity(manifest: SandboxControlManifest, m
|
|
|
848
581
|
}
|
|
849
582
|
}
|
|
850
583
|
|
|
851
|
-
function recoverProcessing(manifest: SandboxControlManifest, manifestPath: string, broker: BrokerOwner, brokerOwns: () => boolean): boolean {
|
|
584
|
+
async function recoverProcessing(manifest: SandboxControlManifest, manifestPath: string, broker: BrokerOwner, brokerOwns: () => boolean): Promise<boolean> {
|
|
852
585
|
for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
|
|
853
586
|
if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name)) continue;
|
|
854
587
|
if (!brokerOwns()) return false;
|
|
@@ -955,9 +688,8 @@ function recoverProcessing(manifest: SandboxControlManifest, manifestPath: strin
|
|
|
955
688
|
if (terminal && (!request || !resultEvidence)) continue;
|
|
956
689
|
if (terminal && request && resultEvidence) {
|
|
957
690
|
if (!terminalResult && request.family !== 'task-create') continue;
|
|
958
|
-
const reconciliation = terminalMatchesEvidence(
|
|
691
|
+
const reconciliation = await terminalMatchesEvidence(
|
|
959
692
|
manifest,
|
|
960
|
-
manifestPath,
|
|
961
693
|
request,
|
|
962
694
|
existingTerminalResponse!,
|
|
963
695
|
resultEvidence,
|
|
@@ -968,10 +700,6 @@ function recoverProcessing(manifest: SandboxControlManifest, manifestPath: strin
|
|
|
968
700
|
if (!reconciliation.valid) continue;
|
|
969
701
|
terminalReconciled = true;
|
|
970
702
|
payloadReferenced = reconciliation.payloadReferenced;
|
|
971
|
-
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
972
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
973
|
-
if (view.state === 'unknown') continue;
|
|
974
|
-
}
|
|
975
703
|
}
|
|
976
704
|
if (!terminalResult && !terminalReconciled) {
|
|
977
705
|
if (terminal) continue;
|
|
@@ -989,19 +717,15 @@ function recoverProcessing(manifest: SandboxControlManifest, manifestPath: strin
|
|
|
989
717
|
if (!terminal) {
|
|
990
718
|
if (!brokerOwns()) return false;
|
|
991
719
|
if (!resultEvidence || !request || payloadInvalid) continue;
|
|
992
|
-
const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult!);
|
|
720
|
+
const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult!);
|
|
993
721
|
if (!recovered) continue;
|
|
994
|
-
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
995
|
-
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
996
|
-
if (view.state === 'unknown') continue;
|
|
997
|
-
}
|
|
998
722
|
writeSandboxControlResponse(manifest, recovered);
|
|
999
723
|
const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
|
|
1000
724
|
if (transitionProtocolActive && !preserveRecoveryEvidence) {
|
|
1001
725
|
writeSandboxControlTransition(manifest, { requestId: entry.name, phase: 'recovered' });
|
|
1002
726
|
}
|
|
1003
727
|
if (preserveRecoveryEvidence) continue;
|
|
1004
|
-
payloadReferenced = Boolean(payload)
|
|
728
|
+
payloadReferenced = Boolean(payload);
|
|
1005
729
|
}
|
|
1006
730
|
} else {
|
|
1007
731
|
if (!brokerOwns()) return false;
|
|
@@ -1099,14 +823,10 @@ export async function serveSandboxControl(
|
|
|
1099
823
|
try {
|
|
1100
824
|
startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
1101
825
|
} catch {
|
|
1102
|
-
// Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
|
|
1103
826
|
startupReceipt = {};
|
|
1104
827
|
}
|
|
1105
828
|
}
|
|
1106
|
-
let taskView = taskViewForManifest({
|
|
1107
|
-
...manifest,
|
|
1108
|
-
receipt: startupReceipt
|
|
1109
|
-
});
|
|
829
|
+
let taskView = taskViewForManifest({ ...manifest, receipt: startupReceipt });
|
|
1110
830
|
try {
|
|
1111
831
|
const previous = readSandboxControlStatus(manifest.publicStatusDir);
|
|
1112
832
|
if (previous.generation === manifest.generation) taskView = mergeSandboxTaskView(taskView, previous.taskView);
|
|
@@ -1140,7 +860,7 @@ export async function serveSandboxControl(
|
|
|
1140
860
|
pid: broker.pid,
|
|
1141
861
|
brokerId: broker.brokerId
|
|
1142
862
|
});
|
|
1143
|
-
if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns)) return;
|
|
863
|
+
if (!await recoverProcessing(manifest, manifestPath, broker, brokerOwns)) return;
|
|
1144
864
|
try {
|
|
1145
865
|
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
1146
866
|
if (published.generation === manifest.generation) taskView = published.taskView;
|
|
@@ -1263,19 +983,7 @@ export async function serveSandboxControl(
|
|
|
1263
983
|
if (!brokerOwns()) break;
|
|
1264
984
|
let terminalCommitted = false;
|
|
1265
985
|
if (settledExecution.result && settledExecution.resultEvidenceWritten) {
|
|
1266
|
-
|
|
1267
|
-
active = settledExecution;
|
|
1268
|
-
continue;
|
|
1269
|
-
}
|
|
1270
|
-
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
|
|
1271
|
-
if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
|
|
1272
|
-
try {
|
|
1273
|
-
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
1274
|
-
if (published.generation === manifest.generation) taskView = published.taskView;
|
|
1275
|
-
} catch {
|
|
1276
|
-
taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
986
|
+
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
|
|
1279
987
|
} else {
|
|
1280
988
|
terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
|
|
1281
989
|
}
|
|
@@ -1351,17 +1059,9 @@ export async function serveSandboxControl(
|
|
|
1351
1059
|
executorCwd: manifest.repoRoot,
|
|
1352
1060
|
executorEntry: options.internalCliPath ?? process.argv[1] ?? null
|
|
1353
1061
|
});
|
|
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
1062
|
prepared = await prepareExecution({
|
|
1362
1063
|
manifest, manifestPath, request, requestPath: claimed,
|
|
1363
|
-
internalCliPath: options.internalCliPath ?? process.argv[1]
|
|
1364
|
-
...(lifecycleAuthority ? { lifecycleAuthority } : {})
|
|
1064
|
+
internalCliPath: options.internalCliPath ?? process.argv[1]!
|
|
1365
1065
|
});
|
|
1366
1066
|
const preparedExecution = prepared;
|
|
1367
1067
|
if (!preparedExecution) throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
|
|
@@ -1376,7 +1076,7 @@ export async function serveSandboxControl(
|
|
|
1376
1076
|
executionPath: executionPath(manifest, request.id)
|
|
1377
1077
|
});
|
|
1378
1078
|
const execution: ActiveExecution = {
|
|
1379
|
-
request, prepared: preparedExecution,
|
|
1079
|
+
request, prepared: preparedExecution, result: null,
|
|
1380
1080
|
resultEvidenceWritten: false, failure: null, settled: false
|
|
1381
1081
|
};
|
|
1382
1082
|
active = execution;
|
|
@@ -1530,8 +1230,7 @@ export async function serveSandboxControl(
|
|
|
1530
1230
|
}
|
|
1531
1231
|
}
|
|
1532
1232
|
if (owned && active.result && active.resultEvidenceWritten) {
|
|
1533
|
-
if (
|
|
1534
|
-
&& publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
|
|
1233
|
+
if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
|
|
1535
1234
|
if (brokerOwns()) {
|
|
1536
1235
|
removeAcceptedResponse(manifest, active.request.id);
|
|
1537
1236
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -1540,22 +1239,8 @@ export async function serveSandboxControl(
|
|
|
1540
1239
|
}
|
|
1541
1240
|
}
|
|
1542
1241
|
if (active) {
|
|
1543
|
-
|
|
1544
|
-
if (owned && brokerOwns() && active
|
|
1545
|
-
const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
|
|
1546
|
-
const view = recovered.status === 'matched'
|
|
1547
|
-
? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
|
|
1548
|
-
: null;
|
|
1549
|
-
if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
|
|
1550
|
-
&& writeSandboxControlResponse(manifest, recovered.response)) {
|
|
1551
|
-
if (terminationConfirmed && brokerOwns()) {
|
|
1552
|
-
removeAcceptedResponse(manifest, active.request.id);
|
|
1553
|
-
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
1554
|
-
active = null;
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
if (owned && brokerOwns() && active && active.request.family !== 'task-finalization' && !active.resultEvidenceWritten) {
|
|
1242
|
+
active.prepared.terminate(owned);
|
|
1243
|
+
if (owned && brokerOwns() && active && !active.resultEvidenceWritten) {
|
|
1559
1244
|
writeSandboxControlResponse(manifest, unknown(active.request.id));
|
|
1560
1245
|
}
|
|
1561
1246
|
}
|