@fitlab-ai/agent-infra 0.11.1 → 0.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/platform-comment.js +59 -0
- package/dist/lib/internal/sandbox-control.js +2 -171
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +7 -2
- package/dist/lib/internal/task-review.js +5 -5
- package/dist/lib/internal/task-validate.js +0 -6
- package/dist/lib/platform/comment-safety.js +7 -1
- package/dist/lib/platform/github-provider.js +13 -3
- package/dist/lib/platform/issue-comments.js +93 -15
- package/dist/lib/platform/pr-summary.js +7 -2
- package/dist/lib/platform/provider-contract.d.ts +2 -0
- package/dist/lib/platform/provider-validation.js +7 -2
- package/dist/lib/platform/verification-sync.js +19 -2
- package/dist/lib/sandbox/control/client.js +3 -44
- package/dist/lib/sandbox/control/executor.js +14 -117
- package/dist/lib/sandbox/control/finalization-response.js +8 -2
- 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 +2 -1
- package/dist/lib/task/artifact-command.js +6 -6
- package/dist/lib/task/codex-orchestration.js +10 -208
- package/dist/lib/task/commit-operation.js +3 -18
- package/dist/lib/task/control-authority.js +35 -44
- package/dist/lib/task/control-recovery.js +3 -42
- package/dist/lib/task/delegation-receipts.js +16 -78
- package/dist/lib/task/events.js +89 -237
- package/dist/lib/task/finalization-artifacts.js +1 -3
- package/dist/lib/task/finalization-state.js +2 -1
- package/dist/lib/task/finalization.js +208 -57
- package/dist/lib/task/lifecycle-execution.js +2 -11
- package/dist/lib/task/lifecycle.js +1 -1
- package/dist/lib/task/local-artifact-finalization.js +25 -242
- package/dist/lib/task/manual-validation-receipt.js +8 -3
- package/dist/lib/task/orchestration.js +12 -24
- package/dist/lib/task/review-command.js +0 -1
- package/dist/lib/task/review-finalization.js +46 -211
- 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/platform-comment.ts +38 -0
- package/lib/internal/sandbox-control.ts +1 -185
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-lifecycle.ts +2 -2
- package/lib/internal/task-operation-registry.ts +8 -4
- package/lib/internal/task-review.ts +5 -5
- package/lib/internal/task-validate.ts +0 -5
- package/lib/platform/comment-safety.ts +8 -0
- package/lib/platform/github-provider.ts +11 -2
- package/lib/platform/issue-comments.ts +102 -17
- package/lib/platform/pr-summary.ts +10 -2
- package/lib/platform/provider-contract.ts +2 -0
- package/lib/platform/provider-validation.ts +6 -2
- package/lib/platform/verification-sync.ts +19 -2
- package/lib/sandbox/control/client.ts +5 -68
- package/lib/sandbox/control/executor.ts +14 -125
- package/lib/sandbox/control/finalization-response.ts +8 -2
- 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 +2 -1
- package/lib/task/artifact-command.ts +6 -7
- package/lib/task/codex-orchestration.ts +8 -241
- package/lib/task/commit-operation.ts +3 -23
- package/lib/task/control-authority.ts +39 -55
- package/lib/task/control-recovery.ts +3 -43
- package/lib/task/delegation-receipts.ts +14 -81
- package/lib/task/events.ts +106 -255
- package/lib/task/finalization-artifacts.ts +1 -3
- package/lib/task/finalization-state.ts +2 -1
- package/lib/task/finalization.ts +221 -65
- package/lib/task/lifecycle-execution.ts +10 -21
- package/lib/task/lifecycle.ts +2 -2
- package/lib/task/local-artifact-finalization.ts +23 -281
- package/lib/task/manual-validation-receipt.ts +8 -2
- package/lib/task/orchestration.ts +30 -53
- package/lib/task/review-command.ts +0 -1
- package/lib/task/review-finalization.ts +45 -193
- package/package.json +2 -2
- package/templates/.agents/rules/lifecycle-orchestration.en.md +23 -9
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +23 -9
- package/templates/.agents/rules/local-artifact-repair.en.md +14 -60
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -60
- package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
- 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 +3 -3
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-code/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +2 -2
- 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 +10 -9
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +10 -9
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
- package/dist/lib/sandbox/control/task-workflow.js +0 -74
- package/dist/lib/sandbox/control/workflow-executor.js +0 -157
- 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 -18
- package/dist/lib/task/workflow-dispatch.js +0 -161
- package/lib/sandbox/control/task-workflow.ts +0 -95
- package/lib/sandbox/control/workflow-executor.ts +0 -183
- package/lib/task/lifecycle-recovery.ts +0 -691
- package/lib/task/recovery-warning.ts +0 -21
- package/lib/task/workflow-command.ts +0 -19
- package/lib/task/workflow-dispatch.ts +0 -171
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { finalizationTerminalResponse } from './finalization-response.js';
|
|
2
|
-
import { isDeepStrictEqual } from 'node:util';
|
|
3
2
|
import { completedReentryView } from './completed-reentry.js';
|
|
4
3
|
import fs from 'node:fs';
|
|
5
4
|
import path from 'node:path';
|
|
@@ -13,10 +12,6 @@ import { validateSandboxControlRequest } from './protocol.js';
|
|
|
13
12
|
import { requestAuditFields, resultAuditFields, appendCriticalAudit, appendDiagnosticAudit, createSandboxControlAuditContext, writeSandboxControlTransition, readSandboxControlTransition } from './audit.js';
|
|
14
13
|
import { parseTaskControlOperation } from '../../task/control-authority.js';
|
|
15
14
|
import { parseControlOutput, classifySandboxControlRecovery, findSandboxControlRecoveryOperation, operationRecoveryBinding, SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES } from '../../task/control-recovery.js';
|
|
16
|
-
import { readRun } from '../../task/orchestration.js';
|
|
17
|
-
import { readLifecycleRecoveryDomainEvidence } from '../../task/lifecycle-recovery.js';
|
|
18
|
-
import { captureRepositorySnapshot } from '../../task/workspace-snapshot.js';
|
|
19
|
-
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
|
|
20
15
|
import { readLifecycleJournalEvidence } from '../../task/lifecycle.js';
|
|
21
16
|
import { resolveTaskRef } from '../../task/resolve-ref.js';
|
|
22
17
|
import { loadShortIdByTaskId } from '../../task/short-id.js';
|
|
@@ -28,94 +23,32 @@ import { readCodexControllerRegistration } from './controller-registration.js';
|
|
|
28
23
|
import { validateSandboxControlIdentity } from './identity-sentinel.js';
|
|
29
24
|
import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
|
|
30
25
|
import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
|
|
31
|
-
import { consumeLifecycleRecoveryAttestation, issueLifecycleRecoveryAttestation, recoverLifecycleRecoveryOperation } from '../../task/control-authority.js';
|
|
32
|
-
import { createCodexCapabilityStore } from '../../agent-clients/adapters/codex-lifecycle/capability-store.js';
|
|
33
|
-
import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
|
|
34
|
-
function consumeLifecycleAuthorityForResult(manifest, execution) {
|
|
35
|
-
const authority = execution.lifecycleAuthority?.attestation;
|
|
36
|
-
if (!authority || execution.result?.exitCode !== 0)
|
|
37
|
-
return true;
|
|
38
|
-
try {
|
|
39
|
-
consumeLifecycleRecoveryAttestation(authority);
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
appendBrokerAudit(manifest, 'lifecycle-authority-consume-retry', {
|
|
44
|
-
...requestAuditFields(manifest, execution.request),
|
|
45
|
-
errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_CONSUME_FAILED'
|
|
46
|
-
});
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function lifecycleAuthorityForRequest(manifest, manifestPath, request) {
|
|
51
|
-
const selector = request.family === 'task-workflow' || request.family === 'task-orchestration'
|
|
52
|
-
? request.authority
|
|
53
|
-
: undefined;
|
|
54
|
-
if (!selector)
|
|
55
|
-
return undefined;
|
|
56
|
-
let controllerBinding = null;
|
|
57
|
-
try {
|
|
58
|
-
const registration = readCodexControllerRegistration(manifestPath);
|
|
59
|
-
controllerBinding = {
|
|
60
|
-
instanceDigest: registration.controllerInstanceDigest,
|
|
61
|
-
controlGeneration: registration.controlGeneration
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// The issuer reports the stable unavailable result; the broker never trusts
|
|
66
|
-
// a controller binding supplied by the request.
|
|
67
|
-
}
|
|
68
|
-
return issueLifecycleRecoveryAttestation(selector, {
|
|
69
|
-
controllerBinding,
|
|
70
|
-
capabilityStore: createCodexCapabilityStore(),
|
|
71
|
-
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
26
|
function appendBrokerAudit(manifest, event, fields = {}) {
|
|
75
27
|
appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
|
|
76
28
|
}
|
|
77
|
-
function operationKey(request
|
|
29
|
+
function operationKey(request) {
|
|
78
30
|
if (request.family === 'task-finalization')
|
|
79
31
|
return request.operation;
|
|
80
32
|
if (request.family === 'task-create')
|
|
81
33
|
return 'create';
|
|
82
34
|
if (request.family === 'codex-controller')
|
|
83
35
|
return request.command;
|
|
84
|
-
if (request.family
|
|
85
|
-
return request.workflow.operation;
|
|
86
|
-
if (request.family !== 'task-lifecycle' && request.family !== 'task-orchestration')
|
|
36
|
+
if (request.family !== 'task-lifecycle')
|
|
87
37
|
return null;
|
|
88
|
-
if (request.family === 'task-orchestration' && request.args[1] === 'route') {
|
|
89
|
-
if (!output)
|
|
90
|
-
return 'route';
|
|
91
|
-
const result = parseControlOutput(output);
|
|
92
|
-
return result?.changed === true && result.status === 'completed' ? 'route.clean-completion' : 'route.read';
|
|
93
|
-
}
|
|
94
38
|
try {
|
|
95
39
|
const operation = parseTaskControlOperation(request.family, request.args);
|
|
96
|
-
return operation.family === 'task-
|
|
40
|
+
return operation.family === 'task-lifecycle' ? operation.request.intent : null;
|
|
97
41
|
}
|
|
98
42
|
catch {
|
|
99
43
|
return null;
|
|
100
44
|
}
|
|
101
45
|
}
|
|
102
|
-
function recoveryOperationKey(request
|
|
103
|
-
|
|
104
|
-
if (operation !== 'route')
|
|
105
|
-
return operation;
|
|
106
|
-
const digest = terminalResult?.intentDigest;
|
|
107
|
-
for (const candidate of ['route.read', 'route.clean-completion']) {
|
|
108
|
-
const expected = operationRecoveryBinding(request.id, request.generation, null, request.family, candidate).intentDigest;
|
|
109
|
-
if (digest === expected)
|
|
110
|
-
return candidate;
|
|
111
|
-
}
|
|
112
|
-
return null;
|
|
46
|
+
function recoveryOperationKey(request) {
|
|
47
|
+
return operationKey(request);
|
|
113
48
|
}
|
|
114
49
|
function terminalResultMatchesRequest(request, terminalResult) {
|
|
115
50
|
const operation = operationKey(request);
|
|
116
|
-
const candidates = operation
|
|
117
|
-
? ['route.read', 'route.clean-completion']
|
|
118
|
-
: operation ? [operation] : [];
|
|
51
|
+
const candidates = operation ? [operation] : [];
|
|
119
52
|
return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
|
|
120
53
|
}
|
|
121
54
|
function criticalRequestPhase(manifest, request, phase, outcome, reference) {
|
|
@@ -257,7 +190,7 @@ function outputMatchesEvidence(output, bytes, sha256) {
|
|
|
257
190
|
return Buffer.byteLength(output, 'utf8') === bytes
|
|
258
191
|
&& createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
|
|
259
192
|
}
|
|
260
|
-
export function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery'
|
|
193
|
+
export function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
|
|
261
194
|
const outputUnavailable = request.family === 'task-create' && !payload;
|
|
262
195
|
return {
|
|
263
196
|
version: 2,
|
|
@@ -265,11 +198,9 @@ export function genericRecoveryResponse(request, exitCode, payload, cause = 'rec
|
|
|
265
198
|
phase: 'completed',
|
|
266
199
|
exitCode,
|
|
267
200
|
stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
|
|
268
|
-
stderr: payload ? '' :
|
|
269
|
-
?
|
|
270
|
-
:
|
|
271
|
-
? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
|
|
272
|
-
: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
|
|
201
|
+
stderr: payload ? '' : cause === 'publish'
|
|
202
|
+
? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
|
|
203
|
+
: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
|
|
273
204
|
error: null,
|
|
274
205
|
outputState: payload ? 'available' : 'unavailable',
|
|
275
206
|
payload: payload ? payloadReference(payload) : null
|
|
@@ -335,32 +266,10 @@ function controllerDomainEvidence(manifest, manifestPath, request, output) {
|
|
|
335
266
|
&& output?.status === 'closed' && typeof output.changed === 'boolean' };
|
|
336
267
|
}
|
|
337
268
|
}
|
|
338
|
-
function
|
|
339
|
-
if (!operation || !output)
|
|
340
|
-
return { consistent: false };
|
|
341
|
-
const outputRun = output.run && typeof output.run === 'object' && !Array.isArray(output.run)
|
|
342
|
-
? output.run : null;
|
|
343
|
-
const runMatches = outputRun === null ? run === null : run !== null && JSON.stringify(run) === JSON.stringify(outputRun);
|
|
344
|
-
if (operation.class === 'read-only') {
|
|
345
|
-
return {
|
|
346
|
-
consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
|
|
347
|
-
snapshotValid: true,
|
|
348
|
-
status: run?.status ?? terminalResult.status,
|
|
349
|
-
pendingDelegation: run?.pendingDelegation ?? null
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
if (!run || !outputRun)
|
|
353
|
-
return { consistent: false };
|
|
354
|
-
return {
|
|
355
|
-
consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
|
|
356
|
-
status: run.status,
|
|
357
|
-
pendingDelegation: run.pendingDelegation
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
|
|
269
|
+
async function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
|
|
361
270
|
if (!operation)
|
|
362
271
|
return { domain: null };
|
|
363
|
-
const taskRef = request.family === 'task-finalization'
|
|
272
|
+
const taskRef = request.family === 'task-finalization'
|
|
364
273
|
? manifest.taskId
|
|
365
274
|
: 'args' in request ? request.args[0] ?? null : manifest.taskId;
|
|
366
275
|
const output = parseControlOutput(payloadOutput);
|
|
@@ -369,21 +278,7 @@ function readRecoveryDomain(manifest, manifestPath, request, operation, terminal
|
|
|
369
278
|
}
|
|
370
279
|
if (operation.family === 'task-lifecycle') {
|
|
371
280
|
if (operation.intent === 'recover-started') {
|
|
372
|
-
|
|
373
|
-
if (!('args' in request))
|
|
374
|
-
return { domain: null };
|
|
375
|
-
const parsed = parseTaskControlOperation('task-lifecycle', request.args);
|
|
376
|
-
if (parsed.family !== 'task-lifecycle' || parsed.request.intent !== 'recover-started') {
|
|
377
|
-
return { domain: null };
|
|
378
|
-
}
|
|
379
|
-
return {
|
|
380
|
-
domain: readLifecycleRecoveryDomainEvidence(manifest.repoRoot, parsed.request, terminalResult),
|
|
381
|
-
journal: readLifecycleJournalEvidence(manifest.repoRoot, taskRef)
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
catch {
|
|
385
|
-
return { domain: null };
|
|
386
|
-
}
|
|
281
|
+
return { domain: null };
|
|
387
282
|
}
|
|
388
283
|
if (!taskRef || !terminalResult.targetState)
|
|
389
284
|
return { domain: null };
|
|
@@ -403,51 +298,12 @@ function readRecoveryDomain(manifest, manifestPath, request, operation, terminal
|
|
|
403
298
|
return { domain: { consistent: false }, journal };
|
|
404
299
|
}
|
|
405
300
|
}
|
|
406
|
-
if (operation.family === 'task-orchestration') {
|
|
407
|
-
if (!taskRef)
|
|
408
|
-
return { domain: null };
|
|
409
|
-
try {
|
|
410
|
-
const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
|
|
411
|
-
if (!resolved.ok)
|
|
412
|
-
return { domain: { consistent: false } };
|
|
413
|
-
const run = readRun(resolved.taskDir);
|
|
414
|
-
if (!run && operation.class !== 'read-only')
|
|
415
|
-
return { domain: { consistent: false } };
|
|
416
|
-
if (operation.class === 'route.clean-completion') {
|
|
417
|
-
const snapshot = captureRepositorySnapshot(manifest.repoRoot);
|
|
418
|
-
const metadata = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
|
|
419
|
-
const completion = run?.completionEvidence ?? null;
|
|
420
|
-
const consistent = run !== null && completion !== null
|
|
421
|
-
&& terminalResult.completionEvidence !== null
|
|
422
|
-
&& isDeepStrictEqual(completion, terminalResult.completionEvidence);
|
|
423
|
-
return {
|
|
424
|
-
domain: {
|
|
425
|
-
consistent,
|
|
426
|
-
status: run?.status ?? null,
|
|
427
|
-
pendingDelegation: run?.pendingDelegation ?? null,
|
|
428
|
-
completionEvidence: completion,
|
|
429
|
-
snapshot,
|
|
430
|
-
lastReviewedCommit: metadata.last_reviewed_commit
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
return { domain: orchestrationDomainEvidence(operation, terminalResult, output, run) };
|
|
435
|
-
}
|
|
436
|
-
catch {
|
|
437
|
-
return { domain: { consistent: false } };
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
301
|
if (operation.family === 'task-create') {
|
|
441
302
|
return { domain: taskCreateDomainEvidence(manifest, output) };
|
|
442
303
|
}
|
|
443
304
|
if (operation.family === 'codex-controller') {
|
|
444
305
|
return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
|
|
445
306
|
}
|
|
446
|
-
if (operation.family === 'task-workflow') {
|
|
447
|
-
return {
|
|
448
|
-
domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
307
|
return { domain: null };
|
|
452
308
|
}
|
|
453
309
|
function readCommittedCriticalPhases(manifest, requestId) {
|
|
@@ -461,8 +317,8 @@ function readCommittedCriticalPhases(manifest, requestId) {
|
|
|
461
317
|
}
|
|
462
318
|
});
|
|
463
319
|
}
|
|
464
|
-
export function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
|
|
465
|
-
const operationName = recoveryOperationKey(request
|
|
320
|
+
export async function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
|
|
321
|
+
const operationName = recoveryOperationKey(request);
|
|
466
322
|
const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
|
|
467
323
|
if (!operation)
|
|
468
324
|
return null;
|
|
@@ -476,32 +332,9 @@ export function recoveryResponse(manifest, manifestPath, request, evidence, payl
|
|
|
476
332
|
const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
|
|
477
333
|
if (evidence.exitCode === 0 && finalization?.status === 'deferred')
|
|
478
334
|
return null;
|
|
479
|
-
if (evidence.exitCode === 0 && request.family === 'task-workflow' && request.authority?.phase === 'task-event.completed') {
|
|
480
|
-
try {
|
|
481
|
-
const registration = readCodexControllerRegistration(manifestPath);
|
|
482
|
-
const recovered = recoverLifecycleRecoveryOperation(request.authority, {
|
|
483
|
-
repoRoot: manifest.repoRoot,
|
|
484
|
-
capabilityStore: createCodexCapabilityStore(),
|
|
485
|
-
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot),
|
|
486
|
-
controllerBinding: {
|
|
487
|
-
instanceDigest: registration.controllerInstanceDigest,
|
|
488
|
-
controlGeneration: registration.controlGeneration
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
if (recovered.status !== 'committed')
|
|
492
|
-
return null;
|
|
493
|
-
}
|
|
494
|
-
catch (error) {
|
|
495
|
-
appendBrokerAudit(manifest, 'lifecycle-authority-recovery-failed', {
|
|
496
|
-
...requestAuditFields(manifest, request),
|
|
497
|
-
errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_RECOVERY_FAILED'
|
|
498
|
-
});
|
|
499
|
-
return null;
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
335
|
const recovery = finalization
|
|
503
336
|
? { domain: { consistent: finalization.status === 'matched' } }
|
|
504
|
-
: readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
337
|
+
: await readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
505
338
|
const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName);
|
|
506
339
|
const decision = classifySandboxControlRecovery({
|
|
507
340
|
operation,
|
|
@@ -523,13 +356,13 @@ export function recoveryResponse(manifest, manifestPath, request, evidence, payl
|
|
|
523
356
|
return null;
|
|
524
357
|
return finalization.response ?? null;
|
|
525
358
|
}
|
|
526
|
-
return genericRecoveryResponse(request, evidence.exitCode, payload
|
|
359
|
+
return genericRecoveryResponse(request, evidence.exitCode, payload);
|
|
527
360
|
}
|
|
528
|
-
function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
|
|
361
|
+
async function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
|
|
529
362
|
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
530
363
|
if (!terminalResult)
|
|
531
364
|
return { valid: false, payloadReferenced: false };
|
|
532
|
-
const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
|
|
365
|
+
const expected = await recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
|
|
533
366
|
return {
|
|
534
367
|
valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
|
|
535
368
|
payloadReferenced: false
|
|
@@ -558,7 +391,7 @@ function terminalMatchesEvidence(manifest, manifestPath, request, response, evid
|
|
|
558
391
|
if (response.outputState === 'unavailable') {
|
|
559
392
|
const causes = ['recovery', 'publish'];
|
|
560
393
|
const valid = causes.some((cause) => JSON.stringify(response)
|
|
561
|
-
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause
|
|
394
|
+
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
|
|
562
395
|
return { valid, payloadReferenced: false };
|
|
563
396
|
}
|
|
564
397
|
return {
|
|
@@ -573,7 +406,7 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
|
|
|
573
406
|
const terminalResult = writeSandboxControlTerminalResult(manifest, {
|
|
574
407
|
id: request.id,
|
|
575
408
|
family: request.family,
|
|
576
|
-
operation: operationKey(request
|
|
409
|
+
operation: operationKey(request)
|
|
577
410
|
}, normalized.stdout);
|
|
578
411
|
if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
|
|
579
412
|
criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
@@ -610,7 +443,7 @@ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
|
|
|
610
443
|
catch {
|
|
611
444
|
payload = null;
|
|
612
445
|
}
|
|
613
|
-
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish'
|
|
446
|
+
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
614
447
|
}
|
|
615
448
|
}
|
|
616
449
|
if (!brokerOwns())
|
|
@@ -697,7 +530,7 @@ function assertCurrentSandboxControlIdentity(manifest, manifestPath) {
|
|
|
697
530
|
throw new Error(`SANDBOX_CONTROL_IDENTITY_${result.state.replaceAll('-', '_').toUpperCase()}`);
|
|
698
531
|
}
|
|
699
532
|
}
|
|
700
|
-
function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
|
|
533
|
+
async function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
|
|
701
534
|
for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
|
|
702
535
|
if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name))
|
|
703
536
|
continue;
|
|
@@ -819,7 +652,7 @@ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
|
|
|
819
652
|
if (terminal && request && resultEvidence) {
|
|
820
653
|
if (!terminalResult && request.family !== 'task-create')
|
|
821
654
|
continue;
|
|
822
|
-
const reconciliation = terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
|
|
655
|
+
const reconciliation = await terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
|
|
823
656
|
if (!reconciliation.valid)
|
|
824
657
|
continue;
|
|
825
658
|
terminalReconciled = true;
|
|
@@ -852,7 +685,7 @@ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
|
|
|
852
685
|
return false;
|
|
853
686
|
if (!resultEvidence || !request || payloadInvalid)
|
|
854
687
|
continue;
|
|
855
|
-
const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
|
|
688
|
+
const recovered = await recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
|
|
856
689
|
if (!recovered)
|
|
857
690
|
continue;
|
|
858
691
|
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
@@ -1008,7 +841,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
1008
841
|
pid: broker.pid,
|
|
1009
842
|
brokerId: broker.brokerId
|
|
1010
843
|
});
|
|
1011
|
-
if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns))
|
|
844
|
+
if (!await recoverProcessing(manifest, manifestPath, broker, brokerOwns))
|
|
1012
845
|
return;
|
|
1013
846
|
try {
|
|
1014
847
|
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
@@ -1150,10 +983,6 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
1150
983
|
break;
|
|
1151
984
|
let terminalCommitted = false;
|
|
1152
985
|
if (settledExecution.result && settledExecution.resultEvidenceWritten) {
|
|
1153
|
-
if (!consumeLifecycleAuthorityForResult(manifest, settledExecution)) {
|
|
1154
|
-
active = settledExecution;
|
|
1155
|
-
continue;
|
|
1156
|
-
}
|
|
1157
986
|
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
|
|
1158
987
|
if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
|
|
1159
988
|
try {
|
|
@@ -1247,14 +1076,9 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
1247
1076
|
executorCwd: manifest.repoRoot,
|
|
1248
1077
|
executorEntry: options.internalCliPath ?? process.argv[1] ?? null
|
|
1249
1078
|
});
|
|
1250
|
-
const lifecycleAuthority = lifecycleAuthorityForRequest(manifest, manifestPath, request);
|
|
1251
|
-
if (lifecycleAuthority?.status === 'rejected') {
|
|
1252
|
-
throw Object.assign(new Error(lifecycleAuthority.error?.message ?? 'lifecycle authority was rejected'), { code: lifecycleAuthority.error?.code ?? 'LIFECYCLE_AUTHORITY_REJECTED' });
|
|
1253
|
-
}
|
|
1254
1079
|
prepared = await prepareExecution({
|
|
1255
1080
|
manifest, manifestPath, request, requestPath: claimed,
|
|
1256
|
-
internalCliPath: options.internalCliPath ?? process.argv[1]
|
|
1257
|
-
...(lifecycleAuthority ? { lifecycleAuthority } : {})
|
|
1081
|
+
internalCliPath: options.internalCliPath ?? process.argv[1]
|
|
1258
1082
|
});
|
|
1259
1083
|
const preparedExecution = prepared;
|
|
1260
1084
|
if (!preparedExecution)
|
|
@@ -1270,7 +1094,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
1270
1094
|
executionPath: executionPath(manifest, request.id)
|
|
1271
1095
|
});
|
|
1272
1096
|
const execution = {
|
|
1273
|
-
request, prepared: preparedExecution,
|
|
1097
|
+
request, prepared: preparedExecution, result: null,
|
|
1274
1098
|
resultEvidenceWritten: false, failure: null, settled: false
|
|
1275
1099
|
};
|
|
1276
1100
|
active = execution;
|
|
@@ -1431,8 +1255,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
1431
1255
|
}
|
|
1432
1256
|
}
|
|
1433
1257
|
if (owned && active.result && active.resultEvidenceWritten) {
|
|
1434
|
-
if (
|
|
1435
|
-
&& publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
|
|
1258
|
+
if (publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
|
|
1436
1259
|
if (brokerOwns()) {
|
|
1437
1260
|
removeAcceptedResponse(manifest, active.request.id);
|
|
1438
1261
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { isCompletionEvidence } from '../../task/orchestration.js';
|
|
2
2
|
import { parseControlOutput } from '../../task/control-recovery.js';
|
|
3
|
-
import { isRecoveryWarning } from '../../task/recovery-warning.js';
|
|
4
3
|
import { execFileSync } from 'node:child_process';
|
|
5
4
|
import fs from 'node:fs';
|
|
6
5
|
import path from 'node:path';
|
|
@@ -84,7 +83,6 @@ export function createSandboxControlTerminalResult(manifest, request, output) {
|
|
|
84
83
|
? nested.run : null;
|
|
85
84
|
const rawCompletion = nested.completionEvidence ?? run?.completionEvidence;
|
|
86
85
|
const operation = request.operation ?? (typeof nested.intent === 'string' ? nested.intent : null) ?? '';
|
|
87
|
-
const warning = isRecoveryWarning(nested.warning) ? nested.warning : null;
|
|
88
86
|
return {
|
|
89
87
|
version: 1,
|
|
90
88
|
requestId: request.id,
|
|
@@ -94,8 +92,7 @@ export function createSandboxControlTerminalResult(manifest, request, output) {
|
|
|
94
92
|
targetState: typeof nested.targetState === 'string' ? nested.targetState : null,
|
|
95
93
|
status: typeof nested.status === 'string' ? nested.status : 'completed',
|
|
96
94
|
changed: typeof nested.changed === 'boolean' ? nested.changed : null,
|
|
97
|
-
completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
|
|
98
|
-
...(warning ? { warning } : {})
|
|
95
|
+
completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
|
|
99
96
|
};
|
|
100
97
|
}
|
|
101
98
|
export function writeSandboxControlTerminalResult(manifest, request, output) {
|
|
@@ -126,9 +123,6 @@ export function readSandboxControlTerminalResult(filePath) {
|
|
|
126
123
|
if (value.completionEvidence !== null && !isCompletionEvidence(value.completionEvidence)) {
|
|
127
124
|
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
128
125
|
}
|
|
129
|
-
if (value.warning !== undefined && value.warning !== null && !isRecoveryWarning(value.warning)) {
|
|
130
|
-
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
131
|
-
}
|
|
132
126
|
if (value.changed === undefined) {
|
|
133
127
|
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
134
128
|
}
|
|
@@ -13,9 +13,10 @@ function receiptIdentity(value, generation, requestId) {
|
|
|
13
13
|
if (!binding || typeof binding !== 'object' || Array.isArray(binding))
|
|
14
14
|
return null;
|
|
15
15
|
const controlBinding = binding;
|
|
16
|
-
if (receipt.version !==
|
|
16
|
+
if (receipt.version !== 4 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
|
|
17
17
|
|| !Number.isSafeInteger(receipt.revision) || receipt.revision < 0
|
|
18
18
|
|| receipt.lifecycle !== 'done'
|
|
19
|
+
|| !['pending', 'done', 'skipped'].includes(String(receipt.summary))
|
|
19
20
|
|| controlBinding.generation !== generation
|
|
20
21
|
|| typeof controlBinding.requestId !== 'string'
|
|
21
22
|
|| !/^[a-f0-9-]{16,64}$/u.test(controlBinding.requestId)
|
|
@@ -15,8 +15,8 @@ export function parseArtifactCommand(args) {
|
|
|
15
15
|
const allowed = {
|
|
16
16
|
inspect: ['--family'],
|
|
17
17
|
init: ['--family', '--artifact', '--locale'],
|
|
18
|
-
'preflight': ['--family', '--artifact'
|
|
19
|
-
'finalize-local': ['--family', '--artifact'
|
|
18
|
+
'preflight': ['--family', '--artifact'],
|
|
19
|
+
'finalize-local': ['--family', '--artifact']
|
|
20
20
|
}[operation];
|
|
21
21
|
for (let index = 2; index < args.length; index += 1) {
|
|
22
22
|
const flag = args[index];
|
|
@@ -38,12 +38,12 @@ export function parseArtifactCommand(args) {
|
|
|
38
38
|
throw new Error("option '--locale' must be 'zh-CN' or 'en'");
|
|
39
39
|
return {
|
|
40
40
|
taskRef, operation: operation, family: fields['--family'],
|
|
41
|
-
artifact: fields['--artifact'] ?? '', locale
|
|
41
|
+
artifact: fields['--artifact'] ?? '', locale
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
/** Resolve authoritative task metadata separately from a trusted candidate directory. */
|
|
45
45
|
export function executeArtifactCommand(command, options = {}) {
|
|
46
|
-
const { taskRef, operation, family, artifact, locale
|
|
46
|
+
const { taskRef, operation, family, artifact, locale } = command;
|
|
47
47
|
const fail = (code, message) => ({ status: 'failed', changed: false, error: { code, message } });
|
|
48
48
|
if (operation === 'inspect') {
|
|
49
49
|
const result = resolveArtifactContext(taskRef, family, { repoRoot: options.repoRoot });
|
|
@@ -76,7 +76,7 @@ export function executeArtifactCommand(command, options = {}) {
|
|
|
76
76
|
return fail('ARTIFACT_PAYLOAD_INVALID', "preflight and finalize-local only support 'analysis', 'plan', and 'code'");
|
|
77
77
|
}
|
|
78
78
|
if (operation === 'preflight')
|
|
79
|
-
return { ...preflightLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot
|
|
80
|
-
return { ...finalizeLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot
|
|
79
|
+
return { ...preflightLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot }) };
|
|
80
|
+
return { ...finalizeLocalArtifact({ taskRef, family, artifact, repoRoot: resolved.repoRoot }) };
|
|
81
81
|
}
|
|
82
82
|
//# sourceMappingURL=artifact-command.js.map
|