@fitlab-ai/agent-infra 0.9.6 → 0.9.8
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 +10 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +47 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +162 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +245 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +398 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +23 -6
- package/dist/lib/decide.js +68 -14
- package/dist/lib/defaults.json +10 -2
- package/dist/lib/git/workflow.js +30 -3
- package/dist/lib/init.js +1 -1
- package/dist/lib/internal/codex-lifecycle.js +69 -1
- package/dist/lib/internal/codex-sandbox-controller.js +106 -0
- package/dist/lib/internal/release-workflow.js +1 -3
- package/dist/lib/internal/task-activity.js +57 -7
- package/dist/lib/internal/task-event.js +57 -4
- package/dist/lib/internal/task-ledger.js +59 -5
- package/dist/lib/internal/task-lifecycle.js +59 -8
- package/dist/lib/internal/task-orchestration.js +48 -8
- package/dist/lib/internal/task-override.js +131 -0
- package/dist/lib/internal/task-review.js +64 -6
- package/dist/lib/internal/task-verify.js +65 -3
- package/dist/lib/internal/task-warning.js +57 -4
- package/dist/lib/platform/context.js +6 -4
- package/dist/lib/platform/github-client.js +55 -2
- package/dist/lib/platform/pr-summary.js +5 -3
- 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/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +394 -264
- package/dist/lib/sandbox/commands/rm.js +64 -15
- package/dist/lib/sandbox/commands/start.js +4 -3
- package/dist/lib/sandbox/control/container-identity.js +64 -0
- package/dist/lib/sandbox/control/executor.js +23 -9
- package/dist/lib/sandbox/control/lifecycle.js +590 -61
- package/dist/lib/sandbox/control/protocol.js +7 -0
- package/dist/lib/sandbox/control/server.js +202 -26
- package/dist/lib/sandbox/control/state.js +51 -25
- package/dist/lib/sandbox/control/timing.js +8 -0
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/mounts.js +7 -0
- package/dist/lib/sandbox/recovery.js +135 -50
- package/dist/lib/sandbox/workspace-view.js +59 -20
- package/dist/lib/server/daemon.js +1 -1
- package/dist/lib/server/process-control.js +11 -1
- package/dist/lib/server/process-state.js +107 -20
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/artifact-lifecycle.js +82 -11
- package/dist/lib/task/artifact-receipts.js +128 -0
- package/dist/lib/task/codex-orchestration.js +81 -6
- package/dist/lib/task/commands/validate.js +1 -1
- package/dist/lib/task/create.js +10 -2
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +120 -17
- package/dist/lib/task/events.js +148 -7
- package/dist/lib/task/guard-override.js +31 -0
- package/dist/lib/task/human-override.js +1240 -0
- package/dist/lib/task/ledger-intents.js +6 -3
- package/dist/lib/task/ledger.js +13 -1
- package/dist/lib/task/lifecycle.js +69 -14
- package/dist/lib/task/orchestration.js +293 -18
- package/dist/lib/task/review-finalization.js +3 -2
- 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 +80 -22
- package/dist/lib/task/verification.js +13 -1
- package/dist/lib/task/workflow-warning-intents.js +4 -2
- package/dist/lib/task/write.js +2 -1
- package/dist/lifecycle-build-manifest.json +6 -0
- package/lib/agent-clients/adapters/claude-code.ts +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +62 -4
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +227 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +322 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +26 -0
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +480 -0
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +35 -5
- package/lib/decide.ts +59 -14
- package/lib/defaults.json +10 -2
- package/lib/git/workflow.ts +25 -2
- package/lib/init.ts +1 -1
- package/lib/internal/codex-lifecycle.ts +68 -1
- package/lib/internal/codex-sandbox-controller.ts +109 -0
- package/lib/internal/release-workflow.ts +1 -3
- package/lib/internal/task-activity.ts +46 -6
- package/lib/internal/task-event.ts +53 -4
- package/lib/internal/task-ledger.ts +48 -4
- package/lib/internal/task-lifecycle.ts +56 -12
- package/lib/internal/task-orchestration.ts +43 -8
- package/lib/internal/task-override.ts +138 -0
- package/lib/internal/task-review.ts +51 -6
- package/lib/internal/task-verify.ts +52 -3
- package/lib/internal/task-warning.ts +44 -3
- package/lib/platform/context.ts +8 -5
- package/lib/platform/github-client.ts +61 -3
- package/lib/platform/pr-summary.ts +10 -3
- 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/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +139 -6
- package/lib/sandbox/commands/rm.ts +64 -14
- package/lib/sandbox/commands/start.ts +4 -3
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/container-identity.ts +93 -0
- package/lib/sandbox/control/executor.ts +24 -12
- package/lib/sandbox/control/lifecycle.ts +685 -67
- package/lib/sandbox/control/protocol.ts +35 -6
- package/lib/sandbox/control/server.ts +184 -24
- package/lib/sandbox/control/state.ts +67 -33
- package/lib/sandbox/control/timing.ts +12 -0
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/mounts.ts +8 -0
- package/lib/sandbox/recovery.ts +159 -49
- package/lib/sandbox/workspace-view.ts +73 -22
- package/lib/server/daemon.ts +1 -1
- package/lib/server/process-control.ts +13 -3
- package/lib/server/process-state.ts +129 -27
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/artifact-lifecycle.ts +73 -10
- package/lib/task/artifact-receipts.ts +161 -0
- package/lib/task/codex-orchestration.ts +91 -5
- package/lib/task/commands/validate.ts +1 -1
- package/lib/task/create.ts +11 -2
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +205 -25
- package/lib/task/events.ts +132 -6
- package/lib/task/guard-override.ts +50 -0
- package/lib/task/human-override.ts +1503 -0
- package/lib/task/ledger-intents.ts +6 -3
- package/lib/task/ledger.ts +16 -1
- package/lib/task/lifecycle.ts +71 -14
- package/lib/task/orchestration.ts +443 -22
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/sections.ts +6 -2
- package/lib/task/task-execution-lock.ts +11 -9
- package/lib/task/verification-engine.ts +93 -32
- package/lib/task/verification.ts +15 -1
- package/lib/task/workflow-warning-intents.ts +4 -2
- package/lib/task/write.ts +4 -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 +33 -4
- package/templates/.agents/rules/lifecycle-orchestration.en.md +10 -3
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +10 -3
- 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/run-manual-validation/SKILL.en.md +6 -6
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +14 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +14 -3
- 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 +25 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +25 -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 -1
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +5 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +75 -3
- package/templates/.agents/workspace/README.en.md +0 -26
- package/templates/.agents/workspace/README.zh-CN.md +0 -26
|
@@ -10,14 +10,23 @@ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.
|
|
|
10
10
|
import { resolveTaskRef } from './resolve-ref.ts';
|
|
11
11
|
import {
|
|
12
12
|
activateDelegation,
|
|
13
|
+
abortPreparedDelegation,
|
|
13
14
|
completeDelegationStage,
|
|
14
15
|
consumeDelegation,
|
|
16
|
+
dispatchDelegation,
|
|
17
|
+
foldBlankToNull,
|
|
15
18
|
managedDelegationRole,
|
|
16
19
|
prepareDelegation,
|
|
17
20
|
sealDelegation
|
|
18
21
|
} from './delegation-receipts.ts';
|
|
19
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
DelegationLifecycleProvenance,
|
|
24
|
+
DelegationReceipt,
|
|
25
|
+
DelegationRole,
|
|
26
|
+
DelegationStage
|
|
27
|
+
} from './delegation-receipts.ts';
|
|
20
28
|
import { normalizeAgentClients } from '../agent-clients/config.ts';
|
|
29
|
+
import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.ts';
|
|
21
30
|
import { inspectPlatformPullRequest } from '../platform/pull-requests.ts';
|
|
22
31
|
import {
|
|
23
32
|
getAgentClientCapability,
|
|
@@ -39,6 +48,7 @@ import type { RepositorySnapshot } from './workspace-snapshot.ts';
|
|
|
39
48
|
import type { WorkspaceSnapshotContext } from './workspace-snapshot.ts';
|
|
40
49
|
import { assertGitRepositoryBinding } from '../git/worktree-identity.ts';
|
|
41
50
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
|
|
51
|
+
import { hasActiveCodexLifecycleEvidence } from '../agent-clients/adapters/codex-lifecycle/store.ts';
|
|
42
52
|
import {
|
|
43
53
|
CommitIntentError,
|
|
44
54
|
commitIntentPath,
|
|
@@ -104,7 +114,44 @@ type ClientCapabilityRecovery = Readonly<{
|
|
|
104
114
|
}>;
|
|
105
115
|
resultingStatus: 'running';
|
|
106
116
|
}>;
|
|
107
|
-
type
|
|
117
|
+
type ClaudeCodeCapabilityRecovery = Readonly<{
|
|
118
|
+
code: 'CLIENT_CAPABILITY_ENABLED_NO_MIGRATION';
|
|
119
|
+
recoveredAt: string;
|
|
120
|
+
previousSchemaVersion: 3;
|
|
121
|
+
previousStatus: 'paused';
|
|
122
|
+
previousPause: Readonly<{ code: 'ORCHESTRATION_CLIENT_UNSUPPORTED'; message: string; recoverable: boolean }>;
|
|
123
|
+
client: 'claude-code';
|
|
124
|
+
guards: Readonly<{
|
|
125
|
+
stepCount: 0;
|
|
126
|
+
nextStage: null;
|
|
127
|
+
baselineEmpty: true;
|
|
128
|
+
receiptCount: 0;
|
|
129
|
+
pendingDelegation: false;
|
|
130
|
+
commitAuthorizationUnused: true;
|
|
131
|
+
completionEvidenceAbsent: true;
|
|
132
|
+
commitIntentAbsent: true;
|
|
133
|
+
}>;
|
|
134
|
+
resultingStatus: 'running';
|
|
135
|
+
}>;
|
|
136
|
+
type SchemaMigrationRecovery = Readonly<{
|
|
137
|
+
code: 'SCHEMA_V3_MIGRATED';
|
|
138
|
+
recoveredAt: string;
|
|
139
|
+
previousSchemaVersion: 2;
|
|
140
|
+
previousStatus: 'paused';
|
|
141
|
+
previousPause: Readonly<{ code: string; message: string; recoverable: boolean }>;
|
|
142
|
+
guards: Readonly<{
|
|
143
|
+
stepCount: 0;
|
|
144
|
+
nextStage: null;
|
|
145
|
+
baselineEmpty: true;
|
|
146
|
+
receiptCount: 0;
|
|
147
|
+
pendingDelegation: false;
|
|
148
|
+
commitAuthorizationUnused: true;
|
|
149
|
+
completionEvidenceAbsent: true;
|
|
150
|
+
commitIntentAbsent: true;
|
|
151
|
+
}>;
|
|
152
|
+
resultingStatus: 'running';
|
|
153
|
+
}>;
|
|
154
|
+
type OrchestrationRecovery = ModelPolicyRecovery | ClientCapabilityRecovery | ClaudeCodeCapabilityRecovery | SchemaMigrationRecovery;
|
|
108
155
|
type CleanCompletionEvidence = Readonly<{
|
|
109
156
|
kind: 'reviewed-head-clean';
|
|
110
157
|
observedAt: string;
|
|
@@ -116,7 +163,7 @@ type CleanCompletionEvidence = Readonly<{
|
|
|
116
163
|
prHead: string;
|
|
117
164
|
}>;
|
|
118
165
|
type OrchestrationRun = Readonly<{
|
|
119
|
-
schemaVersion: 1 | 2;
|
|
166
|
+
schemaVersion: 1 | 2 | 3;
|
|
120
167
|
taskId: string;
|
|
121
168
|
runId: string;
|
|
122
169
|
status: OrchestrationStatus;
|
|
@@ -193,6 +240,9 @@ type OrchestrationOptions = {
|
|
|
193
240
|
diffWorkspace?: (repoRoot: string, before: string, after: string) => string[];
|
|
194
241
|
supportsLifecycleDelegation?: (client: AgentClientId) => boolean;
|
|
195
242
|
token?: () => string;
|
|
243
|
+
monotonicNow?: () => number;
|
|
244
|
+
sleep?: (milliseconds: number) => Promise<void>;
|
|
245
|
+
hasActiveLifecycleEvidence?: (receipt: DelegationReceipt) => boolean;
|
|
196
246
|
};
|
|
197
247
|
|
|
198
248
|
type CommitIntentResult = Readonly<{
|
|
@@ -238,7 +288,7 @@ function readRun(taskDir: string): OrchestrationRun | null {
|
|
|
238
288
|
const file = orchestrationPath(taskDir);
|
|
239
289
|
if (!fs.existsSync(file)) return null;
|
|
240
290
|
const run = JSON.parse(fs.readFileSync(file, 'utf8')) as OrchestrationRun;
|
|
241
|
-
if (run.schemaVersion
|
|
291
|
+
if (![2, 3].includes(run.schemaVersion) || !isV2Policy(run.modelPolicy)) return run;
|
|
242
292
|
return {
|
|
243
293
|
...run,
|
|
244
294
|
modelPolicy: {
|
|
@@ -329,7 +379,7 @@ function commitFinalizationView(inspection: CommitFinalizationInspection): NonNu
|
|
|
329
379
|
|
|
330
380
|
function validCommitRun(run: OrchestrationRun | null, taskId: string): run is OrchestrationRun {
|
|
331
381
|
return run !== null
|
|
332
|
-
&& [1, 2].includes(run.schemaVersion)
|
|
382
|
+
&& [1, 2, 3].includes(run.schemaVersion)
|
|
333
383
|
&& run.taskId === taskId
|
|
334
384
|
&& ['running', 'paused', 'completed'].includes(run.status)
|
|
335
385
|
&& Array.isArray(run.receipts)
|
|
@@ -406,6 +456,38 @@ function canRecoverCodexUnsupportedPause(run: OrchestrationRun, taskDir: string)
|
|
|
406
456
|
&& (run.recoveryHistory ?? []).every((entry) => entry.code === 'MODEL_POLICY_SUPPLEMENTED');
|
|
407
457
|
}
|
|
408
458
|
|
|
459
|
+
function canRecoverClaudeCodeUnsupportedPause(run: OrchestrationRun, taskDir: string): boolean {
|
|
460
|
+
return run.schemaVersion === 3
|
|
461
|
+
&& run.status === 'paused'
|
|
462
|
+
&& run.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
463
|
+
&& run.modelPolicySource?.client === 'claude-code'
|
|
464
|
+
&& run.stepCount === 0
|
|
465
|
+
&& run.nextStage === null
|
|
466
|
+
&& run.baseline === ''
|
|
467
|
+
&& run.pendingDelegation === null
|
|
468
|
+
&& run.receipts.length === 0
|
|
469
|
+
&& run.commitAuthorization?.issuedAt === null
|
|
470
|
+
&& run.commitAuthorization?.consumedAt === null
|
|
471
|
+
&& run.completionEvidence == null
|
|
472
|
+
&& !fs.existsSync(commitIntentPath(taskDir))
|
|
473
|
+
&& (run.recoveryHistory ?? []).length === 0;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function canMigrateRecoverableV2Pause(run: OrchestrationRun, taskDir: string): boolean {
|
|
477
|
+
return run.schemaVersion === 2
|
|
478
|
+
&& run.status === 'paused'
|
|
479
|
+
&& run.pause?.recoverable === true
|
|
480
|
+
&& run.stepCount === 0
|
|
481
|
+
&& run.nextStage === null
|
|
482
|
+
&& run.baseline === ''
|
|
483
|
+
&& run.pendingDelegation === null
|
|
484
|
+
&& run.receipts.length === 0
|
|
485
|
+
&& run.commitAuthorization?.issuedAt === null
|
|
486
|
+
&& run.commitAuthorization?.consumedAt === null
|
|
487
|
+
&& run.completionEvidence == null
|
|
488
|
+
&& !fs.existsSync(commitIntentPath(taskDir));
|
|
489
|
+
}
|
|
490
|
+
|
|
409
491
|
function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
|
|
410
492
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
411
493
|
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
@@ -420,7 +502,7 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
|
|
|
420
502
|
}
|
|
421
503
|
if (existing) {
|
|
422
504
|
if (
|
|
423
|
-
![1, 2].includes(existing.schemaVersion)
|
|
505
|
+
![1, 2, 3].includes(existing.schemaVersion)
|
|
424
506
|
|| !Array.isArray(existing.receipts)
|
|
425
507
|
|| !Object.prototype.hasOwnProperty.call(existing, 'pendingDelegation')
|
|
426
508
|
) {
|
|
@@ -429,7 +511,7 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
|
|
|
429
511
|
if (existing.status === 'completed' && existing.schemaVersion === 1) {
|
|
430
512
|
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
431
513
|
}
|
|
432
|
-
if (existing.schemaVersion === 2) {
|
|
514
|
+
if (existing.schemaVersion === 2 || existing.schemaVersion === 3) {
|
|
433
515
|
const policyError = validateModelPolicy(isV2Policy(existing.modelPolicy) ? existing.modelPolicy : undefined);
|
|
434
516
|
if (
|
|
435
517
|
policyError
|
|
@@ -437,11 +519,18 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
|
|
|
437
519
|
|| !isAgentClientId(existing.modelPolicySource.client)
|
|
438
520
|
|| !Array.isArray(existing.recoveryHistory)
|
|
439
521
|
) {
|
|
440
|
-
return failed('ORCHESTRATION_STATE_INVALID',
|
|
522
|
+
return failed('ORCHESTRATION_STATE_INVALID', `schemaVersion ${existing.schemaVersion} orchestration state is incomplete`, resolved.taskId);
|
|
441
523
|
}
|
|
442
524
|
if (existing.status === 'completed') {
|
|
443
525
|
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
444
526
|
}
|
|
527
|
+
if (existing.schemaVersion === 2 && existing.status === 'running') {
|
|
528
|
+
return failed(
|
|
529
|
+
'ORCHESTRATION_STATE_INVALID',
|
|
530
|
+
'active schemaVersion 2 runs cannot be advanced by lifecycle protocol v3',
|
|
531
|
+
resolved.taskId
|
|
532
|
+
);
|
|
533
|
+
}
|
|
445
534
|
if (existing.modelPolicySource.client !== options.client) {
|
|
446
535
|
return failed('ORCHESTRATION_CLIENT_MISMATCH', 'provided client does not match the persisted run policy source', resolved.taskId);
|
|
447
536
|
}
|
|
@@ -453,6 +542,39 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
|
|
|
453
542
|
}
|
|
454
543
|
}
|
|
455
544
|
if (existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED') {
|
|
545
|
+
if (existing.modelPolicySource.client === 'claude-code') {
|
|
546
|
+
if (!canRecoverClaudeCodeUnsupportedPause(existing, resolved.taskDir)) {
|
|
547
|
+
return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
548
|
+
}
|
|
549
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
550
|
+
const previousPause = existing.pause as ClaudeCodeCapabilityRecovery['previousPause'];
|
|
551
|
+
const recovery: ClaudeCodeCapabilityRecovery = {
|
|
552
|
+
code: 'CLIENT_CAPABILITY_ENABLED_NO_MIGRATION',
|
|
553
|
+
recoveredAt: now,
|
|
554
|
+
previousSchemaVersion: 3,
|
|
555
|
+
previousStatus: 'paused',
|
|
556
|
+
previousPause,
|
|
557
|
+
client: 'claude-code',
|
|
558
|
+
guards: {
|
|
559
|
+
stepCount: 0,
|
|
560
|
+
nextStage: null,
|
|
561
|
+
baselineEmpty: true,
|
|
562
|
+
receiptCount: 0,
|
|
563
|
+
pendingDelegation: false,
|
|
564
|
+
commitAuthorizationUnused: true,
|
|
565
|
+
completionEvidenceAbsent: true,
|
|
566
|
+
commitIntentAbsent: true
|
|
567
|
+
},
|
|
568
|
+
resultingStatus: 'running'
|
|
569
|
+
};
|
|
570
|
+
const resumed = withUpdatedRun(existing, {
|
|
571
|
+
status: 'running',
|
|
572
|
+
pause: null,
|
|
573
|
+
recoveryHistory: Object.freeze([...(existing.recoveryHistory ?? []), recovery])
|
|
574
|
+
}, () => now);
|
|
575
|
+
saveRun(resolved.taskDir, resumed);
|
|
576
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
577
|
+
}
|
|
456
578
|
if (!canRecoverCodexUnsupportedPause(existing, resolved.taskDir)) {
|
|
457
579
|
return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
458
580
|
}
|
|
@@ -478,6 +600,7 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
|
|
|
478
600
|
resultingStatus: 'running'
|
|
479
601
|
};
|
|
480
602
|
const resumed = withUpdatedRun(existing, {
|
|
603
|
+
schemaVersion: 3,
|
|
481
604
|
status: 'running',
|
|
482
605
|
pause: null,
|
|
483
606
|
recoveryHistory: Object.freeze([...(existing.recoveryHistory ?? []), recovery])
|
|
@@ -486,7 +609,42 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
|
|
|
486
609
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
487
610
|
}
|
|
488
611
|
if (existing.status === 'paused' && existing.pause?.recoverable && existing.pendingDelegation === null) {
|
|
489
|
-
|
|
612
|
+
if (existing.schemaVersion === 2 && !canMigrateRecoverableV2Pause(existing, resolved.taskDir)) {
|
|
613
|
+
const paused = withUpdatedRun(existing, {
|
|
614
|
+
pause: {
|
|
615
|
+
code: 'ORCHESTRATION_SCHEMA_MIGRATION_REQUIRED',
|
|
616
|
+
message: 'schemaVersion 2 recoverable pause contains execution evidence and cannot migrate automatically',
|
|
617
|
+
recoverable: false
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
saveRun(resolved.taskDir, paused);
|
|
621
|
+
return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
|
|
622
|
+
}
|
|
623
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
624
|
+
const recovery: SchemaMigrationRecovery | null = existing.schemaVersion === 2 ? {
|
|
625
|
+
code: 'SCHEMA_V3_MIGRATED',
|
|
626
|
+
recoveredAt: now,
|
|
627
|
+
previousSchemaVersion: 2,
|
|
628
|
+
previousStatus: 'paused',
|
|
629
|
+
previousPause: existing.pause,
|
|
630
|
+
guards: {
|
|
631
|
+
stepCount: 0,
|
|
632
|
+
nextStage: null,
|
|
633
|
+
baselineEmpty: true,
|
|
634
|
+
receiptCount: 0,
|
|
635
|
+
pendingDelegation: false,
|
|
636
|
+
commitAuthorizationUnused: true,
|
|
637
|
+
completionEvidenceAbsent: true,
|
|
638
|
+
commitIntentAbsent: true
|
|
639
|
+
},
|
|
640
|
+
resultingStatus: 'running'
|
|
641
|
+
} : null;
|
|
642
|
+
const resumed = withUpdatedRun(existing, {
|
|
643
|
+
schemaVersion: 3,
|
|
644
|
+
status: 'running',
|
|
645
|
+
pause: null,
|
|
646
|
+
...(recovery ? { recoveryHistory: Object.freeze([...(existing.recoveryHistory ?? []), recovery]) } : {})
|
|
647
|
+
}, () => now);
|
|
490
648
|
saveRun(resolved.taskDir, resumed);
|
|
491
649
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
492
650
|
}
|
|
@@ -562,7 +720,7 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
|
|
|
562
720
|
};
|
|
563
721
|
const recovered: OrchestrationRun = Object.freeze({
|
|
564
722
|
...existing,
|
|
565
|
-
schemaVersion:
|
|
723
|
+
schemaVersion: 3,
|
|
566
724
|
status: resultingStatus,
|
|
567
725
|
modelPolicy: policy!,
|
|
568
726
|
modelPolicySource: source,
|
|
@@ -593,7 +751,7 @@ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptio
|
|
|
593
751
|
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
594
752
|
const source: ModelPolicySource = { kind: sourceKind, client: options.client, resolvedAt: now };
|
|
595
753
|
const run: OrchestrationRun = {
|
|
596
|
-
schemaVersion:
|
|
754
|
+
schemaVersion: 3,
|
|
597
755
|
taskId: resolved.taskId,
|
|
598
756
|
runId: (options.id ?? randomUUID)(),
|
|
599
757
|
status: 'running',
|
|
@@ -1332,7 +1490,12 @@ function statusOrchestration(taskRef: string, options: OrchestrationOptions = {}
|
|
|
1332
1490
|
|
|
1333
1491
|
function prepareOrchestrationDelegationUnlocked(
|
|
1334
1492
|
taskRef: string,
|
|
1335
|
-
input: Readonly<{
|
|
1493
|
+
input: Readonly<{
|
|
1494
|
+
client: AgentClientId;
|
|
1495
|
+
requestedModel?: string;
|
|
1496
|
+
requestedReasoningEffort?: string;
|
|
1497
|
+
lifecycleProvenance?: DelegationLifecycleProvenance;
|
|
1498
|
+
}>,
|
|
1336
1499
|
options: OrchestrationOptions = {}
|
|
1337
1500
|
): OrchestrationResult {
|
|
1338
1501
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
@@ -1354,6 +1517,9 @@ function prepareOrchestrationDelegationUnlocked(
|
|
|
1354
1517
|
}
|
|
1355
1518
|
const run = readRun(resolved.taskDir);
|
|
1356
1519
|
if (!run || run.status !== 'running') return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
|
|
1520
|
+
if (run.schemaVersion !== 3) {
|
|
1521
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'delegation preparation requires orchestration schemaVersion 3', resolved.taskId);
|
|
1522
|
+
}
|
|
1357
1523
|
if (!isV2Policy(run.modelPolicy)) return failed('ORCHESTRATION_MODEL_EVIDENCE_MISSING', 'running orchestration has no persisted model policy', resolved.taskId);
|
|
1358
1524
|
if (run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', resolved.taskId);
|
|
1359
1525
|
const repositoryPending = matchingDelegations(() => true, options);
|
|
@@ -1370,6 +1536,9 @@ function prepareOrchestrationDelegationUnlocked(
|
|
|
1370
1536
|
if (!validModel(input.requestedReasoningEffort)) {
|
|
1371
1537
|
return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', resolved.taskId);
|
|
1372
1538
|
}
|
|
1539
|
+
if (input.client === 'codex' && !input.lifecycleProvenance) {
|
|
1540
|
+
return failed('ORCHESTRATION_CODEX_PROVENANCE_REQUIRED', 'Codex prepare requires lifecycle provenance', resolved.taskId);
|
|
1541
|
+
}
|
|
1373
1542
|
const expectedPolicy = run.modelPolicy[next.role];
|
|
1374
1543
|
if (input.requestedModel !== expectedPolicy.model) {
|
|
1375
1544
|
return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
|
|
@@ -1398,8 +1567,9 @@ function prepareOrchestrationDelegationUnlocked(
|
|
|
1398
1567
|
requestedModel: input.requestedModel,
|
|
1399
1568
|
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
1400
1569
|
workspaceSnapshotScope: 'task',
|
|
1570
|
+
lifecycleProvenance: input.lifecycleProvenance ?? null,
|
|
1401
1571
|
beforeFingerprint
|
|
1402
|
-
}, { id: options.id, now: options.now });
|
|
1572
|
+
}, { id: options.id, now: options.now, monotonicNow: options.monotonicNow });
|
|
1403
1573
|
const updated = withUpdatedRun(run, {
|
|
1404
1574
|
nextStage: next.stage,
|
|
1405
1575
|
pendingDelegation: receipt,
|
|
@@ -1413,7 +1583,12 @@ function prepareOrchestrationDelegationUnlocked(
|
|
|
1413
1583
|
|
|
1414
1584
|
function prepareOrchestrationDelegation(
|
|
1415
1585
|
taskRef: string,
|
|
1416
|
-
input: Readonly<{
|
|
1586
|
+
input: Readonly<{
|
|
1587
|
+
client: AgentClientId;
|
|
1588
|
+
requestedModel?: string;
|
|
1589
|
+
requestedReasoningEffort?: string;
|
|
1590
|
+
lifecycleProvenance?: DelegationLifecycleProvenance;
|
|
1591
|
+
}>,
|
|
1417
1592
|
options: OrchestrationOptions = {}
|
|
1418
1593
|
): OrchestrationResult {
|
|
1419
1594
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
@@ -1436,6 +1611,51 @@ function prepareOrchestrationDelegation(
|
|
|
1436
1611
|
}
|
|
1437
1612
|
}
|
|
1438
1613
|
|
|
1614
|
+
function dispatchOrchestrationDelegationUnlocked(
|
|
1615
|
+
taskRef: string,
|
|
1616
|
+
options: OrchestrationOptions = {}
|
|
1617
|
+
): OrchestrationResult {
|
|
1618
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1619
|
+
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1620
|
+
const run = readRun(resolved.taskDir);
|
|
1621
|
+
if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1622
|
+
if (run.schemaVersion !== 3 || run.status !== 'running') {
|
|
1623
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'spawn dispatch requires a running schemaVersion 3 orchestration', resolved.taskId);
|
|
1624
|
+
}
|
|
1625
|
+
const result = dispatchDelegation(run.pendingDelegation, {
|
|
1626
|
+
now: options.now,
|
|
1627
|
+
monotonicNow: options.monotonicNow
|
|
1628
|
+
});
|
|
1629
|
+
if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
1630
|
+
const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
|
|
1631
|
+
saveRun(resolved.taskDir, updated);
|
|
1632
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
function dispatchOrchestrationDelegation(
|
|
1636
|
+
taskRef: string,
|
|
1637
|
+
options: OrchestrationOptions = {}
|
|
1638
|
+
): OrchestrationResult {
|
|
1639
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1640
|
+
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1641
|
+
try {
|
|
1642
|
+
return withTaskExecutionLock(
|
|
1643
|
+
resolved.repoRoot,
|
|
1644
|
+
'__repository__',
|
|
1645
|
+
'task-orchestration.dispatch.repository',
|
|
1646
|
+
() => withTaskExecutionLock(
|
|
1647
|
+
resolved.repoRoot,
|
|
1648
|
+
resolved.taskId,
|
|
1649
|
+
'task-orchestration.dispatch.task',
|
|
1650
|
+
() => dispatchOrchestrationDelegationUnlocked(taskRef, options)
|
|
1651
|
+
)
|
|
1652
|
+
);
|
|
1653
|
+
} catch (error) {
|
|
1654
|
+
if (error instanceof TaskExecutionLockError) return failed(error.code, error.message, resolved.taskId);
|
|
1655
|
+
throw error;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1439
1659
|
function matchingDelegations(
|
|
1440
1660
|
predicate: (receipt: DelegationReceipt) => boolean,
|
|
1441
1661
|
options: OrchestrationOptions
|
|
@@ -1447,7 +1667,7 @@ function matchingDelegations(
|
|
|
1447
1667
|
.filter((entry) => entry.isDirectory())
|
|
1448
1668
|
.flatMap((entry) => {
|
|
1449
1669
|
const run = readRun(path.join(activeRoot, entry.name));
|
|
1450
|
-
if (run?.status !== 'running' || !run.pendingDelegation) return [];
|
|
1670
|
+
if (run?.schemaVersion !== 3 || run.status !== 'running' || !run.pendingDelegation) return [];
|
|
1451
1671
|
const receipt = run.pendingDelegation;
|
|
1452
1672
|
return predicate(receipt)
|
|
1453
1673
|
? [{ taskId: entry.name, run }]
|
|
@@ -1469,6 +1689,33 @@ function uniqueMatchingDelegation(
|
|
|
1469
1689
|
return matches[0]!;
|
|
1470
1690
|
}
|
|
1471
1691
|
|
|
1692
|
+
function hasActivatableOrchestrationDelegation(
|
|
1693
|
+
client: AgentClientId,
|
|
1694
|
+
childId: string,
|
|
1695
|
+
options: OrchestrationOptions = {}
|
|
1696
|
+
): boolean {
|
|
1697
|
+
return matchingDelegations(
|
|
1698
|
+
(receipt) => receipt.client === client && (
|
|
1699
|
+
receipt.status === 'prepared'
|
|
1700
|
+
|| (receipt.childId === childId && ['activated', 'stage-completed'].includes(receipt.status))
|
|
1701
|
+
),
|
|
1702
|
+
options
|
|
1703
|
+
).length > 0;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
function hasSealableOrchestrationDelegation(
|
|
1707
|
+
client: AgentClientId,
|
|
1708
|
+
childId: string,
|
|
1709
|
+
options: OrchestrationOptions = {}
|
|
1710
|
+
): boolean {
|
|
1711
|
+
return matchingDelegations(
|
|
1712
|
+
(receipt) => receipt.client === client
|
|
1713
|
+
&& receipt.childId === childId
|
|
1714
|
+
&& ['stage-completed', 'sealed'].includes(receipt.status),
|
|
1715
|
+
options
|
|
1716
|
+
).length > 0;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1472
1719
|
function activateMatchingOrchestrationDelegation(
|
|
1473
1720
|
client: AgentClientId,
|
|
1474
1721
|
event: Parameters<typeof activateDelegation>[1],
|
|
@@ -1491,9 +1738,9 @@ function activateMatchingOrchestrationDelegation(
|
|
|
1491
1738
|
if ('status' in replay) return matched;
|
|
1492
1739
|
const receipt = replay.run.pendingDelegation!;
|
|
1493
1740
|
const sameEvidence = receipt.parentId === event.parentId
|
|
1494
|
-
&& receipt.spawnMode === event.spawnMode
|
|
1495
|
-
&& receipt.actualModel === event.actualModel
|
|
1496
|
-
&& receipt.actualReasoningEffort === event.actualReasoningEffort
|
|
1741
|
+
&& (receipt.spawnMode ?? null) === (event.spawnMode ?? null)
|
|
1742
|
+
&& (receipt.actualModel ?? null) === foldBlankToNull(event.actualModel)
|
|
1743
|
+
&& (receipt.actualReasoningEffort ?? null) === foldBlankToNull(event.actualReasoningEffort)
|
|
1497
1744
|
&& receipt.modelFallbackReason === (event.modelFallbackReason ?? null)
|
|
1498
1745
|
&& receipt.reasoningEffortFallbackReason === (event.reasoningEffortFallbackReason ?? null)
|
|
1499
1746
|
&& receipt.hostEvidence?.hookDefinitionHash === event.hostEvidence?.hookDefinitionHash;
|
|
@@ -1521,7 +1768,14 @@ function pauseMatchingOrchestrationDelegation(
|
|
|
1521
1768
|
|
|
1522
1769
|
function sealMatchingOrchestrationDelegation(
|
|
1523
1770
|
client: AgentClientId,
|
|
1524
|
-
event: Readonly<{
|
|
1771
|
+
event: Readonly<{
|
|
1772
|
+
nativeAgent: string;
|
|
1773
|
+
childId: string;
|
|
1774
|
+
actualModel?: string;
|
|
1775
|
+
actualReasoningEffort?: string;
|
|
1776
|
+
modelFallbackReason?: string;
|
|
1777
|
+
reasoningEffortFallbackReason?: string;
|
|
1778
|
+
}>,
|
|
1525
1779
|
options: OrchestrationOptions = {}
|
|
1526
1780
|
): OrchestrationResult {
|
|
1527
1781
|
const role = managedDelegationRole(event.nativeAgent);
|
|
@@ -1550,7 +1804,11 @@ function sealMatchingOrchestrationDelegation(
|
|
|
1550
1804
|
childId: event.childId,
|
|
1551
1805
|
exitCode: 0,
|
|
1552
1806
|
afterFingerprint,
|
|
1553
|
-
changedPaths
|
|
1807
|
+
changedPaths,
|
|
1808
|
+
actualModel: event.actualModel,
|
|
1809
|
+
actualReasoningEffort: event.actualReasoningEffort,
|
|
1810
|
+
modelFallbackReason: event.modelFallbackReason,
|
|
1811
|
+
reasoningEffortFallbackReason: event.reasoningEffortFallbackReason
|
|
1554
1812
|
}, options);
|
|
1555
1813
|
} catch (error) {
|
|
1556
1814
|
return pauseOrchestration(
|
|
@@ -1602,7 +1860,7 @@ function sealMatchingOrchestrationDelegationWithHostEvidence(
|
|
|
1602
1860
|
});
|
|
1603
1861
|
const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(gitRoot, receipt.beforeFingerprint, afterFingerprint);
|
|
1604
1862
|
const baseEvent = { childId: event.childId, exitCode: 0, afterFingerprint, changedPaths };
|
|
1605
|
-
const validated = sealDelegation(receipt, baseEvent, { now: options.now });
|
|
1863
|
+
const validated = sealDelegation(receipt, baseEvent, { now: options.now, requireHostEvidence: false });
|
|
1606
1864
|
if (!validated.ok) {
|
|
1607
1865
|
return pauseOrchestration(matched.taskId, validated.code, validated.message, true, options);
|
|
1608
1866
|
}
|
|
@@ -1662,7 +1920,14 @@ function activateOrchestrationDelegation(
|
|
|
1662
1920
|
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1663
1921
|
const run = readRun(resolved.taskDir);
|
|
1664
1922
|
if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1665
|
-
|
|
1923
|
+
if (run.schemaVersion !== 3) return failed('ORCHESTRATION_STATE_INVALID', 'active delegation requires schemaVersion 3', resolved.taskId);
|
|
1924
|
+
if (run.status !== 'running') {
|
|
1925
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1926
|
+
}
|
|
1927
|
+
const result = activateDelegation(run.pendingDelegation, event, {
|
|
1928
|
+
now: options.now,
|
|
1929
|
+
monotonicNow: options.monotonicNow
|
|
1930
|
+
});
|
|
1666
1931
|
if (!result.ok) {
|
|
1667
1932
|
if (result.code === 'DELEGATION_IGNORED') return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1668
1933
|
return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
@@ -1672,6 +1937,153 @@ function activateOrchestrationDelegation(
|
|
|
1672
1937
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1673
1938
|
}
|
|
1674
1939
|
|
|
1940
|
+
async function awaitOrchestrationDelegationActivation(
|
|
1941
|
+
taskRef: string,
|
|
1942
|
+
event: OrchestrationStageIdentity,
|
|
1943
|
+
options: OrchestrationOptions = {}
|
|
1944
|
+
): Promise<OrchestrationResult> {
|
|
1945
|
+
const sleep = options.sleep
|
|
1946
|
+
?? ((milliseconds: number) => new Promise<void>((resolve) => setTimeout(resolve, milliseconds)));
|
|
1947
|
+
while (true) {
|
|
1948
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1949
|
+
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1950
|
+
const run = readRun(resolved.taskDir);
|
|
1951
|
+
if (!run?.pendingDelegation) {
|
|
1952
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1953
|
+
}
|
|
1954
|
+
if (run.schemaVersion !== 3) {
|
|
1955
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'activation barrier requires schemaVersion 3', resolved.taskId);
|
|
1956
|
+
}
|
|
1957
|
+
const receipt = run.pendingDelegation;
|
|
1958
|
+
const matches = receipt.stage === event.stage
|
|
1959
|
+
&& receipt.round === event.round
|
|
1960
|
+
&& receipt.artifact === event.artifact
|
|
1961
|
+
&& receipt.role === event.role;
|
|
1962
|
+
if (!matches) {
|
|
1963
|
+
return pauseOrchestration(
|
|
1964
|
+
taskRef,
|
|
1965
|
+
'ORCHESTRATION_PROVENANCE_MISMATCH',
|
|
1966
|
+
'activation barrier identity does not match the pending delegation',
|
|
1967
|
+
true,
|
|
1968
|
+
options
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
if (receipt.status === 'activated' || receipt.status === 'stage-completed') {
|
|
1972
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1973
|
+
}
|
|
1974
|
+
if (run.status !== 'running') {
|
|
1975
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1976
|
+
}
|
|
1977
|
+
if (receipt.status !== 'prepared') {
|
|
1978
|
+
return pauseOrchestration(
|
|
1979
|
+
taskRef,
|
|
1980
|
+
'ORCHESTRATION_ACTIVATION_STATE_INVALID',
|
|
1981
|
+
`activation barrier found receipt status '${receipt.status}'`,
|
|
1982
|
+
true,
|
|
1983
|
+
options
|
|
1984
|
+
);
|
|
1985
|
+
}
|
|
1986
|
+
if (
|
|
1987
|
+
receipt.activationDeadlineAt == null
|
|
1988
|
+
|| !Number.isFinite(Date.parse(receipt.activationDeadlineAt))
|
|
1989
|
+
) {
|
|
1990
|
+
return pauseOrchestration(
|
|
1991
|
+
taskRef,
|
|
1992
|
+
'ORCHESTRATION_DELEGATION_NOT_DISPATCHED',
|
|
1993
|
+
'activation barrier was entered before the native spawn dispatch boundary',
|
|
1994
|
+
true,
|
|
1995
|
+
options
|
|
1996
|
+
);
|
|
1997
|
+
}
|
|
1998
|
+
if (Date.parse((options.now ?? (() => new Date().toISOString()))()) >= Date.parse(receipt.activationDeadlineAt)) {
|
|
1999
|
+
return pauseOrchestration(
|
|
2000
|
+
taskRef,
|
|
2001
|
+
'ORCHESTRATION_ACTIVATION_TIMEOUT',
|
|
2002
|
+
'fresh child did not receive trusted activation evidence before the deadline',
|
|
2003
|
+
true,
|
|
2004
|
+
options
|
|
2005
|
+
);
|
|
2006
|
+
}
|
|
2007
|
+
await sleep(100);
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
function recoverPreparedOrchestrationDelegation(
|
|
2012
|
+
taskRef: string,
|
|
2013
|
+
options: OrchestrationOptions = {}
|
|
2014
|
+
): OrchestrationResult {
|
|
2015
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
2016
|
+
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
2017
|
+
try {
|
|
2018
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.recover-prepared', () => {
|
|
2019
|
+
const run = readRun(resolved.taskDir);
|
|
2020
|
+
const receipt = run?.pendingDelegation;
|
|
2021
|
+
if (!run || !receipt) {
|
|
2022
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
2023
|
+
}
|
|
2024
|
+
if (receipt.status !== 'prepared') {
|
|
2025
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_UNSAFE', 'only a never-activated prepared receipt can recover', resolved.taskId);
|
|
2026
|
+
}
|
|
2027
|
+
const wallNow = Date.parse((options.now ?? (() => new Date().toISOString()))());
|
|
2028
|
+
if (
|
|
2029
|
+
receipt.activationDeadlineAt != null
|
|
2030
|
+
&& Number.isFinite(Date.parse(receipt.activationDeadlineAt))
|
|
2031
|
+
&& wallNow <= Date.parse(receipt.activationDeadlineAt)
|
|
2032
|
+
) {
|
|
2033
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_EARLY', 'prepared receipt activation deadline has not elapsed', resolved.taskId);
|
|
2034
|
+
}
|
|
2035
|
+
const activeLifecycleEvidence = options.hasActiveLifecycleEvidence?.(receipt)
|
|
2036
|
+
?? (
|
|
2037
|
+
receipt.client === 'codex'
|
|
2038
|
+
&& receipt.lifecycleProvenance
|
|
2039
|
+
&& hasActiveCodexLifecycleEvidence(
|
|
2040
|
+
resolveAgentRuntimeStoreRoot({ repoRoot: resolved.repoRoot, store: 'lifecycle' }),
|
|
2041
|
+
{
|
|
2042
|
+
nativeAgent: `agent-infra-lifecycle-${receipt.role}`,
|
|
2043
|
+
hookDefinitionHash: receipt.lifecycleProvenance.hookDefinitionHash
|
|
2044
|
+
}
|
|
2045
|
+
)
|
|
2046
|
+
);
|
|
2047
|
+
if (activeLifecycleEvidence) {
|
|
2048
|
+
return failed(
|
|
2049
|
+
'ORCHESTRATION_PREPARED_RECOVERY_ACTIVE_EVIDENCE',
|
|
2050
|
+
'active Codex lifecycle evidence blocks prepared receipt recovery',
|
|
2051
|
+
resolved.taskId
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
if (fs.existsSync(commitIntentPath(resolved.taskDir))) {
|
|
2055
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_UNSAFE', 'active commit intent blocks prepared recovery', resolved.taskId);
|
|
2056
|
+
}
|
|
2057
|
+
const current = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
|
|
2058
|
+
gitRoot: gitRootFor(resolved.repoRoot, options),
|
|
2059
|
+
stateRoot: resolved.repoRoot,
|
|
2060
|
+
taskId: receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null
|
|
2061
|
+
});
|
|
2062
|
+
if (current !== receipt.beforeFingerprint) {
|
|
2063
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_DRIFT', 'workspace changed after delegation preparation', resolved.taskId);
|
|
2064
|
+
}
|
|
2065
|
+
const aborted = abortPreparedDelegation(receipt);
|
|
2066
|
+
if (!aborted.ok) return failed(aborted.code, aborted.message, resolved.taskId);
|
|
2067
|
+
const updated = withUpdatedRun(run, {
|
|
2068
|
+
status: 'running',
|
|
2069
|
+
pause: null,
|
|
2070
|
+
nextStage: null,
|
|
2071
|
+
pendingDelegation: null,
|
|
2072
|
+
receipts: Object.freeze([...run.receipts, aborted.receipt])
|
|
2073
|
+
}, options.now);
|
|
2074
|
+
saveRun(resolved.taskDir, updated);
|
|
2075
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
2076
|
+
});
|
|
2077
|
+
} catch (error) {
|
|
2078
|
+
if (error instanceof TaskExecutionLockError) return failed(error.code, error.message, resolved.taskId);
|
|
2079
|
+
return failed(
|
|
2080
|
+
'ORCHESTRATION_PREPARED_RECOVERY_FAILED',
|
|
2081
|
+
error instanceof Error ? error.message : String(error),
|
|
2082
|
+
resolved.taskId
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
|
|
1675
2087
|
function completeOrchestrationStage(
|
|
1676
2088
|
taskRef: string,
|
|
1677
2089
|
event: Parameters<typeof completeDelegationStage>[1],
|
|
@@ -1681,6 +2093,7 @@ function completeOrchestrationStage(
|
|
|
1681
2093
|
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1682
2094
|
const run = readRun(resolved.taskDir);
|
|
1683
2095
|
if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
|
|
2096
|
+
if (run.schemaVersion !== 3) return failed('ORCHESTRATION_STATE_INVALID', 'stage completion requires schemaVersion 3', resolved.taskId);
|
|
1684
2097
|
if (!run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending delegation', resolved.taskId);
|
|
1685
2098
|
const result = completeDelegationStage(run.pendingDelegation, event);
|
|
1686
2099
|
if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
@@ -1703,6 +2116,7 @@ function inspectOrchestrationStage(
|
|
|
1703
2116
|
return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
1704
2117
|
}
|
|
1705
2118
|
if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
|
|
2119
|
+
if (run.schemaVersion !== 3) return failed('ORCHESTRATION_STATE_INVALID', 'stage inspection requires schemaVersion 3', resolved.taskId);
|
|
1706
2120
|
const receipt = run.pendingDelegation;
|
|
1707
2121
|
if (
|
|
1708
2122
|
!receipt
|
|
@@ -1761,6 +2175,7 @@ function sealOrchestrationDelegation(
|
|
|
1761
2175
|
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1762
2176
|
const run = readRun(resolved.taskDir);
|
|
1763
2177
|
if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
2178
|
+
if (run.schemaVersion !== 3) return failed('ORCHESTRATION_STATE_INVALID', 'delegation sealing requires schemaVersion 3', resolved.taskId);
|
|
1764
2179
|
const result = sealDelegation(run.pendingDelegation, event, { now: options.now });
|
|
1765
2180
|
if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
1766
2181
|
const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
|
|
@@ -1773,6 +2188,7 @@ function advanceOrchestration(taskRef: string, options: OrchestrationOptions = {
|
|
|
1773
2188
|
if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1774
2189
|
const run = readRun(resolved.taskDir);
|
|
1775
2190
|
if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
2191
|
+
if (run.schemaVersion !== 3) return failed('ORCHESTRATION_STATE_INVALID', 'orchestration advance requires schemaVersion 3', resolved.taskId);
|
|
1776
2192
|
const result = consumeDelegation(run.pendingDelegation, { now: options.now });
|
|
1777
2193
|
if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
1778
2194
|
const completed = result.receipt.stage === 'commit';
|
|
@@ -1809,6 +2225,7 @@ function pauseOrchestration(
|
|
|
1809
2225
|
export {
|
|
1810
2226
|
activateMatchingOrchestrationDelegation,
|
|
1811
2227
|
activateOrchestrationDelegation,
|
|
2228
|
+
awaitOrchestrationDelegationActivation,
|
|
1812
2229
|
advanceOrchestration,
|
|
1813
2230
|
abortCommitIntent,
|
|
1814
2231
|
beginCommitIntent,
|
|
@@ -1817,7 +2234,10 @@ export {
|
|
|
1817
2234
|
commitOrchestrationStageCompletion,
|
|
1818
2235
|
completeCommitIntent,
|
|
1819
2236
|
completeOrchestrationStage,
|
|
2237
|
+
dispatchOrchestrationDelegation,
|
|
1820
2238
|
inspectOrchestrationStage,
|
|
2239
|
+
hasActivatableOrchestrationDelegation,
|
|
2240
|
+
hasSealableOrchestrationDelegation,
|
|
1821
2241
|
orchestrationPath,
|
|
1822
2242
|
pauseMatchingOrchestrationDelegation,
|
|
1823
2243
|
pauseOrchestration,
|
|
@@ -1826,6 +2246,7 @@ export {
|
|
|
1826
2246
|
readRun,
|
|
1827
2247
|
reconcileMatchingOrchestrationDelegation,
|
|
1828
2248
|
recoverCommitIntent,
|
|
2249
|
+
recoverPreparedOrchestrationDelegation,
|
|
1829
2250
|
routeOrchestration,
|
|
1830
2251
|
sealMatchingOrchestrationDelegation,
|
|
1831
2252
|
sealMatchingOrchestrationDelegationWithHostEvidence,
|