@fitlab-ai/agent-infra 0.9.5 → 0.9.7
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 +8 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +171 -20
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +162 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +244 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +370 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +68 -8
- package/dist/lib/agent-clients/adapters/codex.js +1 -1
- package/dist/lib/defaults.json +10 -2
- package/dist/lib/git/workflow.js +30 -4
- package/dist/lib/internal/codex-lifecycle.js +169 -3
- package/dist/lib/internal/codex-sandbox-controller.js +106 -0
- package/dist/lib/internal/release-workflow.js +108 -11
- package/dist/lib/internal/sandbox-control.js +43 -6
- package/dist/lib/internal/task-create.js +12 -4
- package/dist/lib/internal/task-orchestration.js +40 -7
- package/dist/lib/platform/context.js +6 -4
- package/dist/lib/platform/github-client.js +55 -2
- package/dist/lib/platform/issue-comments.js +2 -1
- package/dist/lib/process-data/checkpoint.js +319 -0
- package/dist/lib/process-data/index.js +128 -60
- package/dist/lib/process-data/materialize.js +84 -0
- package/dist/lib/process-data/normalize.js +34 -1
- package/dist/lib/process-data/reconcile.js +55 -1
- package/dist/lib/process-data/sources.js +422 -1
- package/dist/lib/process-data/store.js +341 -11
- package/dist/lib/release/workflow.js +29 -21
- package/dist/lib/sandbox/commands/create.js +111 -60
- package/dist/lib/sandbox/commands/prune.js +40 -16
- package/dist/lib/sandbox/commands/rm.js +372 -94
- package/dist/lib/sandbox/commands/start.js +4 -3
- package/dist/lib/sandbox/control/client.js +138 -22
- package/dist/lib/sandbox/control/container-identity.js +64 -0
- package/dist/lib/sandbox/control/executor.js +142 -0
- package/dist/lib/sandbox/control/lifecycle.js +557 -0
- package/dist/lib/sandbox/control/protocol.js +69 -26
- package/dist/lib/sandbox/control/server.js +395 -99
- package/dist/lib/sandbox/control/state.js +191 -0
- package/dist/lib/sandbox/control/timing.js +8 -0
- package/dist/lib/sandbox/index.js +2 -2
- package/dist/lib/sandbox/managed-fs.js +14 -2
- package/dist/lib/sandbox/mounts.js +22 -19
- package/dist/lib/sandbox/recovery.js +284 -50
- package/dist/lib/sandbox/workspace-view.js +90 -10
- package/dist/lib/sandbox/worktree-safety.js +196 -0
- package/dist/lib/server/daemon.js +1 -1
- package/dist/lib/server/process-control.js +17 -1
- package/dist/lib/server/process-state.js +57 -20
- package/dist/lib/task/artifact-lifecycle.js +87 -15
- package/dist/lib/task/artifact-receipts.js +128 -0
- package/dist/lib/task/codex-orchestration.js +236 -0
- package/dist/lib/task/commands/status.js +3 -0
- package/dist/lib/task/commands/validate.js +289 -0
- package/dist/lib/task/commit-finalization.js +36 -9
- package/dist/lib/task/create.js +10 -2
- package/dist/lib/task/delegation-receipts.js +109 -2
- package/dist/lib/task/events.js +149 -6
- package/dist/lib/task/finalization-artifacts.js +2 -1
- package/dist/lib/task/implementation-inputs.js +1 -7
- package/dist/lib/task/index.js +9 -0
- package/dist/lib/task/ledger.js +13 -1
- package/dist/lib/task/orchestration.js +365 -15
- package/dist/lib/task/sections.js +5 -1
- package/dist/lib/task/task-execution-lock.js +5 -4
- package/dist/lib/task/verification-engine.js +78 -14
- package/dist/lib/task/verification.js +1 -0
- package/dist/lifecycle-build-manifest.json +6 -0
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +202 -18
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +227 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +321 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -3
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +26 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +453 -0
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +83 -6
- package/lib/agent-clients/adapters/codex.ts +1 -1
- package/lib/defaults.json +10 -2
- package/lib/git/workflow.ts +25 -3
- package/lib/internal/codex-lifecycle.ts +175 -4
- package/lib/internal/codex-sandbox-controller.ts +109 -0
- package/lib/internal/release-workflow.ts +107 -11
- package/lib/internal/sandbox-control.ts +38 -6
- package/lib/internal/task-create.ts +16 -4
- package/lib/internal/task-orchestration.ts +34 -7
- package/lib/platform/context.ts +8 -5
- package/lib/platform/github-client.ts +61 -3
- package/lib/platform/issue-comments.ts +2 -1
- package/lib/process-data/checkpoint.ts +335 -0
- package/lib/process-data/index.ts +112 -60
- package/lib/process-data/materialize.ts +83 -0
- package/lib/process-data/normalize.ts +29 -1
- package/lib/process-data/reconcile.ts +64 -2
- package/lib/process-data/sources.ts +449 -2
- package/lib/process-data/store.ts +304 -11
- package/lib/process-data/types.ts +124 -2
- package/lib/release/workflow.ts +69 -20
- package/lib/sandbox/commands/create.ts +70 -25
- package/lib/sandbox/commands/prune.ts +53 -15
- package/lib/sandbox/commands/rm.ts +423 -108
- package/lib/sandbox/commands/start.ts +4 -3
- package/lib/sandbox/control/client.ts +152 -28
- package/lib/sandbox/control/container-identity.ts +93 -0
- package/lib/sandbox/control/executor.ts +167 -0
- package/lib/sandbox/control/lifecycle.ts +573 -0
- package/lib/sandbox/control/protocol.ts +121 -68
- package/lib/sandbox/control/server.ts +398 -109
- package/lib/sandbox/control/state.ts +213 -0
- package/lib/sandbox/control/timing.ts +12 -0
- package/lib/sandbox/index.ts +2 -2
- package/lib/sandbox/managed-fs.ts +17 -2
- package/lib/sandbox/mounts.ts +16 -8
- package/lib/sandbox/recovery.ts +335 -65
- package/lib/sandbox/workspace-view.ts +102 -11
- package/lib/sandbox/worktree-safety.ts +234 -0
- package/lib/server/daemon.ts +1 -1
- package/lib/server/process-control.ts +24 -3
- package/lib/server/process-state.ts +82 -27
- package/lib/task/artifact-lifecycle.ts +78 -14
- package/lib/task/artifact-receipts.ts +161 -0
- package/lib/task/codex-orchestration.ts +319 -0
- package/lib/task/commands/status.ts +2 -0
- package/lib/task/commands/validate.ts +282 -0
- package/lib/task/commit-finalization.ts +52 -9
- package/lib/task/create.ts +11 -2
- package/lib/task/delegation-receipts.ts +208 -7
- package/lib/task/events.ts +131 -5
- package/lib/task/finalization-artifacts.ts +2 -1
- package/lib/task/implementation-inputs.ts +1 -5
- package/lib/task/index.ts +8 -0
- package/lib/task/ledger.ts +16 -1
- package/lib/task/orchestration.ts +552 -18
- package/lib/task/sections.ts +6 -2
- package/lib/task/task-execution-lock.ts +11 -9
- package/lib/task/verification-engine.ts +93 -24
- package/lib/task/verification.ts +2 -1
- package/package.json +1 -1
- package/templates/.agents/README.en.md +7 -0
- package/templates/.agents/README.zh-CN.md +5 -0
- package/templates/.agents/hooks/lifecycle-delegation.js +72 -12
- package/templates/.agents/rules/lifecycle-orchestration.en.md +8 -2
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +8 -2
- package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -0
- package/templates/.agents/rules/pr-checks-commands.github.en.md +1 -1
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.en.md +3 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
- package/templates/.agents/skills/post-release/SKILL.en.md +32 -6
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +32 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +42 -0
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +42 -0
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +25 -0
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +25 -0
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +67 -0
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +67 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +69 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +69 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +9 -7
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +9 -7
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +5 -3
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +5 -3
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +75 -3
- package/templates/.claude/commands/run-manual-validation.en.md +9 -0
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +9 -0
- package/templates/.codex/hooks.json +8 -8
- package/templates/.opencode/commands/run-manual-validation.en.md +11 -0
- package/templates/.opencode/commands/run-manual-validation.zh-CN.md +11 -0
- package/templates/.agents/workspace/README.en.md +0 -26
- package/templates/.agents/workspace/README.zh-CN.md +0 -26
|
@@ -7,7 +7,7 @@ import { parseReviewSummary } from './review-artifacts.js';
|
|
|
7
7
|
import { inspectArtifactDirectory } from './artifact-lifecycle.js';
|
|
8
8
|
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
9
9
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
10
|
-
import { activateDelegation, completeDelegationStage, consumeDelegation, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
|
|
10
|
+
import { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
|
|
11
11
|
import { normalizeAgentClients } from '../agent-clients/config.js';
|
|
12
12
|
import { inspectPlatformPullRequest } from '../platform/pull-requests.js';
|
|
13
13
|
import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
|
|
@@ -15,6 +15,7 @@ import { isAgentClientId } from '../agent-clients/types.js';
|
|
|
15
15
|
import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
|
|
16
16
|
import { assertGitRepositoryBinding } from '../git/worktree-identity.js';
|
|
17
17
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
18
|
+
import { hasActiveCodexLifecycleEvidence } from '../agent-clients/adapters/codex-lifecycle/store.js';
|
|
18
19
|
import { CommitIntentError, commitIntentPath, createCommitIntent, digest, readCommitIntent, removeCommitIntent, removeCommitIntentByDigest, serialize, updateCommitIntent } from './commit-intent.js';
|
|
19
20
|
import { inspectCommitFinalization, planCommitTaskFinalization } from './commit-finalization.js';
|
|
20
21
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
@@ -33,7 +34,7 @@ function readRun(taskDir) {
|
|
|
33
34
|
if (!fs.existsSync(file))
|
|
34
35
|
return null;
|
|
35
36
|
const run = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
36
|
-
if (run.schemaVersion
|
|
37
|
+
if (![2, 3].includes(run.schemaVersion) || !isV2Policy(run.modelPolicy))
|
|
37
38
|
return run;
|
|
38
39
|
return {
|
|
39
40
|
...run,
|
|
@@ -110,7 +111,7 @@ function commitFinalizationView(inspection) {
|
|
|
110
111
|
}
|
|
111
112
|
function validCommitRun(run, taskId) {
|
|
112
113
|
return run !== null
|
|
113
|
-
&& [1, 2].includes(run.schemaVersion)
|
|
114
|
+
&& [1, 2, 3].includes(run.schemaVersion)
|
|
114
115
|
&& run.taskId === taskId
|
|
115
116
|
&& ['running', 'paused', 'completed'].includes(run.status)
|
|
116
117
|
&& Array.isArray(run.receipts)
|
|
@@ -159,6 +160,36 @@ function resolveProjectPolicy(repoRoot, client) {
|
|
|
159
160
|
const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
160
161
|
return normalizeAgentClients(raw).state[client].orchestration;
|
|
161
162
|
}
|
|
163
|
+
function canRecoverCodexUnsupportedPause(run, taskDir) {
|
|
164
|
+
return run.schemaVersion === 2
|
|
165
|
+
&& run.status === 'paused'
|
|
166
|
+
&& run.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
167
|
+
&& run.modelPolicySource?.client === 'codex'
|
|
168
|
+
&& run.stepCount === 0
|
|
169
|
+
&& run.nextStage === null
|
|
170
|
+
&& run.baseline === ''
|
|
171
|
+
&& run.pendingDelegation === null
|
|
172
|
+
&& run.receipts.length === 0
|
|
173
|
+
&& run.commitAuthorization?.issuedAt === null
|
|
174
|
+
&& run.commitAuthorization?.consumedAt === null
|
|
175
|
+
&& run.completionEvidence == null
|
|
176
|
+
&& !fs.existsSync(commitIntentPath(taskDir))
|
|
177
|
+
&& (run.recoveryHistory ?? []).every((entry) => entry.code === 'MODEL_POLICY_SUPPLEMENTED');
|
|
178
|
+
}
|
|
179
|
+
function canMigrateRecoverableV2Pause(run, taskDir) {
|
|
180
|
+
return run.schemaVersion === 2
|
|
181
|
+
&& run.status === 'paused'
|
|
182
|
+
&& run.pause?.recoverable === true
|
|
183
|
+
&& run.stepCount === 0
|
|
184
|
+
&& run.nextStage === null
|
|
185
|
+
&& run.baseline === ''
|
|
186
|
+
&& run.pendingDelegation === null
|
|
187
|
+
&& run.receipts.length === 0
|
|
188
|
+
&& run.commitAuthorization?.issuedAt === null
|
|
189
|
+
&& run.commitAuthorization?.consumedAt === null
|
|
190
|
+
&& run.completionEvidence == null
|
|
191
|
+
&& !fs.existsSync(commitIntentPath(taskDir));
|
|
192
|
+
}
|
|
162
193
|
function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
163
194
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
164
195
|
if (!resolved.ok)
|
|
@@ -174,7 +205,7 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
174
205
|
return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
175
206
|
}
|
|
176
207
|
if (existing) {
|
|
177
|
-
if (![1, 2].includes(existing.schemaVersion)
|
|
208
|
+
if (![1, 2, 3].includes(existing.schemaVersion)
|
|
178
209
|
|| !Array.isArray(existing.receipts)
|
|
179
210
|
|| !Object.prototype.hasOwnProperty.call(existing, 'pendingDelegation')) {
|
|
180
211
|
return failed('ORCHESTRATION_STATE_INVALID', 'orchestration state has an invalid schema', resolved.taskId);
|
|
@@ -182,17 +213,20 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
182
213
|
if (existing.status === 'completed' && existing.schemaVersion === 1) {
|
|
183
214
|
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
184
215
|
}
|
|
185
|
-
if (existing.schemaVersion === 2) {
|
|
216
|
+
if (existing.schemaVersion === 2 || existing.schemaVersion === 3) {
|
|
186
217
|
const policyError = validateModelPolicy(isV2Policy(existing.modelPolicy) ? existing.modelPolicy : undefined);
|
|
187
218
|
if (policyError
|
|
188
219
|
|| !existing.modelPolicySource
|
|
189
220
|
|| !isAgentClientId(existing.modelPolicySource.client)
|
|
190
221
|
|| !Array.isArray(existing.recoveryHistory)) {
|
|
191
|
-
return failed('ORCHESTRATION_STATE_INVALID',
|
|
222
|
+
return failed('ORCHESTRATION_STATE_INVALID', `schemaVersion ${existing.schemaVersion} orchestration state is incomplete`, resolved.taskId);
|
|
192
223
|
}
|
|
193
224
|
if (existing.status === 'completed') {
|
|
194
225
|
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
195
226
|
}
|
|
227
|
+
if (existing.schemaVersion === 2 && existing.status === 'running') {
|
|
228
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'active schemaVersion 2 runs cannot be advanced by lifecycle protocol v3', resolved.taskId);
|
|
229
|
+
}
|
|
196
230
|
if (existing.modelPolicySource.client !== options.client) {
|
|
197
231
|
return failed('ORCHESTRATION_CLIENT_MISMATCH', 'provided client does not match the persisted run policy source', resolved.taskId);
|
|
198
232
|
}
|
|
@@ -204,8 +238,77 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
204
238
|
return failed('ORCHESTRATION_MODEL_POLICY_MISMATCH', 'provided model policy does not match the persisted run policy', resolved.taskId);
|
|
205
239
|
}
|
|
206
240
|
}
|
|
241
|
+
if (existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED') {
|
|
242
|
+
if (!canRecoverCodexUnsupportedPause(existing, resolved.taskDir)) {
|
|
243
|
+
return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
244
|
+
}
|
|
245
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
246
|
+
const previousPause = existing.pause;
|
|
247
|
+
const recovery = {
|
|
248
|
+
code: 'CLIENT_CAPABILITY_ENABLED',
|
|
249
|
+
recoveredAt: now,
|
|
250
|
+
previousSchemaVersion: 2,
|
|
251
|
+
previousStatus: 'paused',
|
|
252
|
+
previousPause,
|
|
253
|
+
client: 'codex',
|
|
254
|
+
guards: {
|
|
255
|
+
stepCount: 0,
|
|
256
|
+
nextStage: null,
|
|
257
|
+
baselineEmpty: true,
|
|
258
|
+
receiptCount: 0,
|
|
259
|
+
pendingDelegation: false,
|
|
260
|
+
commitAuthorizationUnused: true,
|
|
261
|
+
completionEvidenceAbsent: true,
|
|
262
|
+
commitIntentAbsent: true
|
|
263
|
+
},
|
|
264
|
+
resultingStatus: 'running'
|
|
265
|
+
};
|
|
266
|
+
const resumed = withUpdatedRun(existing, {
|
|
267
|
+
schemaVersion: 3,
|
|
268
|
+
status: 'running',
|
|
269
|
+
pause: null,
|
|
270
|
+
recoveryHistory: Object.freeze([...(existing.recoveryHistory ?? []), recovery])
|
|
271
|
+
}, () => now);
|
|
272
|
+
saveRun(resolved.taskDir, resumed);
|
|
273
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
274
|
+
}
|
|
207
275
|
if (existing.status === 'paused' && existing.pause?.recoverable && existing.pendingDelegation === null) {
|
|
208
|
-
|
|
276
|
+
if (existing.schemaVersion === 2 && !canMigrateRecoverableV2Pause(existing, resolved.taskDir)) {
|
|
277
|
+
const paused = withUpdatedRun(existing, {
|
|
278
|
+
pause: {
|
|
279
|
+
code: 'ORCHESTRATION_SCHEMA_MIGRATION_REQUIRED',
|
|
280
|
+
message: 'schemaVersion 2 recoverable pause contains execution evidence and cannot migrate automatically',
|
|
281
|
+
recoverable: false
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
saveRun(resolved.taskDir, paused);
|
|
285
|
+
return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
|
|
286
|
+
}
|
|
287
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
288
|
+
const recovery = existing.schemaVersion === 2 ? {
|
|
289
|
+
code: 'SCHEMA_V3_MIGRATED',
|
|
290
|
+
recoveredAt: now,
|
|
291
|
+
previousSchemaVersion: 2,
|
|
292
|
+
previousStatus: 'paused',
|
|
293
|
+
previousPause: existing.pause,
|
|
294
|
+
guards: {
|
|
295
|
+
stepCount: 0,
|
|
296
|
+
nextStage: null,
|
|
297
|
+
baselineEmpty: true,
|
|
298
|
+
receiptCount: 0,
|
|
299
|
+
pendingDelegation: false,
|
|
300
|
+
commitAuthorizationUnused: true,
|
|
301
|
+
completionEvidenceAbsent: true,
|
|
302
|
+
commitIntentAbsent: true
|
|
303
|
+
},
|
|
304
|
+
resultingStatus: 'running'
|
|
305
|
+
} : null;
|
|
306
|
+
const resumed = withUpdatedRun(existing, {
|
|
307
|
+
schemaVersion: 3,
|
|
308
|
+
status: 'running',
|
|
309
|
+
pause: null,
|
|
310
|
+
...(recovery ? { recoveryHistory: Object.freeze([...(existing.recoveryHistory ?? []), recovery]) } : {})
|
|
311
|
+
}, () => now);
|
|
209
312
|
saveRun(resolved.taskDir, resumed);
|
|
210
313
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
211
314
|
}
|
|
@@ -279,7 +382,7 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
279
382
|
};
|
|
280
383
|
const recovered = Object.freeze({
|
|
281
384
|
...existing,
|
|
282
|
-
schemaVersion:
|
|
385
|
+
schemaVersion: 3,
|
|
283
386
|
status: resultingStatus,
|
|
284
387
|
modelPolicy: policy,
|
|
285
388
|
modelPolicySource: source,
|
|
@@ -311,7 +414,7 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
311
414
|
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
312
415
|
const source = { kind: sourceKind, client: options.client, resolvedAt: now };
|
|
313
416
|
const run = {
|
|
314
|
-
schemaVersion:
|
|
417
|
+
schemaVersion: 3,
|
|
315
418
|
taskId: resolved.taskId,
|
|
316
419
|
runId: (options.id ?? randomUUID)(),
|
|
317
420
|
status: 'running',
|
|
@@ -976,6 +1079,9 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
976
1079
|
const run = readRun(resolved.taskDir);
|
|
977
1080
|
if (!run || run.status !== 'running')
|
|
978
1081
|
return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
|
|
1082
|
+
if (run.schemaVersion !== 3) {
|
|
1083
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'delegation preparation requires orchestration schemaVersion 3', resolved.taskId);
|
|
1084
|
+
}
|
|
979
1085
|
if (!isV2Policy(run.modelPolicy))
|
|
980
1086
|
return failed('ORCHESTRATION_MODEL_EVIDENCE_MISSING', 'running orchestration has no persisted model policy', resolved.taskId);
|
|
981
1087
|
if (run.pendingDelegation)
|
|
@@ -996,6 +1102,9 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
996
1102
|
if (!validModel(input.requestedReasoningEffort)) {
|
|
997
1103
|
return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', resolved.taskId);
|
|
998
1104
|
}
|
|
1105
|
+
if (input.client === 'codex' && !input.lifecycleProvenance) {
|
|
1106
|
+
return failed('ORCHESTRATION_CODEX_PROVENANCE_REQUIRED', 'Codex prepare requires lifecycle provenance', resolved.taskId);
|
|
1107
|
+
}
|
|
999
1108
|
const expectedPolicy = run.modelPolicy[next.role];
|
|
1000
1109
|
if (input.requestedModel !== expectedPolicy.model) {
|
|
1001
1110
|
return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
|
|
@@ -1025,8 +1134,9 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
1025
1134
|
requestedModel: input.requestedModel,
|
|
1026
1135
|
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
1027
1136
|
workspaceSnapshotScope: 'task',
|
|
1137
|
+
lifecycleProvenance: input.lifecycleProvenance ?? null,
|
|
1028
1138
|
beforeFingerprint
|
|
1029
|
-
}, { id: options.id, now: options.now });
|
|
1139
|
+
}, { id: options.id, now: options.now, monotonicNow: options.monotonicNow });
|
|
1030
1140
|
const updated = withUpdatedRun(run, {
|
|
1031
1141
|
nextStage: next.stage,
|
|
1032
1142
|
pendingDelegation: receipt,
|
|
@@ -1050,6 +1160,39 @@ function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
|
1050
1160
|
throw error;
|
|
1051
1161
|
}
|
|
1052
1162
|
}
|
|
1163
|
+
function dispatchOrchestrationDelegationUnlocked(taskRef, options = {}) {
|
|
1164
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1165
|
+
if (!resolved.ok)
|
|
1166
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1167
|
+
const run = readRun(resolved.taskDir);
|
|
1168
|
+
if (!run?.pendingDelegation)
|
|
1169
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1170
|
+
if (run.schemaVersion !== 3 || run.status !== 'running') {
|
|
1171
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'spawn dispatch requires a running schemaVersion 3 orchestration', resolved.taskId);
|
|
1172
|
+
}
|
|
1173
|
+
const result = dispatchDelegation(run.pendingDelegation, {
|
|
1174
|
+
now: options.now,
|
|
1175
|
+
monotonicNow: options.monotonicNow
|
|
1176
|
+
});
|
|
1177
|
+
if (!result.ok)
|
|
1178
|
+
return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
1179
|
+
const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
|
|
1180
|
+
saveRun(resolved.taskDir, updated);
|
|
1181
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1182
|
+
}
|
|
1183
|
+
function dispatchOrchestrationDelegation(taskRef, options = {}) {
|
|
1184
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1185
|
+
if (!resolved.ok)
|
|
1186
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1187
|
+
try {
|
|
1188
|
+
return withTaskExecutionLock(resolved.repoRoot, '__repository__', 'task-orchestration.dispatch.repository', () => withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.dispatch.task', () => dispatchOrchestrationDelegationUnlocked(taskRef, options)));
|
|
1189
|
+
}
|
|
1190
|
+
catch (error) {
|
|
1191
|
+
if (error instanceof TaskExecutionLockError)
|
|
1192
|
+
return failed(error.code, error.message, resolved.taskId);
|
|
1193
|
+
throw error;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1053
1196
|
function matchingDelegations(predicate, options) {
|
|
1054
1197
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
1055
1198
|
const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
|
|
@@ -1059,7 +1202,7 @@ function matchingDelegations(predicate, options) {
|
|
|
1059
1202
|
.filter((entry) => entry.isDirectory())
|
|
1060
1203
|
.flatMap((entry) => {
|
|
1061
1204
|
const run = readRun(path.join(activeRoot, entry.name));
|
|
1062
|
-
if (!run
|
|
1205
|
+
if (run?.schemaVersion !== 3 || run.status !== 'running' || !run.pendingDelegation)
|
|
1063
1206
|
return [];
|
|
1064
1207
|
const receipt = run.pendingDelegation;
|
|
1065
1208
|
return predicate(receipt)
|
|
@@ -1075,18 +1218,50 @@ function uniqueMatchingDelegation(client, predicate, options) {
|
|
|
1075
1218
|
return failed('ORCHESTRATION_DELEGATION_AMBIGUOUS', 'multiple lifecycle delegations match the native hook event');
|
|
1076
1219
|
return matches[0];
|
|
1077
1220
|
}
|
|
1221
|
+
function hasActivatableOrchestrationDelegation(client, childId, options = {}) {
|
|
1222
|
+
return matchingDelegations((receipt) => receipt.client === client && (receipt.status === 'prepared'
|
|
1223
|
+
|| (receipt.childId === childId && ['activated', 'stage-completed'].includes(receipt.status))), options).length > 0;
|
|
1224
|
+
}
|
|
1225
|
+
function hasSealableOrchestrationDelegation(client, childId, options = {}) {
|
|
1226
|
+
return matchingDelegations((receipt) => receipt.client === client
|
|
1227
|
+
&& receipt.childId === childId
|
|
1228
|
+
&& ['stage-completed', 'sealed'].includes(receipt.status), options).length > 0;
|
|
1229
|
+
}
|
|
1078
1230
|
function activateMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
1079
1231
|
const role = managedDelegationRole(event.nativeAgent);
|
|
1080
1232
|
if (!role)
|
|
1081
1233
|
return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
|
|
1082
1234
|
const matched = uniqueMatchingDelegation(client, (receipt) => receipt.status === 'prepared', options);
|
|
1083
|
-
if ('status' in matched)
|
|
1084
|
-
|
|
1235
|
+
if ('status' in matched) {
|
|
1236
|
+
if (matched.error?.code !== 'ORCHESTRATION_DELEGATION_MISSING')
|
|
1237
|
+
return matched;
|
|
1238
|
+
const replay = uniqueMatchingDelegation(client, (receipt) => receipt.childId === event.childId && ['activated', 'stage-completed'].includes(receipt.status), options);
|
|
1239
|
+
if ('status' in replay)
|
|
1240
|
+
return matched;
|
|
1241
|
+
const receipt = replay.run.pendingDelegation;
|
|
1242
|
+
const sameEvidence = receipt.parentId === event.parentId
|
|
1243
|
+
&& receipt.spawnMode === event.spawnMode
|
|
1244
|
+
&& receipt.actualModel === event.actualModel
|
|
1245
|
+
&& receipt.actualReasoningEffort === event.actualReasoningEffort
|
|
1246
|
+
&& receipt.modelFallbackReason === (event.modelFallbackReason ?? null)
|
|
1247
|
+
&& receipt.reasoningEffortFallbackReason === (event.reasoningEffortFallbackReason ?? null)
|
|
1248
|
+
&& receipt.hostEvidence?.hookDefinitionHash === event.hostEvidence?.hookDefinitionHash;
|
|
1249
|
+
if (!sameEvidence) {
|
|
1250
|
+
return pauseOrchestration(replay.taskId, 'DELEGATION_REPLAY_CONFLICT', 'replayed native start evidence conflicts with the active receipt', true, options);
|
|
1251
|
+
}
|
|
1252
|
+
return { status: replay.run.status, changed: false, taskId: replay.taskId, run: replay.run, next: null, error: null };
|
|
1253
|
+
}
|
|
1085
1254
|
if (matched.run.receipts.some((receipt) => receipt.childId === event.childId)) {
|
|
1086
1255
|
return pauseOrchestration(matched.taskId, 'DELEGATION_IDENTITY_REUSED', 'native child identity was already used by this run', true, options);
|
|
1087
1256
|
}
|
|
1088
1257
|
return activateOrchestrationDelegation(matched.taskId, event, options);
|
|
1089
1258
|
}
|
|
1259
|
+
function pauseMatchingOrchestrationDelegation(client, code, message, options = {}) {
|
|
1260
|
+
const matched = uniqueMatchingDelegation(client, () => true, options);
|
|
1261
|
+
if ('status' in matched)
|
|
1262
|
+
return matched;
|
|
1263
|
+
return pauseOrchestration(matched.taskId, code, message, true, options);
|
|
1264
|
+
}
|
|
1090
1265
|
function sealMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
1091
1266
|
const role = managedDelegationRole(event.nativeAgent);
|
|
1092
1267
|
if (!role)
|
|
@@ -1119,6 +1294,63 @@ function sealMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
|
1119
1294
|
return pauseOrchestration(matched.taskId, 'ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), true, options);
|
|
1120
1295
|
}
|
|
1121
1296
|
}
|
|
1297
|
+
function sealMatchingOrchestrationDelegationWithHostEvidence(client, event, consumeEvidence, options = {}) {
|
|
1298
|
+
const role = managedDelegationRole(event.nativeAgent);
|
|
1299
|
+
if (!role)
|
|
1300
|
+
return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
|
|
1301
|
+
const matched = uniqueMatchingDelegation(client, (receipt) => ((receipt.status === 'stage-completed' || receipt.status === 'sealed')
|
|
1302
|
+
&& receipt.childId === event.childId), options);
|
|
1303
|
+
if ('status' in matched)
|
|
1304
|
+
return matched;
|
|
1305
|
+
const receipt = matched.run.pendingDelegation;
|
|
1306
|
+
if (receipt.status === 'sealed') {
|
|
1307
|
+
return reconcileMatchingOrchestrationDelegation(client, event.childId, options);
|
|
1308
|
+
}
|
|
1309
|
+
if (receipt.role !== role) {
|
|
1310
|
+
return pauseOrchestration(matched.taskId, 'DELEGATION_ROLE_MISMATCH', `managed role ${role} does not match ${receipt.role}`, true, options);
|
|
1311
|
+
}
|
|
1312
|
+
const repoRoot = options.repoRoot ?? process.cwd();
|
|
1313
|
+
try {
|
|
1314
|
+
const gitRoot = gitRootFor(repoRoot, options);
|
|
1315
|
+
const snapshotTaskId = receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null;
|
|
1316
|
+
const afterFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
|
|
1317
|
+
gitRoot,
|
|
1318
|
+
stateRoot: repoRoot,
|
|
1319
|
+
taskId: snapshotTaskId
|
|
1320
|
+
});
|
|
1321
|
+
const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(gitRoot, receipt.beforeFingerprint, afterFingerprint);
|
|
1322
|
+
const baseEvent = { childId: event.childId, exitCode: 0, afterFingerprint, changedPaths };
|
|
1323
|
+
const validated = sealDelegation(receipt, baseEvent, { now: options.now, requireHostEvidence: false });
|
|
1324
|
+
if (!validated.ok) {
|
|
1325
|
+
return pauseOrchestration(matched.taskId, validated.code, validated.message, true, options);
|
|
1326
|
+
}
|
|
1327
|
+
const hostEvidence = consumeEvidence(receipt);
|
|
1328
|
+
const sealed = sealDelegation(receipt, { ...baseEvent, hostEvidence }, { now: options.now });
|
|
1329
|
+
if (!sealed.ok)
|
|
1330
|
+
return pauseOrchestration(matched.taskId, sealed.code, sealed.message, true, options);
|
|
1331
|
+
const resolved = resolveTaskRef(matched.taskId, { repoRoot });
|
|
1332
|
+
if (!resolved.ok)
|
|
1333
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1334
|
+
const updated = withUpdatedRun(matched.run, { pendingDelegation: sealed.receipt }, options.now);
|
|
1335
|
+
saveRun(resolved.taskDir, updated);
|
|
1336
|
+
return { status: 'running', changed: true, taskId: matched.taskId, run: updated, next: null, error: null };
|
|
1337
|
+
}
|
|
1338
|
+
catch (error) {
|
|
1339
|
+
return pauseOrchestration(matched.taskId, 'ORCHESTRATION_CODEX_EVIDENCE_FAILED', error instanceof Error ? error.message : String(error), true, options);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
function reconcileMatchingOrchestrationDelegation(client, childId, options = {}) {
|
|
1343
|
+
const matched = uniqueMatchingDelegation(client, (receipt) => receipt.childId === childId, options);
|
|
1344
|
+
if ('status' in matched)
|
|
1345
|
+
return matched;
|
|
1346
|
+
const receipt = matched.run.pendingDelegation;
|
|
1347
|
+
if (receipt.status !== 'sealed'
|
|
1348
|
+
|| receipt.hostEvidence?.consumer !== receipt.id
|
|
1349
|
+
|| receipt.hostEvidence.consumedAt === null) {
|
|
1350
|
+
return pauseOrchestration(matched.taskId, 'ORCHESTRATION_CODEX_RECONCILIATION_FAILED', 'Codex native spawn did not produce one sealed receipt with consumed host evidence', true, options);
|
|
1351
|
+
}
|
|
1352
|
+
return { status: matched.run.status, changed: false, taskId: matched.taskId, run: matched.run, next: null, error: null };
|
|
1353
|
+
}
|
|
1122
1354
|
function activateOrchestrationDelegation(taskRef, event, options = {}) {
|
|
1123
1355
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1124
1356
|
if (!resolved.ok)
|
|
@@ -1126,7 +1358,15 @@ function activateOrchestrationDelegation(taskRef, event, options = {}) {
|
|
|
1126
1358
|
const run = readRun(resolved.taskDir);
|
|
1127
1359
|
if (!run?.pendingDelegation)
|
|
1128
1360
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1129
|
-
|
|
1361
|
+
if (run.schemaVersion !== 3)
|
|
1362
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'active delegation requires schemaVersion 3', resolved.taskId);
|
|
1363
|
+
if (run.status !== 'running') {
|
|
1364
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1365
|
+
}
|
|
1366
|
+
const result = activateDelegation(run.pendingDelegation, event, {
|
|
1367
|
+
now: options.now,
|
|
1368
|
+
monotonicNow: options.monotonicNow
|
|
1369
|
+
});
|
|
1130
1370
|
if (!result.ok) {
|
|
1131
1371
|
if (result.code === 'DELEGATION_IGNORED')
|
|
1132
1372
|
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
@@ -1136,6 +1376,108 @@ function activateOrchestrationDelegation(taskRef, event, options = {}) {
|
|
|
1136
1376
|
saveRun(resolved.taskDir, updated);
|
|
1137
1377
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1138
1378
|
}
|
|
1379
|
+
async function awaitOrchestrationDelegationActivation(taskRef, event, options = {}) {
|
|
1380
|
+
const sleep = options.sleep
|
|
1381
|
+
?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
1382
|
+
while (true) {
|
|
1383
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1384
|
+
if (!resolved.ok)
|
|
1385
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1386
|
+
const run = readRun(resolved.taskDir);
|
|
1387
|
+
if (!run?.pendingDelegation) {
|
|
1388
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1389
|
+
}
|
|
1390
|
+
if (run.schemaVersion !== 3) {
|
|
1391
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'activation barrier requires schemaVersion 3', resolved.taskId);
|
|
1392
|
+
}
|
|
1393
|
+
const receipt = run.pendingDelegation;
|
|
1394
|
+
const matches = receipt.stage === event.stage
|
|
1395
|
+
&& receipt.round === event.round
|
|
1396
|
+
&& receipt.artifact === event.artifact
|
|
1397
|
+
&& receipt.role === event.role;
|
|
1398
|
+
if (!matches) {
|
|
1399
|
+
return pauseOrchestration(taskRef, 'ORCHESTRATION_PROVENANCE_MISMATCH', 'activation barrier identity does not match the pending delegation', true, options);
|
|
1400
|
+
}
|
|
1401
|
+
if (receipt.status === 'activated' || receipt.status === 'stage-completed') {
|
|
1402
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1403
|
+
}
|
|
1404
|
+
if (run.status !== 'running') {
|
|
1405
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1406
|
+
}
|
|
1407
|
+
if (receipt.status !== 'prepared') {
|
|
1408
|
+
return pauseOrchestration(taskRef, 'ORCHESTRATION_ACTIVATION_STATE_INVALID', `activation barrier found receipt status '${receipt.status}'`, true, options);
|
|
1409
|
+
}
|
|
1410
|
+
if (receipt.activationDeadlineAt == null
|
|
1411
|
+
|| !Number.isFinite(Date.parse(receipt.activationDeadlineAt))) {
|
|
1412
|
+
return pauseOrchestration(taskRef, 'ORCHESTRATION_DELEGATION_NOT_DISPATCHED', 'activation barrier was entered before the native spawn dispatch boundary', true, options);
|
|
1413
|
+
}
|
|
1414
|
+
if (Date.parse((options.now ?? (() => new Date().toISOString()))()) >= Date.parse(receipt.activationDeadlineAt)) {
|
|
1415
|
+
return pauseOrchestration(taskRef, 'ORCHESTRATION_ACTIVATION_TIMEOUT', 'fresh child did not receive trusted activation evidence before the deadline', true, options);
|
|
1416
|
+
}
|
|
1417
|
+
await sleep(100);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
|
|
1421
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1422
|
+
if (!resolved.ok)
|
|
1423
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1424
|
+
try {
|
|
1425
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.recover-prepared', () => {
|
|
1426
|
+
const run = readRun(resolved.taskDir);
|
|
1427
|
+
const receipt = run?.pendingDelegation;
|
|
1428
|
+
if (!run || !receipt) {
|
|
1429
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1430
|
+
}
|
|
1431
|
+
if (receipt.status !== 'prepared') {
|
|
1432
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_UNSAFE', 'only a never-activated prepared receipt can recover', resolved.taskId);
|
|
1433
|
+
}
|
|
1434
|
+
const wallNow = Date.parse((options.now ?? (() => new Date().toISOString()))());
|
|
1435
|
+
if (receipt.activationDeadlineAt != null
|
|
1436
|
+
&& Number.isFinite(Date.parse(receipt.activationDeadlineAt))
|
|
1437
|
+
&& wallNow <= Date.parse(receipt.activationDeadlineAt)) {
|
|
1438
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_EARLY', 'prepared receipt activation deadline has not elapsed', resolved.taskId);
|
|
1439
|
+
}
|
|
1440
|
+
const activeLifecycleEvidence = options.hasActiveLifecycleEvidence?.(receipt)
|
|
1441
|
+
?? (receipt.client === 'codex'
|
|
1442
|
+
&& receipt.lifecycleProvenance
|
|
1443
|
+
&& hasActiveCodexLifecycleEvidence(path.join(resolved.repoRoot, '.agents', 'workspace', '.runtime', 'codex-lifecycle'), {
|
|
1444
|
+
nativeAgent: `agent-infra-lifecycle-${receipt.role}`,
|
|
1445
|
+
hookDefinitionHash: receipt.lifecycleProvenance.hookDefinitionHash
|
|
1446
|
+
}));
|
|
1447
|
+
if (activeLifecycleEvidence) {
|
|
1448
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_ACTIVE_EVIDENCE', 'active Codex lifecycle evidence blocks prepared receipt recovery', resolved.taskId);
|
|
1449
|
+
}
|
|
1450
|
+
if (fs.existsSync(commitIntentPath(resolved.taskDir))) {
|
|
1451
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_UNSAFE', 'active commit intent blocks prepared recovery', resolved.taskId);
|
|
1452
|
+
}
|
|
1453
|
+
const current = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
|
|
1454
|
+
gitRoot: gitRootFor(resolved.repoRoot, options),
|
|
1455
|
+
stateRoot: resolved.repoRoot,
|
|
1456
|
+
taskId: receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null
|
|
1457
|
+
});
|
|
1458
|
+
if (current !== receipt.beforeFingerprint) {
|
|
1459
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_DRIFT', 'workspace changed after delegation preparation', resolved.taskId);
|
|
1460
|
+
}
|
|
1461
|
+
const aborted = abortPreparedDelegation(receipt);
|
|
1462
|
+
if (!aborted.ok)
|
|
1463
|
+
return failed(aborted.code, aborted.message, resolved.taskId);
|
|
1464
|
+
const updated = withUpdatedRun(run, {
|
|
1465
|
+
status: 'running',
|
|
1466
|
+
pause: null,
|
|
1467
|
+
nextStage: null,
|
|
1468
|
+
pendingDelegation: null,
|
|
1469
|
+
receipts: Object.freeze([...run.receipts, aborted.receipt])
|
|
1470
|
+
}, options.now);
|
|
1471
|
+
saveRun(resolved.taskDir, updated);
|
|
1472
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
catch (error) {
|
|
1476
|
+
if (error instanceof TaskExecutionLockError)
|
|
1477
|
+
return failed(error.code, error.message, resolved.taskId);
|
|
1478
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1139
1481
|
function completeOrchestrationStage(taskRef, event, options = {}) {
|
|
1140
1482
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1141
1483
|
if (!resolved.ok)
|
|
@@ -1143,6 +1485,8 @@ function completeOrchestrationStage(taskRef, event, options = {}) {
|
|
|
1143
1485
|
const run = readRun(resolved.taskDir);
|
|
1144
1486
|
if (!run)
|
|
1145
1487
|
return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
|
|
1488
|
+
if (run.schemaVersion !== 3)
|
|
1489
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'stage completion requires schemaVersion 3', resolved.taskId);
|
|
1146
1490
|
if (!run.pendingDelegation)
|
|
1147
1491
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending delegation', resolved.taskId);
|
|
1148
1492
|
const result = completeDelegationStage(run.pendingDelegation, event);
|
|
@@ -1165,6 +1509,8 @@ function inspectOrchestrationStage(taskRef, event, options = {}) {
|
|
|
1165
1509
|
}
|
|
1166
1510
|
if (!run)
|
|
1167
1511
|
return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
|
|
1512
|
+
if (run.schemaVersion !== 3)
|
|
1513
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'stage inspection requires schemaVersion 3', resolved.taskId);
|
|
1168
1514
|
const receipt = run.pendingDelegation;
|
|
1169
1515
|
if (!receipt
|
|
1170
1516
|
|| receipt.status !== 'activated'
|
|
@@ -1209,6 +1555,8 @@ function sealOrchestrationDelegation(taskRef, event, options = {}) {
|
|
|
1209
1555
|
const run = readRun(resolved.taskDir);
|
|
1210
1556
|
if (!run?.pendingDelegation)
|
|
1211
1557
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1558
|
+
if (run.schemaVersion !== 3)
|
|
1559
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'delegation sealing requires schemaVersion 3', resolved.taskId);
|
|
1212
1560
|
const result = sealDelegation(run.pendingDelegation, event, { now: options.now });
|
|
1213
1561
|
if (!result.ok)
|
|
1214
1562
|
return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
@@ -1223,6 +1571,8 @@ function advanceOrchestration(taskRef, options = {}) {
|
|
|
1223
1571
|
const run = readRun(resolved.taskDir);
|
|
1224
1572
|
if (!run?.pendingDelegation)
|
|
1225
1573
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1574
|
+
if (run.schemaVersion !== 3)
|
|
1575
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'orchestration advance requires schemaVersion 3', resolved.taskId);
|
|
1226
1576
|
const result = consumeDelegation(run.pendingDelegation, { now: options.now });
|
|
1227
1577
|
if (!result.ok)
|
|
1228
1578
|
return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
@@ -1251,5 +1601,5 @@ function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
|
|
|
1251
1601
|
saveRun(resolved.taskDir, updated);
|
|
1252
1602
|
return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1253
1603
|
}
|
|
1254
|
-
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, abortCommitIntent, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, commitOrchestrationStageCompletion, completeCommitIntent, completeOrchestrationStage, inspectOrchestrationStage, orchestrationPath, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, recoverCommitIntent, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, startCommitAttempt, statusCommitIntent, statusOrchestration, terminateCommitAttempt };
|
|
1604
|
+
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, awaitOrchestrationDelegationActivation, advanceOrchestration, abortCommitIntent, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, commitOrchestrationStageCompletion, completeCommitIntent, completeOrchestrationStage, dispatchOrchestrationDelegation, inspectOrchestrationStage, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, orchestrationPath, pauseMatchingOrchestrationDelegation, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, reconcileMatchingOrchestrationDelegation, recoverCommitIntent, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealMatchingOrchestrationDelegationWithHostEvidence, sealOrchestrationDelegation, startCommitAttempt, statusCommitIntent, statusOrchestration, terminateCommitAttempt };
|
|
1255
1605
|
//# sourceMappingURL=orchestration.js.map
|
|
@@ -16,6 +16,10 @@ function parseTable(content, input) {
|
|
|
16
16
|
new Set(input.columns).size !== input.columns.length) {
|
|
17
17
|
throw new DocumentMutationError('MUTATION_INVALID', 'table columns are invalid');
|
|
18
18
|
}
|
|
19
|
+
const keyColumn = input.keyColumn ?? input.columns[0];
|
|
20
|
+
if (!input.columns.includes(keyColumn)) {
|
|
21
|
+
throw new DocumentMutationError('MUTATION_INVALID', 'table key column is invalid');
|
|
22
|
+
}
|
|
19
23
|
const sections = matchingSections(content, input.sectionAliases);
|
|
20
24
|
if (sections.length === 0)
|
|
21
25
|
return null;
|
|
@@ -60,7 +64,7 @@ function parseTable(content, input) {
|
|
|
60
64
|
column,
|
|
61
65
|
decodeCell(parsed.cells[columnIndex]).trim()
|
|
62
66
|
]));
|
|
63
|
-
const key = values[
|
|
67
|
+
const key = values[keyColumn];
|
|
64
68
|
if (seen.has(key)) {
|
|
65
69
|
throw new DocumentMutationError('TABLE_DUPLICATE_KEY', `duplicate table key '${key}'`);
|
|
66
70
|
}
|
|
@@ -38,11 +38,12 @@ function parseOwner(raw, key) {
|
|
|
38
38
|
try {
|
|
39
39
|
const value = JSON.parse(raw);
|
|
40
40
|
if (value !== null
|
|
41
|
-
&& value.version ===
|
|
41
|
+
&& value.version === 2
|
|
42
42
|
&& Number.isSafeInteger(value.pid)
|
|
43
43
|
&& (value.pid ?? 0) > 0
|
|
44
|
-
&& typeof value.startTime === '
|
|
45
|
-
&& value.startTime
|
|
44
|
+
&& typeof value.startTime === 'number'
|
|
45
|
+
&& Number.isSafeInteger(value.startTime)
|
|
46
|
+
&& value.startTime >= 0
|
|
46
47
|
&& typeof value.token === 'string'
|
|
47
48
|
&& value.token.length > 0
|
|
48
49
|
&& typeof value.owner === 'string'
|
|
@@ -148,7 +149,7 @@ function withTaskExecutionLock(repoRoot, taskId, ownerName, callback, options =
|
|
|
148
149
|
throw new TaskExecutionLockError('ORCHESTRATION_LOCK_FAILED', 'current process start time is unavailable', { operation: 'identity', errno: null, key });
|
|
149
150
|
}
|
|
150
151
|
const owner = {
|
|
151
|
-
version:
|
|
152
|
+
version: 2,
|
|
152
153
|
pid: process.pid,
|
|
153
154
|
startTime,
|
|
154
155
|
token,
|