@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
|
|
3
|
-
import { createCodexLifecycleStore } from './store.ts';
|
|
4
|
-
import type { StoredCodexLifecycle } from './store.ts';
|
|
3
|
+
import { createCodexLifecycleStore, readCodexLifecycleActivationEvidence } from './store.ts';
|
|
4
|
+
import type { CodexLifecycleActivationEvidence, StoredCodexLifecycle } from './store.ts';
|
|
5
5
|
import { managedDelegationRole } from '../../../task/delegation-receipts.ts';
|
|
6
6
|
import type { DelegationReceipt } from '../../../task/delegation-receipts.ts';
|
|
7
7
|
import { normalizeAgentToken } from '../../tokens.ts';
|
|
@@ -10,7 +10,6 @@ import type { ActivityLogSection, StepRow } from '../../../task/activity-log.ts'
|
|
|
10
10
|
import { captureTaskWriteMetadata, writeTask } from '../../../task/write.ts';
|
|
11
11
|
import { parseTypedTaskFrontmatter } from '../../../task/frontmatter.ts';
|
|
12
12
|
import { resolveTaskRef } from '../../../task/resolve-ref.ts';
|
|
13
|
-
import { resolveAgentRuntimeStoreRoot } from '../../../runtime/agent-runtime.ts';
|
|
14
13
|
import {
|
|
15
14
|
recoverActivatedOrchestrationDelegationUnderLock,
|
|
16
15
|
readRun
|
|
@@ -93,10 +92,12 @@ function rowForReceipt(rows: readonly StepRow[], receipt: DelegationReceipt): St
|
|
|
93
92
|
|
|
94
93
|
function readLifecycleStore(
|
|
95
94
|
options: LifecycleRecoveryOptions,
|
|
96
|
-
repoRoot: string
|
|
95
|
+
repoRoot: string,
|
|
96
|
+
taskId: string
|
|
97
97
|
): ReturnType<typeof createCodexLifecycleStore> {
|
|
98
98
|
return options.lifecycleStore ?? createCodexLifecycleStore({
|
|
99
|
-
|
|
99
|
+
repoRoot,
|
|
100
|
+
taskId,
|
|
100
101
|
cliVersion: 'recovery',
|
|
101
102
|
now: options.now
|
|
102
103
|
});
|
|
@@ -119,39 +120,10 @@ function validateStopRecord(
|
|
|
119
120
|
record: StoredCodexLifecycle,
|
|
120
121
|
receipt: DelegationReceipt
|
|
121
122
|
): { ok: true } | { ok: false; code: string; message: string } {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
receipt.client !== 'codex'
|
|
128
|
-
|| !provenance
|
|
129
|
-
|| host?.kind !== 'codex-lifecycle-v2'
|
|
130
|
-
|| !start
|
|
131
|
-
|| !stop
|
|
132
|
-
|| record.state.status !== 'stop-ready'
|
|
133
|
-
|| stop.terminalStatus !== 'completed'
|
|
134
|
-
|| stop.hookStopObserved !== true
|
|
135
|
-
|| start.childThreadId !== receipt.childId
|
|
136
|
-
|| start.parentThreadId !== receipt.parentId
|
|
137
|
-
|| managedDelegationRole(start.nativeAgent) !== receipt.role
|
|
138
|
-
|| start.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
139
|
-
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
140
|
-
|| host.capabilitySessionId !== provenance.capabilitySessionId
|
|
141
|
-
|| host.capabilityTurnId !== provenance.capabilityTurnId
|
|
142
|
-
|| host.capabilityToolUseId !== provenance.capabilityToolUseId
|
|
143
|
-
|| host.spawnToolUseId !== start.spawnToolUseId
|
|
144
|
-
|| !host.spawnObservedAt
|
|
145
|
-
|| host.spawnObservedAt !== record.spawnObservedAt
|
|
146
|
-
|| host.controllerInstanceDigest !== provenance.controllerInstanceDigest
|
|
147
|
-
|| host.controlGeneration !== provenance.controlGeneration
|
|
148
|
-
|| typeof host.controllerInstanceDigest !== 'string'
|
|
149
|
-
|| typeof host.controlGeneration !== 'string'
|
|
150
|
-
|| host.startRevision < 1
|
|
151
|
-
|| record.revision <= host.startRevision
|
|
152
|
-
|| record.state.child?.childThreadId !== receipt.childId
|
|
153
|
-
|| record.state.stop?.childThreadId !== receipt.childId
|
|
154
|
-
) {
|
|
123
|
+
if (!stopRecordMatchesReceipt(record, receipt)) {
|
|
124
|
+
return { ok: false, code: 'RECOVERY_TASK_BINDING_MISMATCH', message: 'lifecycle evidence belongs to a different task receipt' };
|
|
125
|
+
}
|
|
126
|
+
if (!stopEvidenceMatchesReceipt(record, receipt)) {
|
|
155
127
|
return { ok: false, code: 'RECOVERY_STOP_EVIDENCE_INVALID', message: 'Codex stop evidence does not match the activated receipt' };
|
|
156
128
|
}
|
|
157
129
|
if (record.consumer !== null && record.consumer !== receipt.id) {
|
|
@@ -160,6 +132,104 @@ function validateStopRecord(
|
|
|
160
132
|
return { ok: true };
|
|
161
133
|
}
|
|
162
134
|
|
|
135
|
+
function stopRecordMatchesReceipt(record: StoredCodexLifecycle, receipt: DelegationReceipt): boolean {
|
|
136
|
+
return record.taskBinding?.runId === receipt.runId
|
|
137
|
+
&& record.taskBinding.receiptId === receipt.id;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function stopEvidenceMatchesReceipt(record: StoredCodexLifecycle, receipt: DelegationReceipt): boolean {
|
|
141
|
+
const start = record.state.startEvidence;
|
|
142
|
+
const stop = record.state.stopEvidence;
|
|
143
|
+
const host = readCodexLifecycleActivationEvidence(receipt);
|
|
144
|
+
if (!stopRecordHasRequiredEvidence(record, receipt, start, stop, host)) return false;
|
|
145
|
+
return spawnMatchesReceipt(record, receipt, host!, start!)
|
|
146
|
+
&& childMatchesReceipt(record, receipt, start!, stop!)
|
|
147
|
+
&& hostEvidenceMatches(record, receipt, host!, start!);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function stopRecordHasRequiredEvidence(
|
|
151
|
+
record: StoredCodexLifecycle,
|
|
152
|
+
receipt: DelegationReceipt,
|
|
153
|
+
start: StoredCodexLifecycle['state']['startEvidence'],
|
|
154
|
+
stop: StoredCodexLifecycle['state']['stopEvidence'],
|
|
155
|
+
host: CodexLifecycleActivationEvidence | null
|
|
156
|
+
): boolean {
|
|
157
|
+
return receipt.client === 'codex'
|
|
158
|
+
&& host?.kind === 'codex-lifecycle-v2'
|
|
159
|
+
&& Boolean(start && stop && record.state.spawn && record.state.child && record.state.stop)
|
|
160
|
+
&& record.state.status === 'stop-ready'
|
|
161
|
+
&& stop?.terminalStatus === 'completed'
|
|
162
|
+
&& stop.hookStopObserved === true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function spawnMatchesReceipt(
|
|
166
|
+
record: StoredCodexLifecycle,
|
|
167
|
+
receipt: DelegationReceipt,
|
|
168
|
+
host: CodexLifecycleActivationEvidence,
|
|
169
|
+
start: NonNullable<StoredCodexLifecycle['state']['startEvidence']>
|
|
170
|
+
): boolean {
|
|
171
|
+
const spawn = record.state.spawn;
|
|
172
|
+
return Boolean(spawn)
|
|
173
|
+
&& spawn!.sessionId === start.parentThreadId
|
|
174
|
+
&& spawn!.turnId === host.capabilityTurnId
|
|
175
|
+
&& spawn!.toolUseId === host.spawnToolUseId
|
|
176
|
+
&& spawn!.nativeAgent === start.nativeAgent
|
|
177
|
+
&& spawn!.hookDefinitionHash === host.hookDefinitionHash
|
|
178
|
+
&& spawn!.requestedModel === receipt.requestedModel
|
|
179
|
+
&& spawn!.requestedReasoningEffort === receipt.requestedReasoningEffort
|
|
180
|
+
&& spawn!.taskBinding?.runId === receipt.runId
|
|
181
|
+
&& spawn!.taskBinding.receiptId === receipt.id;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function childMatchesReceipt(
|
|
185
|
+
record: StoredCodexLifecycle,
|
|
186
|
+
receipt: DelegationReceipt,
|
|
187
|
+
start: NonNullable<StoredCodexLifecycle['state']['startEvidence']>,
|
|
188
|
+
stop: NonNullable<StoredCodexLifecycle['state']['stopEvidence']>
|
|
189
|
+
): boolean {
|
|
190
|
+
const child = record.state.child;
|
|
191
|
+
const stopState = record.state.stop;
|
|
192
|
+
return Boolean(child && stopState)
|
|
193
|
+
&& child!.sessionId === start.parentThreadId
|
|
194
|
+
&& stopState!.turnId === child!.turnId
|
|
195
|
+
&& (stopState!.source === 'parent-rollout'
|
|
196
|
+
? stop.turnId === record.state.terminal?.turnId
|
|
197
|
+
: child!.turnId === stop.turnId)
|
|
198
|
+
&& child!.childThreadId === receipt.childId
|
|
199
|
+
&& child!.nativeAgent === start.nativeAgent
|
|
200
|
+
&& stopState!.childThreadId === receipt.childId
|
|
201
|
+
&& stopState!.nativeAgent === start.nativeAgent
|
|
202
|
+
&& start.childThreadId === receipt.childId
|
|
203
|
+
&& start.parentThreadId === receipt.parentId
|
|
204
|
+
&& managedDelegationRole(start.nativeAgent) === receipt.role;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function hostEvidenceMatches(
|
|
208
|
+
record: StoredCodexLifecycle,
|
|
209
|
+
receipt: DelegationReceipt,
|
|
210
|
+
host: CodexLifecycleActivationEvidence,
|
|
211
|
+
start: NonNullable<StoredCodexLifecycle['state']['startEvidence']>
|
|
212
|
+
): boolean {
|
|
213
|
+
return start.hookDefinitionHash === host.hookDefinitionHash
|
|
214
|
+
&& host.capabilitySessionId === start.parentThreadId
|
|
215
|
+
&& host.capabilityTurnId === record.state.spawn?.turnId
|
|
216
|
+
&& host.spawnToolUseId === start.spawnToolUseId
|
|
217
|
+
&& Boolean(host.spawnObservedAt)
|
|
218
|
+
&& host.spawnObservedAt === record.spawnObservedAt
|
|
219
|
+
&& typeof host.controllerInstanceDigest === 'string'
|
|
220
|
+
&& typeof host.controlGeneration === 'string'
|
|
221
|
+
&& hostRevisionMatches(record, receipt, host);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function hostRevisionMatches(record: StoredCodexLifecycle, receipt: DelegationReceipt, host: CodexLifecycleActivationEvidence): boolean {
|
|
225
|
+
return typeof host.controllerInstanceDigest === 'string'
|
|
226
|
+
&& typeof host.controlGeneration === 'string'
|
|
227
|
+
&& host.startRevision >= 1
|
|
228
|
+
&& record.revision > host.startRevision
|
|
229
|
+
&& record.state.child?.childThreadId === receipt.childId
|
|
230
|
+
&& record.state.stop?.childThreadId === receipt.childId;
|
|
231
|
+
}
|
|
232
|
+
|
|
163
233
|
function abortedCandidates(run: OrchestrationRun, rows: readonly StepRow[]): RecoveryCandidate[] {
|
|
164
234
|
const candidates = run.receipts.flatMap((receipt) => {
|
|
165
235
|
if (receipt.status !== 'aborted' || receipt.activatedAt === null || receipt.client !== 'codex') return [];
|
|
@@ -228,62 +298,117 @@ function recoverStartedLifecycleUnderLock(
|
|
|
228
298
|
|
|
229
299
|
const rows = openRows(section);
|
|
230
300
|
const pending = run.pendingDelegation;
|
|
231
|
-
if (!pending)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
301
|
+
if (!pending) return recoverAbortedLifecycle(request, taskId, section, resolved.repoRoot, run, rows, options);
|
|
302
|
+
return recoverActiveLifecycle(request, taskId, section, resolved.repoRoot, run, rows, pending, options);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function recoverAbortedLifecycle(
|
|
306
|
+
request: LifecycleRecoveryRequest,
|
|
307
|
+
taskId: string,
|
|
308
|
+
section: ActivityLogSection,
|
|
309
|
+
repoRoot: string,
|
|
310
|
+
run: OrchestrationRun,
|
|
311
|
+
rows: readonly StepRow[],
|
|
312
|
+
options: LifecycleRecoveryOptions
|
|
313
|
+
): LifecycleRecoveryResult {
|
|
314
|
+
const candidates = abortedCandidates(run, rows);
|
|
315
|
+
if (candidates.length === 0) return result(request, 'no-op', { taskId });
|
|
316
|
+
if (candidates.length > 1) {
|
|
317
|
+
return failure(request, 'conflict', 'RECOVERY_CANDIDATE_AMBIGUOUS', 'more than one aborted delegation has an open lifecycle row', { taskId });
|
|
238
318
|
}
|
|
319
|
+
return appendRecoveryLog(request, taskId, section, candidates[0]!, repoRoot, options.writeTask ?? writeTask);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function recoverActiveLifecycle(
|
|
323
|
+
request: LifecycleRecoveryRequest,
|
|
324
|
+
taskId: string,
|
|
325
|
+
section: ActivityLogSection,
|
|
326
|
+
repoRoot: string,
|
|
327
|
+
run: OrchestrationRun,
|
|
328
|
+
rows: readonly StepRow[],
|
|
329
|
+
pending: DelegationReceipt,
|
|
330
|
+
options: LifecycleRecoveryOptions
|
|
331
|
+
): LifecycleRecoveryResult {
|
|
239
332
|
if (pending.status !== 'activated') return result(request, 'no-op', { taskId });
|
|
240
333
|
if (pending.client !== 'codex' || run.status !== 'running' || run.pause !== null) {
|
|
241
334
|
return failure(request, 'conflict', 'RECOVERY_ORCHESTRATION_INVALID', 'activated recovery requires one running Codex delegation', {
|
|
242
335
|
taskId, receiptId: pending.id, childId: pending.childId
|
|
243
336
|
});
|
|
244
337
|
}
|
|
338
|
+
const rowResult = matchingActiveRecoveryRow(request, taskId, rows, pending);
|
|
339
|
+
if ('result' in rowResult) return rowResult.result;
|
|
340
|
+
const evidenceResult = consumeRecoveryEvidence(request, taskId, repoRoot, pending, options);
|
|
341
|
+
if ('result' in evidenceResult) return evidenceResult.result;
|
|
342
|
+
return finishActiveRecovery(request, taskId, section, repoRoot, pending, rowResult.row, options);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function matchingActiveRecoveryRow(
|
|
346
|
+
request: LifecycleRecoveryRequest,
|
|
347
|
+
taskId: string,
|
|
348
|
+
rows: readonly StepRow[],
|
|
349
|
+
pending: DelegationReceipt
|
|
350
|
+
): { row: StepRow } | { result: LifecycleRecoveryResult } {
|
|
245
351
|
const matchingRows = rowForReceipt(rows, pending);
|
|
246
352
|
if (matchingRows.length !== 1 || normalizeAgentToken(matchingRows[0]!.agent) !== 'codex' || matchingRows[0]!.note !== 'started') {
|
|
247
|
-
return failure(request, 'conflict', 'RECOVERY_LOG_CONFLICT', 'activated delegation does not have one matching open lifecycle row', {
|
|
353
|
+
return { result: failure(request, 'conflict', 'RECOVERY_LOG_CONFLICT', 'activated delegation does not have one matching open lifecycle row', {
|
|
248
354
|
taskId, receiptId: pending.id, childId: pending.childId
|
|
249
|
-
});
|
|
355
|
+
}) };
|
|
250
356
|
}
|
|
251
357
|
if (!pending.childId) {
|
|
252
|
-
return failure(request, 'conflict', 'RECOVERY_DELEGATION_INVALID', 'activated delegation has no child identity', { taskId, receiptId: pending.id });
|
|
358
|
+
return { result: failure(request, 'conflict', 'RECOVERY_DELEGATION_INVALID', 'activated delegation has no child identity', { taskId, receiptId: pending.id }) };
|
|
253
359
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if ('error' in stored) return failure(request, 'owner-unknown', 'RECOVERY_STORE_UNKNOWN', stored.error.message, { taskId, receiptId: pending.id, childId: pending.childId });
|
|
257
|
-
if ('missing' in stored) return failure(request, 'owner-unknown', 'RECOVERY_STOP_EVIDENCE_MISSING', 'matching Codex lifecycle stop evidence is missing', { taskId, receiptId: pending.id, childId: pending.childId });
|
|
258
|
-
const evidence = validateStopRecord(stored, pending);
|
|
259
|
-
if (!evidence.ok) return failure(request, evidence.code === 'RECOVERY_CONSUMER_CONFLICT' ? 'conflict' : 'owner-unknown', evidence.code, evidence.message, { taskId, receiptId: pending.id, childId: pending.childId });
|
|
360
|
+
return { row: matchingRows[0]! };
|
|
361
|
+
}
|
|
260
362
|
|
|
363
|
+
function consumeRecoveryEvidence(
|
|
364
|
+
request: LifecycleRecoveryRequest,
|
|
365
|
+
taskId: string,
|
|
366
|
+
repoRoot: string,
|
|
367
|
+
pending: DelegationReceipt,
|
|
368
|
+
options: LifecycleRecoveryOptions
|
|
369
|
+
): { consumed: StoredCodexLifecycle } | { result: LifecycleRecoveryResult } {
|
|
370
|
+
const store = readLifecycleStore(options, repoRoot, taskId);
|
|
371
|
+
const stored = readStoredEvidence(store, pending.childId!);
|
|
372
|
+
if ('error' in stored) return { result: failure(request, 'owner-unknown', 'RECOVERY_STORE_UNKNOWN', stored.error.message, { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
373
|
+
if ('missing' in stored) return { result: failure(request, 'owner-unknown', 'RECOVERY_STOP_EVIDENCE_MISSING', 'matching Codex lifecycle stop evidence is missing', { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
374
|
+
const evidence = validateStopRecord(stored, pending);
|
|
375
|
+
if (!evidence.ok) return { result: failure(request, evidence.code === 'RECOVERY_CONSUMER_CONFLICT' ? 'conflict' : 'owner-unknown', evidence.code, evidence.message, { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
261
376
|
let consumed = stored;
|
|
262
377
|
if (stored.consumer === null) {
|
|
263
378
|
try {
|
|
264
|
-
consumed = store.
|
|
379
|
+
consumed = store.consumeWithinTaskLock(pending.childId!, pending.id, stored.state.startEvidence?.hookDefinitionHash, {
|
|
380
|
+
taskId: pending.taskId, runId: pending.runId, receiptId: pending.id
|
|
381
|
+
});
|
|
265
382
|
} catch (error) {
|
|
266
|
-
return failure(request, 'owner-unknown', 'RECOVERY_EVIDENCE_CONSUME_FAILED', error instanceof Error ? error.message : String(error), { taskId, receiptId: pending.id, childId: pending.childId });
|
|
383
|
+
return { result: failure(request, 'owner-unknown', 'RECOVERY_EVIDENCE_CONSUME_FAILED', error instanceof Error ? error.message : String(error), { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
267
384
|
}
|
|
268
385
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
386
|
+
return consumed.consumedAt
|
|
387
|
+
? { consumed }
|
|
388
|
+
: { result: failure(request, 'owner-unknown', 'RECOVERY_EVIDENCE_INVALID', 'consumed stop evidence has no timestamp', { taskId, receiptId: pending.id, childId: pending.childId }) };
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function finishActiveRecovery(
|
|
392
|
+
request: LifecycleRecoveryRequest,
|
|
393
|
+
taskId: string,
|
|
394
|
+
section: ActivityLogSection,
|
|
395
|
+
repoRoot: string,
|
|
396
|
+
pending: DelegationReceipt,
|
|
397
|
+
row: StepRow,
|
|
398
|
+
options: LifecycleRecoveryOptions
|
|
399
|
+
): LifecycleRecoveryResult {
|
|
272
400
|
const recovered = recoverActivatedOrchestrationDelegationUnderLock(taskId, {
|
|
273
401
|
receiptId: pending.id,
|
|
274
402
|
stage: pending.stage,
|
|
275
403
|
round: pending.round,
|
|
276
404
|
artifact: pending.artifact,
|
|
277
405
|
startedAgent: request.agent,
|
|
278
|
-
childId: pending.childId
|
|
279
|
-
|
|
280
|
-
consumer: pending.id,
|
|
281
|
-
consumedAt: consumed.consumedAt
|
|
282
|
-
}, { ...options.orchestration, repoRoot: resolved.repoRoot, now: options.now });
|
|
406
|
+
childId: pending.childId!
|
|
407
|
+
}, { ...options.orchestration, repoRoot, now: options.now });
|
|
283
408
|
if (recovered.status === 'failed' || !recovered.run) {
|
|
284
409
|
return failure(request, 'owner-unknown', recovered.error?.code ?? 'RECOVERY_ORCHESTRATION_FAILED', recovered.error?.message ?? 'orchestration recovery failed', { taskId, receiptId: pending.id, childId: pending.childId });
|
|
285
410
|
}
|
|
286
|
-
return appendRecoveryLog(request, taskId, section, { receipt: recovered.run.receipts.at(-1)!, row
|
|
411
|
+
return appendRecoveryLog(request, taskId, section, { receipt: recovered.run.receipts.at(-1)!, row }, repoRoot, options.writeTask ?? writeTask);
|
|
287
412
|
}
|
|
288
413
|
|
|
289
414
|
function recoverStartedLifecycleFromAdapter(
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runCodexSandboxController,
|
|
3
3
|
verifyCodexSandboxControllerContextWithWarnings
|
|
4
|
-
} from '
|
|
5
|
-
import { ensureInternalHandlerRoute, internalHandlerRoute } from '
|
|
4
|
+
} from './sandbox-controller.ts';
|
|
5
|
+
import { ensureInternalHandlerRoute, internalHandlerRoute } from '../../../internal/cli-route-inventory.ts';
|
|
6
6
|
|
|
7
7
|
const USAGE = 'Usage: agent-infra-internal codex-sandbox-controller <run|verify-context> [options]\n';
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
requestCodexControllerClose,
|
|
11
11
|
requestCodexControllerOpen,
|
|
12
12
|
requestCodexControllerVerify
|
|
13
|
-
} from '
|
|
13
|
+
} from './controller-client.ts';
|
|
14
14
|
import { getProcessStartTime, type ProcessIdentity } from '../../../server/process-state.ts';
|
|
15
15
|
import { LIFECYCLE_PROTOCOL_VERSION, type LifecycleIdentityWarning } from './build-identity.ts';
|
|
16
16
|
import {
|
|
@@ -126,8 +126,7 @@ function verifyControllerBinding(
|
|
|
126
126
|
...control,
|
|
127
127
|
timeoutMs: 30_000
|
|
128
128
|
});
|
|
129
|
-
if (verified.binding.
|
|
130
|
-
|| verified.binding.controlGeneration !== context.controlGeneration
|
|
129
|
+
if (verified.binding.controlGeneration !== context.controlGeneration
|
|
131
130
|
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
132
131
|
throw new Error('CODEX_SANDBOX_CONTROLLER_TASK_BINDING_INVALID');
|
|
133
132
|
}
|
|
@@ -150,8 +149,7 @@ function verifyCodexSandboxControllerContextWithWarnings(
|
|
|
150
149
|
...control,
|
|
151
150
|
timeoutMs: 30_000
|
|
152
151
|
});
|
|
153
|
-
if (verified.binding.
|
|
154
|
-
|| verified.binding.controlGeneration !== context.controlGeneration
|
|
152
|
+
if (verified.binding.controlGeneration !== context.controlGeneration
|
|
155
153
|
|| verified.binding.controllerInstanceDigest !== context.controllerInstanceDigest) {
|
|
156
154
|
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
157
155
|
}
|