@fitlab-ai/agent-infra 0.11.11 → 0.11.13
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 +2 -2
- package/dist/lib/agent-clients/adapter.js +7 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +5 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +78 -63
- package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +114 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +447 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +130 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +131 -0
- package/dist/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.js +2 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +48 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +124 -62
- package/dist/lib/{internal/codex-sandbox-controller.js → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js} +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +211 -213
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +468 -0
- package/dist/lib/agent-clients/adapters/codex.js +40 -1
- package/dist/lib/agent-clients/adapters/index.js +2 -10
- package/dist/lib/agent-clients/tokens.js +3 -2
- package/dist/lib/internal/agent-client.js +132 -72
- package/dist/lib/internal/cli-route-inventory.js +2 -2
- package/dist/lib/internal/sandbox-control.js +1 -81
- package/dist/lib/internal/task-finalization.js +26 -19
- package/dist/lib/internal/task-lifecycle.js +1 -1
- package/dist/lib/internal/task-operation-registry.js +1 -1
- package/dist/lib/internal/task-validate.js +1 -1
- package/dist/lib/platform/github-client.js +33 -3
- package/dist/lib/platform/issues.js +18 -6
- package/dist/lib/runtime/agent-runtime.js +6 -29
- package/dist/lib/sandbox/commands/control-status.js +1 -0
- package/dist/lib/sandbox/commands/create.js +7 -2
- package/dist/lib/sandbox/commands/ls.js +4 -5
- package/dist/lib/sandbox/commands/rm.js +1 -1
- package/dist/lib/sandbox/control/agent-operation.js +34 -0
- package/dist/lib/sandbox/control/client.js +14 -201
- package/dist/lib/sandbox/control/executor.js +12 -125
- package/dist/lib/sandbox/control/lifecycle.js +66 -4
- package/dist/lib/sandbox/control/protocol.js +15 -54
- package/dist/lib/sandbox/control/server.js +216 -281
- package/dist/lib/sandbox/control/task-view.js +0 -13
- package/dist/lib/sandbox/mounts.js +9 -1
- package/dist/lib/sandbox/recovery.js +31 -10
- package/dist/lib/sandbox/removal.js +28 -5
- package/dist/lib/sandbox/workspace-identity.js +18 -6
- package/dist/lib/sandbox/workspace-view.js +56 -19
- package/dist/lib/task/commands/ls.js +3 -3
- package/dist/lib/task/control-authority.js +56 -264
- package/dist/lib/task/control-recovery.js +2 -3
- package/dist/lib/task/create.js +2 -2
- package/dist/lib/task/delegation-receipts.js +74 -161
- package/dist/lib/task/events.js +88 -44
- package/dist/lib/task/finalization.js +35 -6
- package/dist/lib/task/lifecycle.js +274 -115
- package/dist/lib/task/orchestration.js +201 -194
- package/dist/lib/task/runtime-paths.js +10 -0
- package/dist/lib/task/short-id.js +37 -27
- package/dist/lib/task/verification-engine.js +32 -26
- package/dist/lib/task/workspace-snapshot.js +1 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +39 -0
- package/lib/agent-clients/adapters/claude-code.ts +5 -1
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +105 -80
- package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +147 -0
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +510 -0
- package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +158 -0
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +1 -1
- package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +151 -0
- package/lib/{sandbox/control → agent-clients/adapters/codex-lifecycle}/controller-registration.ts +4 -4
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +43 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +8 -5
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +190 -65
- package/lib/{internal/codex-sandbox-controller.ts → agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts} +2 -2
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -5
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +270 -224
- package/lib/agent-clients/adapters/codex-orchestration.ts +632 -0
- package/lib/agent-clients/adapters/codex.ts +44 -1
- package/lib/agent-clients/adapters/index.ts +7 -20
- package/lib/agent-clients/tokens.ts +4 -3
- package/lib/internal/agent-client.ts +142 -73
- package/lib/internal/cli-route-inventory.ts +2 -2
- package/lib/internal/sandbox-control.ts +0 -97
- package/lib/internal/task-finalization.ts +30 -18
- package/lib/internal/task-lifecycle.ts +1 -1
- package/lib/internal/task-operation-registry.ts +1 -1
- package/lib/internal/task-validate.ts +1 -1
- package/lib/platform/github-client.ts +34 -3
- package/lib/platform/issues.ts +18 -6
- package/lib/runtime/agent-runtime.ts +6 -41
- package/lib/sandbox/commands/control-status.ts +1 -0
- package/lib/sandbox/commands/create.ts +7 -1
- package/lib/sandbox/commands/ls.ts +4 -5
- package/lib/sandbox/commands/rm.ts +1 -1
- package/lib/sandbox/control/agent-operation.ts +49 -0
- package/lib/sandbox/control/client.ts +20 -254
- package/lib/sandbox/control/executor.ts +13 -136
- package/lib/sandbox/control/lifecycle.ts +67 -4
- package/lib/sandbox/control/protocol.ts +22 -66
- package/lib/sandbox/control/server.ts +21 -95
- package/lib/sandbox/control/task-view.ts +0 -19
- package/lib/sandbox/mounts.ts +10 -1
- package/lib/sandbox/recovery.ts +38 -17
- package/lib/sandbox/removal.ts +33 -7
- package/lib/sandbox/workspace-identity.ts +16 -6
- package/lib/sandbox/workspace-view.ts +50 -18
- package/lib/task/commands/ls.ts +3 -3
- package/lib/task/control-authority.ts +66 -378
- package/lib/task/control-recovery.ts +3 -4
- package/lib/task/create.ts +3 -3
- package/lib/task/delegation-receipts.ts +87 -231
- package/lib/task/events.ts +100 -42
- package/lib/task/finalization.ts +37 -7
- package/lib/task/lifecycle.ts +257 -109
- package/lib/task/orchestration.ts +260 -237
- package/lib/task/runtime-paths.ts +12 -0
- package/lib/task/short-id.ts +33 -40
- package/lib/task/verification-engine.ts +30 -25
- package/lib/task/workspace-snapshot.ts +1 -1
- package/package.json +1 -1
- package/templates/.agents/hooks/lifecycle-delegation.js +1 -25
- package/templates/.agents/rules/README.en.md +2 -1
- package/templates/.agents/rules/README.zh-CN.md +2 -1
- package/templates/.agents/rules/next-step-output.en.md +6 -19
- package/templates/.agents/rules/next-step-output.zh-CN.md +6 -19
- package/templates/.agents/rules/strategy-pattern.en.md +25 -0
- package/templates/.agents/rules/strategy-pattern.zh-CN.md +25 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +4 -0
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +0 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +0 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +5 -29
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -26
- package/templates/.agents/skills/plan-task/SKILL.en.md +7 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +0 -546
- package/dist/lib/internal/codex-lifecycle.js +0 -385
- package/dist/lib/task/codex-orchestration.js +0 -206
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +0 -776
- package/lib/internal/codex-lifecycle.ts +0 -390
- package/lib/task/codex-orchestration.ts +0 -276
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { closeCodexControllerRegistration, CodexControllerRegistrationError, openCodexControllerRegistration, readCodexControllerRegistration, resolveCodexControllerBinding } from './controller-registration.js';
|
|
2
|
+
import { computeLifecycleBuildIdentity } from './build-identity.js';
|
|
3
|
+
function validProcess(value) {
|
|
4
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
5
|
+
return false;
|
|
6
|
+
const process = value;
|
|
7
|
+
return Object.keys(process).sort().join(',') === 'pid,startTime'
|
|
8
|
+
&& Number.isSafeInteger(process.pid) && process.pid > 0
|
|
9
|
+
&& Number.isSafeInteger(process.startTime) && process.startTime >= 0;
|
|
10
|
+
}
|
|
11
|
+
function validProof(value) {
|
|
12
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
13
|
+
return false;
|
|
14
|
+
const proof = value;
|
|
15
|
+
return Object.keys(proof).sort().join(',') === 'controllerProcess,leaseId,leaseSecret,version'
|
|
16
|
+
&& proof.version === 1
|
|
17
|
+
&& typeof proof.leaseId === 'string' && /^[a-f0-9]{64}$/u.test(proof.leaseId)
|
|
18
|
+
&& typeof proof.leaseSecret === 'string' && /^[a-f0-9]{64}$/u.test(proof.leaseSecret)
|
|
19
|
+
&& validProcess(proof.controllerProcess);
|
|
20
|
+
}
|
|
21
|
+
function validate(params) {
|
|
22
|
+
const { operation, payload, manifest } = params;
|
|
23
|
+
const expectedKeys = operation === 'controller.open' ? ['controllerProcess'] : ['proof'];
|
|
24
|
+
if (!['controller.open', 'controller.close', 'controller.verify'].includes(operation)
|
|
25
|
+
|| Object.keys(payload).sort().join(',') !== expectedKeys.sort().join(',')
|
|
26
|
+
|| (operation === 'controller.open'
|
|
27
|
+
? !validProcess(payload.controllerProcess)
|
|
28
|
+
: !validProof(payload.proof))) {
|
|
29
|
+
throw new Error('SANDBOX_CONTROL_REQUEST_INVALID: controller operation payload is invalid');
|
|
30
|
+
}
|
|
31
|
+
if (manifest.mode !== 'task-bound' || !manifest.taskId) {
|
|
32
|
+
throw new Error('SANDBOX_CONTROL_BRANCH_ONLY: controller operations require a task-bound sandbox');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async function execute(params) {
|
|
36
|
+
const { operation, payload, manifest, manifestPath } = params;
|
|
37
|
+
try {
|
|
38
|
+
const result = operation === 'controller.open'
|
|
39
|
+
? openCodexControllerRegistration({
|
|
40
|
+
manifest,
|
|
41
|
+
manifestPath,
|
|
42
|
+
controllerProcess: payload.controllerProcess,
|
|
43
|
+
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
|
|
44
|
+
})
|
|
45
|
+
: operation === 'controller.close'
|
|
46
|
+
? closeCodexControllerRegistration({
|
|
47
|
+
manifest,
|
|
48
|
+
manifestPath,
|
|
49
|
+
proof: payload.proof
|
|
50
|
+
})
|
|
51
|
+
: (() => {
|
|
52
|
+
const binding = resolveCodexControllerBinding({
|
|
53
|
+
manifest,
|
|
54
|
+
manifestPath,
|
|
55
|
+
proof: payload.proof,
|
|
56
|
+
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
version: 1,
|
|
60
|
+
status: 'verified',
|
|
61
|
+
changed: false,
|
|
62
|
+
lease: null,
|
|
63
|
+
binding: {
|
|
64
|
+
taskId: manifest.taskId,
|
|
65
|
+
controlGeneration: binding.controlGeneration,
|
|
66
|
+
controllerInstanceDigest: binding.instanceDigest
|
|
67
|
+
},
|
|
68
|
+
error: null
|
|
69
|
+
};
|
|
70
|
+
})();
|
|
71
|
+
return { exitCode: 0, stdout: `${JSON.stringify(result)}\n`, stderr: '' };
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
75
|
+
const code = error instanceof CodexControllerRegistrationError
|
|
76
|
+
? error.code
|
|
77
|
+
: /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'CODEX_SANDBOX_CONTROLLER_FAILED';
|
|
78
|
+
return {
|
|
79
|
+
exitCode: 1,
|
|
80
|
+
stdout: `${JSON.stringify({
|
|
81
|
+
version: 1,
|
|
82
|
+
status: 'failed',
|
|
83
|
+
changed: false,
|
|
84
|
+
lease: null,
|
|
85
|
+
error: { code, message, retryable: false }
|
|
86
|
+
})}\n`,
|
|
87
|
+
stderr: ''
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function recover(params) {
|
|
92
|
+
if (params.stdout === null)
|
|
93
|
+
return { consistent: false };
|
|
94
|
+
let output;
|
|
95
|
+
try {
|
|
96
|
+
const parsed = JSON.parse(params.stdout);
|
|
97
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
98
|
+
return { consistent: false };
|
|
99
|
+
output = parsed;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { consistent: false };
|
|
103
|
+
}
|
|
104
|
+
if (output.error !== null || typeof output.status !== 'string')
|
|
105
|
+
return { consistent: false };
|
|
106
|
+
try {
|
|
107
|
+
const registration = readCodexControllerRegistration(params.manifestPath);
|
|
108
|
+
if (params.operation === 'controller.open') {
|
|
109
|
+
const lease = output.lease;
|
|
110
|
+
return { consistent: output.status === 'opened' && output.changed === true
|
|
111
|
+
&& lease?.controlGeneration === registration.controlGeneration
|
|
112
|
+
&& lease?.controllerInstanceDigest === registration.controllerInstanceDigest
|
|
113
|
+
&& registration.controlGeneration === params.manifest.generation };
|
|
114
|
+
}
|
|
115
|
+
if (params.operation === 'controller.verify') {
|
|
116
|
+
const binding = output.binding;
|
|
117
|
+
return { consistent: output.status === 'verified' && output.changed === false
|
|
118
|
+
&& binding?.controlGeneration === registration.controlGeneration
|
|
119
|
+
&& binding?.controllerInstanceDigest === registration.controllerInstanceDigest };
|
|
120
|
+
}
|
|
121
|
+
return { consistent: false };
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
return { consistent: params.operation === 'controller.close'
|
|
125
|
+
&& error.code === 'CODEX_SANDBOX_CONTROLLER_REGISTRATION_MISSING'
|
|
126
|
+
&& output.status === 'closed' && typeof output.changed === 'boolean' };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const codexControllerOperation = Object.freeze({ validate, execute, recover });
|
|
130
|
+
export { codexControllerOperation };
|
|
131
|
+
//# sourceMappingURL=controller-operation.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { parseLinuxProcessStat } from '
|
|
5
|
-
import { commandForEngine, runProbe } from '
|
|
4
|
+
import { parseLinuxProcessStat } from '../../../server/process-state.js';
|
|
5
|
+
import { commandForEngine, runProbe } from '../../../sandbox/shell.js';
|
|
6
6
|
const CONTROLLER_TTL_MS = 4 * 60 * 60 * 1_000;
|
|
7
7
|
const HEX_256 = /^[a-f0-9]{64}$/u;
|
|
8
8
|
const REGISTRATION_KEYS = [
|
|
@@ -1,3 +1,50 @@
|
|
|
1
|
+
function isCodexLifecycleActivationEvidence(value, parentId) {
|
|
2
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
3
|
+
return false;
|
|
4
|
+
const state = value;
|
|
5
|
+
const evidence = state.activationEvidence;
|
|
6
|
+
if (!evidence || typeof evidence !== 'object' || Array.isArray(evidence))
|
|
7
|
+
return false;
|
|
8
|
+
const activation = evidence;
|
|
9
|
+
return activation.kind === 'codex-lifecycle-v2'
|
|
10
|
+
&& typeof activation.hookDefinitionHash === 'string'
|
|
11
|
+
&& activation.hookDefinitionHash.trim() !== ''
|
|
12
|
+
&& Number.isSafeInteger(activation.startRevision)
|
|
13
|
+
&& activation.startRevision >= 1
|
|
14
|
+
&& activation.capabilitySessionId === parentId;
|
|
15
|
+
}
|
|
16
|
+
function isCodexDelegationReceiptEvidence(value) {
|
|
17
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
18
|
+
return false;
|
|
19
|
+
const receipt = value;
|
|
20
|
+
if (receipt.client !== 'codex' || typeof receipt.id !== 'string')
|
|
21
|
+
return false;
|
|
22
|
+
const status = receipt.status;
|
|
23
|
+
if (!['activated', 'stage-completed', 'sealed', 'consumed', 'aborted'].includes(status))
|
|
24
|
+
return true;
|
|
25
|
+
const adapterEvidence = receipt.adapterEvidence;
|
|
26
|
+
if (!adapterEvidence || typeof adapterEvidence !== 'object' || Array.isArray(adapterEvidence))
|
|
27
|
+
return false;
|
|
28
|
+
const codex = adapterEvidence.codex;
|
|
29
|
+
if (!codex || typeof codex !== 'object' || Array.isArray(codex))
|
|
30
|
+
return false;
|
|
31
|
+
const state = codex;
|
|
32
|
+
if (!isCodexLifecycleActivationEvidence(state, String(receipt.parentId ?? '')))
|
|
33
|
+
return false;
|
|
34
|
+
if (!['sealed', 'consumed', 'aborted'].includes(status))
|
|
35
|
+
return true;
|
|
36
|
+
const records = state.records;
|
|
37
|
+
if (!records || typeof records !== 'object' || Array.isArray(records))
|
|
38
|
+
return false;
|
|
39
|
+
return Object.values(records).some((record) => {
|
|
40
|
+
if (!record || typeof record !== 'object' || Array.isArray(record))
|
|
41
|
+
return false;
|
|
42
|
+
const evidence = record;
|
|
43
|
+
return evidence.consumer === receipt.id
|
|
44
|
+
&& typeof evidence.consumedAt === 'string'
|
|
45
|
+
&& evidence.consumedAt.trim() !== '';
|
|
46
|
+
});
|
|
47
|
+
}
|
|
1
48
|
function createCodexLifecycleState(cliVersion) {
|
|
2
49
|
if (!cliVersion.trim())
|
|
3
50
|
throw new Error('Codex CLI version is required');
|
|
@@ -185,5 +232,5 @@ function expireCodexLifecycleState(state) {
|
|
|
185
232
|
})
|
|
186
233
|
});
|
|
187
234
|
}
|
|
188
|
-
export { createCodexLifecycleState, expireCodexLifecycleState, reduceCodexLifecycleEvent };
|
|
235
|
+
export { createCodexLifecycleState, expireCodexLifecycleState, isCodexLifecycleActivationEvidence, isCodexDelegationReceiptEvidence, reduceCodexLifecycleEvent };
|
|
189
236
|
//# sourceMappingURL=evidence.js.map
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { createCodexLifecycleStore } from './store.js';
|
|
2
|
+
import { createCodexLifecycleStore, readCodexLifecycleActivationEvidence } from './store.js';
|
|
3
3
|
import { managedDelegationRole } from '../../../task/delegation-receipts.js';
|
|
4
4
|
import { normalizeAgentToken } from '../../tokens.js';
|
|
5
5
|
import { locateActivityLog, pairEntries, appendActivityEntry } from '../../../task/activity-log.js';
|
|
6
6
|
import { captureTaskWriteMetadata, writeTask } from '../../../task/write.js';
|
|
7
7
|
import { parseTypedTaskFrontmatter } from '../../../task/frontmatter.js';
|
|
8
8
|
import { resolveTaskRef } from '../../../task/resolve-ref.js';
|
|
9
|
-
import { resolveAgentRuntimeStoreRoot } from '../../../runtime/agent-runtime.js';
|
|
10
9
|
import { recoverActivatedOrchestrationDelegationUnderLock, readRun } from '../../../task/orchestration.js';
|
|
11
10
|
const ACTIONS = {
|
|
12
11
|
analysis: 'Analyze Task',
|
|
@@ -47,9 +46,10 @@ function rowForReceipt(rows, receipt) {
|
|
|
47
46
|
const pattern = actionPattern(receipt);
|
|
48
47
|
return pattern ? rows.filter((row) => pattern.test(row.step)) : [];
|
|
49
48
|
}
|
|
50
|
-
function readLifecycleStore(options, repoRoot) {
|
|
49
|
+
function readLifecycleStore(options, repoRoot, taskId) {
|
|
51
50
|
return options.lifecycleStore ?? createCodexLifecycleStore({
|
|
52
|
-
|
|
51
|
+
repoRoot,
|
|
52
|
+
taskId,
|
|
53
53
|
cliVersion: 'recovery',
|
|
54
54
|
now: options.now
|
|
55
55
|
});
|
|
@@ -66,37 +66,10 @@ function readStoredEvidence(store, childId) {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
function validateStopRecord(record, receipt) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (receipt.client !== 'codex'
|
|
74
|
-
|| !provenance
|
|
75
|
-
|| host?.kind !== 'codex-lifecycle-v2'
|
|
76
|
-
|| !start
|
|
77
|
-
|| !stop
|
|
78
|
-
|| record.state.status !== 'stop-ready'
|
|
79
|
-
|| stop.terminalStatus !== 'completed'
|
|
80
|
-
|| stop.hookStopObserved !== true
|
|
81
|
-
|| start.childThreadId !== receipt.childId
|
|
82
|
-
|| start.parentThreadId !== receipt.parentId
|
|
83
|
-
|| managedDelegationRole(start.nativeAgent) !== receipt.role
|
|
84
|
-
|| start.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
85
|
-
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
86
|
-
|| host.capabilitySessionId !== provenance.capabilitySessionId
|
|
87
|
-
|| host.capabilityTurnId !== provenance.capabilityTurnId
|
|
88
|
-
|| host.capabilityToolUseId !== provenance.capabilityToolUseId
|
|
89
|
-
|| host.spawnToolUseId !== start.spawnToolUseId
|
|
90
|
-
|| !host.spawnObservedAt
|
|
91
|
-
|| host.spawnObservedAt !== record.spawnObservedAt
|
|
92
|
-
|| host.controllerInstanceDigest !== provenance.controllerInstanceDigest
|
|
93
|
-
|| host.controlGeneration !== provenance.controlGeneration
|
|
94
|
-
|| typeof host.controllerInstanceDigest !== 'string'
|
|
95
|
-
|| typeof host.controlGeneration !== 'string'
|
|
96
|
-
|| host.startRevision < 1
|
|
97
|
-
|| record.revision <= host.startRevision
|
|
98
|
-
|| record.state.child?.childThreadId !== receipt.childId
|
|
99
|
-
|| record.state.stop?.childThreadId !== receipt.childId) {
|
|
69
|
+
if (!stopRecordMatchesReceipt(record, receipt)) {
|
|
70
|
+
return { ok: false, code: 'RECOVERY_TASK_BINDING_MISMATCH', message: 'lifecycle evidence belongs to a different task receipt' };
|
|
71
|
+
}
|
|
72
|
+
if (!stopEvidenceMatchesReceipt(record, receipt)) {
|
|
100
73
|
return { ok: false, code: 'RECOVERY_STOP_EVIDENCE_INVALID', message: 'Codex stop evidence does not match the activated receipt' };
|
|
101
74
|
}
|
|
102
75
|
if (record.consumer !== null && record.consumer !== receipt.id) {
|
|
@@ -104,6 +77,77 @@ function validateStopRecord(record, receipt) {
|
|
|
104
77
|
}
|
|
105
78
|
return { ok: true };
|
|
106
79
|
}
|
|
80
|
+
function stopRecordMatchesReceipt(record, receipt) {
|
|
81
|
+
return record.taskBinding?.runId === receipt.runId
|
|
82
|
+
&& record.taskBinding.receiptId === receipt.id;
|
|
83
|
+
}
|
|
84
|
+
function stopEvidenceMatchesReceipt(record, receipt) {
|
|
85
|
+
const start = record.state.startEvidence;
|
|
86
|
+
const stop = record.state.stopEvidence;
|
|
87
|
+
const host = readCodexLifecycleActivationEvidence(receipt);
|
|
88
|
+
if (!stopRecordHasRequiredEvidence(record, receipt, start, stop, host))
|
|
89
|
+
return false;
|
|
90
|
+
return spawnMatchesReceipt(record, receipt, host, start)
|
|
91
|
+
&& childMatchesReceipt(record, receipt, start, stop)
|
|
92
|
+
&& hostEvidenceMatches(record, receipt, host, start);
|
|
93
|
+
}
|
|
94
|
+
function stopRecordHasRequiredEvidence(record, receipt, start, stop, host) {
|
|
95
|
+
return receipt.client === 'codex'
|
|
96
|
+
&& host?.kind === 'codex-lifecycle-v2'
|
|
97
|
+
&& Boolean(start && stop && record.state.spawn && record.state.child && record.state.stop)
|
|
98
|
+
&& record.state.status === 'stop-ready'
|
|
99
|
+
&& stop?.terminalStatus === 'completed'
|
|
100
|
+
&& stop.hookStopObserved === true;
|
|
101
|
+
}
|
|
102
|
+
function spawnMatchesReceipt(record, receipt, host, start) {
|
|
103
|
+
const spawn = record.state.spawn;
|
|
104
|
+
return Boolean(spawn)
|
|
105
|
+
&& spawn.sessionId === start.parentThreadId
|
|
106
|
+
&& spawn.turnId === host.capabilityTurnId
|
|
107
|
+
&& spawn.toolUseId === host.spawnToolUseId
|
|
108
|
+
&& spawn.nativeAgent === start.nativeAgent
|
|
109
|
+
&& spawn.hookDefinitionHash === host.hookDefinitionHash
|
|
110
|
+
&& spawn.requestedModel === receipt.requestedModel
|
|
111
|
+
&& spawn.requestedReasoningEffort === receipt.requestedReasoningEffort
|
|
112
|
+
&& spawn.taskBinding?.runId === receipt.runId
|
|
113
|
+
&& spawn.taskBinding.receiptId === receipt.id;
|
|
114
|
+
}
|
|
115
|
+
function childMatchesReceipt(record, receipt, start, stop) {
|
|
116
|
+
const child = record.state.child;
|
|
117
|
+
const stopState = record.state.stop;
|
|
118
|
+
return Boolean(child && stopState)
|
|
119
|
+
&& child.sessionId === start.parentThreadId
|
|
120
|
+
&& stopState.turnId === child.turnId
|
|
121
|
+
&& (stopState.source === 'parent-rollout'
|
|
122
|
+
? stop.turnId === record.state.terminal?.turnId
|
|
123
|
+
: child.turnId === stop.turnId)
|
|
124
|
+
&& child.childThreadId === receipt.childId
|
|
125
|
+
&& child.nativeAgent === start.nativeAgent
|
|
126
|
+
&& stopState.childThreadId === receipt.childId
|
|
127
|
+
&& stopState.nativeAgent === start.nativeAgent
|
|
128
|
+
&& start.childThreadId === receipt.childId
|
|
129
|
+
&& start.parentThreadId === receipt.parentId
|
|
130
|
+
&& managedDelegationRole(start.nativeAgent) === receipt.role;
|
|
131
|
+
}
|
|
132
|
+
function hostEvidenceMatches(record, receipt, host, start) {
|
|
133
|
+
return start.hookDefinitionHash === host.hookDefinitionHash
|
|
134
|
+
&& host.capabilitySessionId === start.parentThreadId
|
|
135
|
+
&& host.capabilityTurnId === record.state.spawn?.turnId
|
|
136
|
+
&& host.spawnToolUseId === start.spawnToolUseId
|
|
137
|
+
&& Boolean(host.spawnObservedAt)
|
|
138
|
+
&& host.spawnObservedAt === record.spawnObservedAt
|
|
139
|
+
&& typeof host.controllerInstanceDigest === 'string'
|
|
140
|
+
&& typeof host.controlGeneration === 'string'
|
|
141
|
+
&& hostRevisionMatches(record, receipt, host);
|
|
142
|
+
}
|
|
143
|
+
function hostRevisionMatches(record, receipt, host) {
|
|
144
|
+
return typeof host.controllerInstanceDigest === 'string'
|
|
145
|
+
&& typeof host.controlGeneration === 'string'
|
|
146
|
+
&& host.startRevision >= 1
|
|
147
|
+
&& record.revision > host.startRevision
|
|
148
|
+
&& record.state.child?.childThreadId === receipt.childId
|
|
149
|
+
&& record.state.stop?.childThreadId === receipt.childId;
|
|
150
|
+
}
|
|
107
151
|
function abortedCandidates(run, rows) {
|
|
108
152
|
const candidates = run.receipts.flatMap((receipt) => {
|
|
109
153
|
if (receipt.status !== 'aborted' || receipt.activatedAt === null || receipt.client !== 'codex')
|
|
@@ -165,15 +209,20 @@ function recoverStartedLifecycleUnderLock(request, options = {}) {
|
|
|
165
209
|
return result(request, 'no-op', { taskId });
|
|
166
210
|
const rows = openRows(section);
|
|
167
211
|
const pending = run.pendingDelegation;
|
|
168
|
-
if (!pending)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return
|
|
212
|
+
if (!pending)
|
|
213
|
+
return recoverAbortedLifecycle(request, taskId, section, resolved.repoRoot, run, rows, options);
|
|
214
|
+
return recoverActiveLifecycle(request, taskId, section, resolved.repoRoot, run, rows, pending, options);
|
|
215
|
+
}
|
|
216
|
+
function recoverAbortedLifecycle(request, taskId, section, repoRoot, run, rows, options) {
|
|
217
|
+
const candidates = abortedCandidates(run, rows);
|
|
218
|
+
if (candidates.length === 0)
|
|
219
|
+
return result(request, 'no-op', { taskId });
|
|
220
|
+
if (candidates.length > 1) {
|
|
221
|
+
return failure(request, 'conflict', 'RECOVERY_CANDIDATE_AMBIGUOUS', 'more than one aborted delegation has an open lifecycle row', { taskId });
|
|
176
222
|
}
|
|
223
|
+
return appendRecoveryLog(request, taskId, section, candidates[0], repoRoot, options.writeTask ?? writeTask);
|
|
224
|
+
}
|
|
225
|
+
function recoverActiveLifecycle(request, taskId, section, repoRoot, run, rows, pending, options) {
|
|
177
226
|
if (pending.status !== 'activated')
|
|
178
227
|
return result(request, 'no-op', { taskId });
|
|
179
228
|
if (pending.client !== 'codex' || run.status !== 'running' || run.pause !== null) {
|
|
@@ -181,51 +230,64 @@ function recoverStartedLifecycleUnderLock(request, options = {}) {
|
|
|
181
230
|
taskId, receiptId: pending.id, childId: pending.childId
|
|
182
231
|
});
|
|
183
232
|
}
|
|
233
|
+
const rowResult = matchingActiveRecoveryRow(request, taskId, rows, pending);
|
|
234
|
+
if ('result' in rowResult)
|
|
235
|
+
return rowResult.result;
|
|
236
|
+
const evidenceResult = consumeRecoveryEvidence(request, taskId, repoRoot, pending, options);
|
|
237
|
+
if ('result' in evidenceResult)
|
|
238
|
+
return evidenceResult.result;
|
|
239
|
+
return finishActiveRecovery(request, taskId, section, repoRoot, pending, rowResult.row, options);
|
|
240
|
+
}
|
|
241
|
+
function matchingActiveRecoveryRow(request, taskId, rows, pending) {
|
|
184
242
|
const matchingRows = rowForReceipt(rows, pending);
|
|
185
243
|
if (matchingRows.length !== 1 || normalizeAgentToken(matchingRows[0].agent) !== 'codex' || matchingRows[0].note !== 'started') {
|
|
186
|
-
return failure(request, 'conflict', 'RECOVERY_LOG_CONFLICT', 'activated delegation does not have one matching open lifecycle row', {
|
|
187
|
-
|
|
188
|
-
|
|
244
|
+
return { result: failure(request, 'conflict', 'RECOVERY_LOG_CONFLICT', 'activated delegation does not have one matching open lifecycle row', {
|
|
245
|
+
taskId, receiptId: pending.id, childId: pending.childId
|
|
246
|
+
}) };
|
|
189
247
|
}
|
|
190
248
|
if (!pending.childId) {
|
|
191
|
-
return failure(request, 'conflict', 'RECOVERY_DELEGATION_INVALID', 'activated delegation has no child identity', { taskId, receiptId: pending.id });
|
|
249
|
+
return { result: failure(request, 'conflict', 'RECOVERY_DELEGATION_INVALID', 'activated delegation has no child identity', { taskId, receiptId: pending.id }) };
|
|
192
250
|
}
|
|
193
|
-
|
|
251
|
+
return { row: matchingRows[0] };
|
|
252
|
+
}
|
|
253
|
+
function consumeRecoveryEvidence(request, taskId, repoRoot, pending, options) {
|
|
254
|
+
const store = readLifecycleStore(options, repoRoot, taskId);
|
|
194
255
|
const stored = readStoredEvidence(store, pending.childId);
|
|
195
256
|
if ('error' in stored)
|
|
196
|
-
return failure(request, 'owner-unknown', 'RECOVERY_STORE_UNKNOWN', stored.error.message, { taskId, receiptId: pending.id, childId: pending.childId });
|
|
257
|
+
return { result: failure(request, 'owner-unknown', 'RECOVERY_STORE_UNKNOWN', stored.error.message, { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
197
258
|
if ('missing' in stored)
|
|
198
|
-
return failure(request, 'owner-unknown', 'RECOVERY_STOP_EVIDENCE_MISSING', 'matching Codex lifecycle stop evidence is missing', { taskId, receiptId: pending.id, childId: pending.childId });
|
|
259
|
+
return { result: failure(request, 'owner-unknown', 'RECOVERY_STOP_EVIDENCE_MISSING', 'matching Codex lifecycle stop evidence is missing', { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
199
260
|
const evidence = validateStopRecord(stored, pending);
|
|
200
261
|
if (!evidence.ok)
|
|
201
|
-
return failure(request, evidence.code === 'RECOVERY_CONSUMER_CONFLICT' ? 'conflict' : 'owner-unknown', evidence.code, evidence.message, { taskId, receiptId: pending.id, childId: pending.childId });
|
|
262
|
+
return { result: failure(request, evidence.code === 'RECOVERY_CONSUMER_CONFLICT' ? 'conflict' : 'owner-unknown', evidence.code, evidence.message, { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
202
263
|
let consumed = stored;
|
|
203
264
|
if (stored.consumer === null) {
|
|
204
265
|
try {
|
|
205
|
-
consumed = store.
|
|
266
|
+
consumed = store.consumeWithinTaskLock(pending.childId, pending.id, stored.state.startEvidence?.hookDefinitionHash, {
|
|
267
|
+
taskId: pending.taskId, runId: pending.runId, receiptId: pending.id
|
|
268
|
+
});
|
|
206
269
|
}
|
|
207
270
|
catch (error) {
|
|
208
|
-
return failure(request, 'owner-unknown', 'RECOVERY_EVIDENCE_CONSUME_FAILED', error instanceof Error ? error.message : String(error), { taskId, receiptId: pending.id, childId: pending.childId });
|
|
271
|
+
return { result: failure(request, 'owner-unknown', 'RECOVERY_EVIDENCE_CONSUME_FAILED', error instanceof Error ? error.message : String(error), { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
209
272
|
}
|
|
210
273
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
274
|
+
return consumed.consumedAt
|
|
275
|
+
? { consumed }
|
|
276
|
+
: { result: failure(request, 'owner-unknown', 'RECOVERY_EVIDENCE_INVALID', 'consumed stop evidence has no timestamp', { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
277
|
+
}
|
|
278
|
+
function finishActiveRecovery(request, taskId, section, repoRoot, pending, row, options) {
|
|
214
279
|
const recovered = recoverActivatedOrchestrationDelegationUnderLock(taskId, {
|
|
215
280
|
receiptId: pending.id,
|
|
216
281
|
stage: pending.stage,
|
|
217
282
|
round: pending.round,
|
|
218
283
|
artifact: pending.artifact,
|
|
219
284
|
startedAgent: request.agent,
|
|
220
|
-
childId: pending.childId
|
|
221
|
-
|
|
222
|
-
consumer: pending.id,
|
|
223
|
-
consumedAt: consumed.consumedAt
|
|
224
|
-
}, { ...options.orchestration, repoRoot: resolved.repoRoot, now: options.now });
|
|
285
|
+
childId: pending.childId
|
|
286
|
+
}, { ...options.orchestration, repoRoot, now: options.now });
|
|
225
287
|
if (recovered.status === 'failed' || !recovered.run) {
|
|
226
288
|
return failure(request, 'owner-unknown', recovered.error?.code ?? 'RECOVERY_ORCHESTRATION_FAILED', recovered.error?.message ?? 'orchestration recovery failed', { taskId, receiptId: pending.id, childId: pending.childId });
|
|
227
289
|
}
|
|
228
|
-
return appendRecoveryLog(request, taskId, section, { receipt: recovered.run.receipts.at(-1), row
|
|
290
|
+
return appendRecoveryLog(request, taskId, section, { receipt: recovered.run.receipts.at(-1), row }, repoRoot, options.writeTask ?? writeTask);
|
|
229
291
|
}
|
|
230
292
|
function recoverStartedLifecycleFromAdapter(request, options = {}) {
|
|
231
293
|
return recoverStartedLifecycleUnderLock(request, options);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { runCodexSandboxController, verifyCodexSandboxControllerContextWithWarnings } from '
|
|
2
|
-
import { ensureInternalHandlerRoute, internalHandlerRoute } from '
|
|
1
|
+
import { runCodexSandboxController, verifyCodexSandboxControllerContextWithWarnings } from './sandbox-controller.js';
|
|
2
|
+
import { ensureInternalHandlerRoute, internalHandlerRoute } from '../../../internal/cli-route-inventory.js';
|
|
3
3
|
const USAGE = 'Usage: agent-infra-internal codex-sandbox-controller <run|verify-context> [options]\n';
|
|
4
4
|
function fail(code, message, exitCode = 1) {
|
|
5
5
|
process.stdout.write(`${JSON.stringify({
|
|
@@ -103,4 +103,4 @@ async function codexSandboxController(args = []) {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
export { codexSandboxController };
|
|
106
|
-
//# sourceMappingURL=
|
|
106
|
+
//# sourceMappingURL=sandbox-controller-cli.js.map
|
|
@@ -5,7 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import semver from 'semver';
|
|
7
7
|
import * as toml from 'smol-toml';
|
|
8
|
-
import { requestCodexControllerClose, requestCodexControllerOpen, requestCodexControllerVerify } from '
|
|
8
|
+
import { requestCodexControllerClose, requestCodexControllerOpen, requestCodexControllerVerify } from './controller-client.js';
|
|
9
9
|
import { getProcessStartTime } from '../../../server/process-state.js';
|
|
10
10
|
import { LIFECYCLE_PROTOCOL_VERSION } from './build-identity.js';
|
|
11
11
|
import { contextFromControllerLease, controllerProofFromContext, verifyCodexSandboxControllerContextWithWarnings as verifyContextFileWithWarnings, writeCodexSandboxControllerContext } from './controller-context.js';
|
|
@@ -62,8 +62,7 @@ function verifyControllerBinding(context, control, requestControllerVerify) {
|
|
|
62
62
|
...control,
|
|
63
63
|
timeoutMs: 30_000
|
|
64
64
|
});
|
|
65
|
-
if (verified.binding.
|
|
66
|
-
|| verified.binding.controlGeneration !== context.controlGeneration
|
|
65
|
+
if (verified.binding.controlGeneration !== context.controlGeneration
|
|
67
66
|
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
68
67
|
throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_BINDING_INVALID');
|
|
69
68
|
}
|
|
@@ -82,8 +81,7 @@ function verifyCodexSandboxControllerContextWithWarnings(contextPath, options =
|
|
|
82
81
|
...control,
|
|
83
82
|
timeoutMs: 30_000
|
|
84
83
|
});
|
|
85
|
-
if (verified.binding.
|
|
86
|
-
|| verified.binding.controlGeneration !== context.controlGeneration
|
|
84
|
+
if (verified.binding.controlGeneration !== context.controlGeneration
|
|
87
85
|
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
88
86
|
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
89
87
|
}
|