@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
|
@@ -7,14 +7,16 @@ 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, foldBlankToNull, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
|
|
11
11
|
import { normalizeAgentClients } from '../agent-clients/config.js';
|
|
12
|
+
import { resolveAgentRuntimeStoreRoot } from '../runtime/agent-runtime.js';
|
|
12
13
|
import { inspectPlatformPullRequest } from '../platform/pull-requests.js';
|
|
13
14
|
import { getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
|
|
14
15
|
import { isAgentClientId } from '../agent-clients/types.js';
|
|
15
16
|
import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
|
|
16
17
|
import { assertGitRepositoryBinding } from '../git/worktree-identity.js';
|
|
17
18
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
19
|
+
import { hasActiveCodexLifecycleEvidence } from '../agent-clients/adapters/codex-lifecycle/store.js';
|
|
18
20
|
import { CommitIntentError, commitIntentPath, createCommitIntent, digest, readCommitIntent, removeCommitIntent, removeCommitIntentByDigest, serialize, updateCommitIntent } from './commit-intent.js';
|
|
19
21
|
import { inspectCommitFinalization, planCommitTaskFinalization } from './commit-finalization.js';
|
|
20
22
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
@@ -33,7 +35,7 @@ function readRun(taskDir) {
|
|
|
33
35
|
if (!fs.existsSync(file))
|
|
34
36
|
return null;
|
|
35
37
|
const run = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
36
|
-
if (run.schemaVersion
|
|
38
|
+
if (![2, 3].includes(run.schemaVersion) || !isV2Policy(run.modelPolicy))
|
|
37
39
|
return run;
|
|
38
40
|
return {
|
|
39
41
|
...run,
|
|
@@ -110,7 +112,7 @@ function commitFinalizationView(inspection) {
|
|
|
110
112
|
}
|
|
111
113
|
function validCommitRun(run, taskId) {
|
|
112
114
|
return run !== null
|
|
113
|
-
&& [1, 2].includes(run.schemaVersion)
|
|
115
|
+
&& [1, 2, 3].includes(run.schemaVersion)
|
|
114
116
|
&& run.taskId === taskId
|
|
115
117
|
&& ['running', 'paused', 'completed'].includes(run.status)
|
|
116
118
|
&& Array.isArray(run.receipts)
|
|
@@ -175,6 +177,36 @@ function canRecoverCodexUnsupportedPause(run, taskDir) {
|
|
|
175
177
|
&& !fs.existsSync(commitIntentPath(taskDir))
|
|
176
178
|
&& (run.recoveryHistory ?? []).every((entry) => entry.code === 'MODEL_POLICY_SUPPLEMENTED');
|
|
177
179
|
}
|
|
180
|
+
function canRecoverClaudeCodeUnsupportedPause(run, taskDir) {
|
|
181
|
+
return run.schemaVersion === 3
|
|
182
|
+
&& run.status === 'paused'
|
|
183
|
+
&& run.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
184
|
+
&& run.modelPolicySource?.client === 'claude-code'
|
|
185
|
+
&& run.stepCount === 0
|
|
186
|
+
&& run.nextStage === null
|
|
187
|
+
&& run.baseline === ''
|
|
188
|
+
&& run.pendingDelegation === null
|
|
189
|
+
&& run.receipts.length === 0
|
|
190
|
+
&& run.commitAuthorization?.issuedAt === null
|
|
191
|
+
&& run.commitAuthorization?.consumedAt === null
|
|
192
|
+
&& run.completionEvidence == null
|
|
193
|
+
&& !fs.existsSync(commitIntentPath(taskDir))
|
|
194
|
+
&& (run.recoveryHistory ?? []).length === 0;
|
|
195
|
+
}
|
|
196
|
+
function canMigrateRecoverableV2Pause(run, taskDir) {
|
|
197
|
+
return run.schemaVersion === 2
|
|
198
|
+
&& run.status === 'paused'
|
|
199
|
+
&& run.pause?.recoverable === true
|
|
200
|
+
&& run.stepCount === 0
|
|
201
|
+
&& run.nextStage === null
|
|
202
|
+
&& run.baseline === ''
|
|
203
|
+
&& run.pendingDelegation === null
|
|
204
|
+
&& run.receipts.length === 0
|
|
205
|
+
&& run.commitAuthorization?.issuedAt === null
|
|
206
|
+
&& run.commitAuthorization?.consumedAt === null
|
|
207
|
+
&& run.completionEvidence == null
|
|
208
|
+
&& !fs.existsSync(commitIntentPath(taskDir));
|
|
209
|
+
}
|
|
178
210
|
function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
179
211
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
180
212
|
if (!resolved.ok)
|
|
@@ -190,7 +222,7 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
190
222
|
return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
191
223
|
}
|
|
192
224
|
if (existing) {
|
|
193
|
-
if (![1, 2].includes(existing.schemaVersion)
|
|
225
|
+
if (![1, 2, 3].includes(existing.schemaVersion)
|
|
194
226
|
|| !Array.isArray(existing.receipts)
|
|
195
227
|
|| !Object.prototype.hasOwnProperty.call(existing, 'pendingDelegation')) {
|
|
196
228
|
return failed('ORCHESTRATION_STATE_INVALID', 'orchestration state has an invalid schema', resolved.taskId);
|
|
@@ -198,17 +230,20 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
198
230
|
if (existing.status === 'completed' && existing.schemaVersion === 1) {
|
|
199
231
|
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
200
232
|
}
|
|
201
|
-
if (existing.schemaVersion === 2) {
|
|
233
|
+
if (existing.schemaVersion === 2 || existing.schemaVersion === 3) {
|
|
202
234
|
const policyError = validateModelPolicy(isV2Policy(existing.modelPolicy) ? existing.modelPolicy : undefined);
|
|
203
235
|
if (policyError
|
|
204
236
|
|| !existing.modelPolicySource
|
|
205
237
|
|| !isAgentClientId(existing.modelPolicySource.client)
|
|
206
238
|
|| !Array.isArray(existing.recoveryHistory)) {
|
|
207
|
-
return failed('ORCHESTRATION_STATE_INVALID',
|
|
239
|
+
return failed('ORCHESTRATION_STATE_INVALID', `schemaVersion ${existing.schemaVersion} orchestration state is incomplete`, resolved.taskId);
|
|
208
240
|
}
|
|
209
241
|
if (existing.status === 'completed') {
|
|
210
242
|
return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
211
243
|
}
|
|
244
|
+
if (existing.schemaVersion === 2 && existing.status === 'running') {
|
|
245
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'active schemaVersion 2 runs cannot be advanced by lifecycle protocol v3', resolved.taskId);
|
|
246
|
+
}
|
|
212
247
|
if (existing.modelPolicySource.client !== options.client) {
|
|
213
248
|
return failed('ORCHESTRATION_CLIENT_MISMATCH', 'provided client does not match the persisted run policy source', resolved.taskId);
|
|
214
249
|
}
|
|
@@ -221,6 +256,39 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
221
256
|
}
|
|
222
257
|
}
|
|
223
258
|
if (existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED') {
|
|
259
|
+
if (existing.modelPolicySource.client === 'claude-code') {
|
|
260
|
+
if (!canRecoverClaudeCodeUnsupportedPause(existing, resolved.taskDir)) {
|
|
261
|
+
return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
262
|
+
}
|
|
263
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
264
|
+
const previousPause = existing.pause;
|
|
265
|
+
const recovery = {
|
|
266
|
+
code: 'CLIENT_CAPABILITY_ENABLED_NO_MIGRATION',
|
|
267
|
+
recoveredAt: now,
|
|
268
|
+
previousSchemaVersion: 3,
|
|
269
|
+
previousStatus: 'paused',
|
|
270
|
+
previousPause,
|
|
271
|
+
client: 'claude-code',
|
|
272
|
+
guards: {
|
|
273
|
+
stepCount: 0,
|
|
274
|
+
nextStage: null,
|
|
275
|
+
baselineEmpty: true,
|
|
276
|
+
receiptCount: 0,
|
|
277
|
+
pendingDelegation: false,
|
|
278
|
+
commitAuthorizationUnused: true,
|
|
279
|
+
completionEvidenceAbsent: true,
|
|
280
|
+
commitIntentAbsent: true
|
|
281
|
+
},
|
|
282
|
+
resultingStatus: 'running'
|
|
283
|
+
};
|
|
284
|
+
const resumed = withUpdatedRun(existing, {
|
|
285
|
+
status: 'running',
|
|
286
|
+
pause: null,
|
|
287
|
+
recoveryHistory: Object.freeze([...(existing.recoveryHistory ?? []), recovery])
|
|
288
|
+
}, () => now);
|
|
289
|
+
saveRun(resolved.taskDir, resumed);
|
|
290
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
291
|
+
}
|
|
224
292
|
if (!canRecoverCodexUnsupportedPause(existing, resolved.taskDir)) {
|
|
225
293
|
return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
226
294
|
}
|
|
@@ -246,6 +314,7 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
246
314
|
resultingStatus: 'running'
|
|
247
315
|
};
|
|
248
316
|
const resumed = withUpdatedRun(existing, {
|
|
317
|
+
schemaVersion: 3,
|
|
249
318
|
status: 'running',
|
|
250
319
|
pause: null,
|
|
251
320
|
recoveryHistory: Object.freeze([...(existing.recoveryHistory ?? []), recovery])
|
|
@@ -254,7 +323,42 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
254
323
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
255
324
|
}
|
|
256
325
|
if (existing.status === 'paused' && existing.pause?.recoverable && existing.pendingDelegation === null) {
|
|
257
|
-
|
|
326
|
+
if (existing.schemaVersion === 2 && !canMigrateRecoverableV2Pause(existing, resolved.taskDir)) {
|
|
327
|
+
const paused = withUpdatedRun(existing, {
|
|
328
|
+
pause: {
|
|
329
|
+
code: 'ORCHESTRATION_SCHEMA_MIGRATION_REQUIRED',
|
|
330
|
+
message: 'schemaVersion 2 recoverable pause contains execution evidence and cannot migrate automatically',
|
|
331
|
+
recoverable: false
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
saveRun(resolved.taskDir, paused);
|
|
335
|
+
return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
|
|
336
|
+
}
|
|
337
|
+
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
338
|
+
const recovery = existing.schemaVersion === 2 ? {
|
|
339
|
+
code: 'SCHEMA_V3_MIGRATED',
|
|
340
|
+
recoveredAt: now,
|
|
341
|
+
previousSchemaVersion: 2,
|
|
342
|
+
previousStatus: 'paused',
|
|
343
|
+
previousPause: existing.pause,
|
|
344
|
+
guards: {
|
|
345
|
+
stepCount: 0,
|
|
346
|
+
nextStage: null,
|
|
347
|
+
baselineEmpty: true,
|
|
348
|
+
receiptCount: 0,
|
|
349
|
+
pendingDelegation: false,
|
|
350
|
+
commitAuthorizationUnused: true,
|
|
351
|
+
completionEvidenceAbsent: true,
|
|
352
|
+
commitIntentAbsent: true
|
|
353
|
+
},
|
|
354
|
+
resultingStatus: 'running'
|
|
355
|
+
} : null;
|
|
356
|
+
const resumed = withUpdatedRun(existing, {
|
|
357
|
+
schemaVersion: 3,
|
|
358
|
+
status: 'running',
|
|
359
|
+
pause: null,
|
|
360
|
+
...(recovery ? { recoveryHistory: Object.freeze([...(existing.recoveryHistory ?? []), recovery]) } : {})
|
|
361
|
+
}, () => now);
|
|
258
362
|
saveRun(resolved.taskDir, resumed);
|
|
259
363
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
|
|
260
364
|
}
|
|
@@ -328,7 +432,7 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
328
432
|
};
|
|
329
433
|
const recovered = Object.freeze({
|
|
330
434
|
...existing,
|
|
331
|
-
schemaVersion:
|
|
435
|
+
schemaVersion: 3,
|
|
332
436
|
status: resultingStatus,
|
|
333
437
|
modelPolicy: policy,
|
|
334
438
|
modelPolicySource: source,
|
|
@@ -360,7 +464,7 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
360
464
|
const now = (options.now ?? (() => new Date().toISOString()))();
|
|
361
465
|
const source = { kind: sourceKind, client: options.client, resolvedAt: now };
|
|
362
466
|
const run = {
|
|
363
|
-
schemaVersion:
|
|
467
|
+
schemaVersion: 3,
|
|
364
468
|
taskId: resolved.taskId,
|
|
365
469
|
runId: (options.id ?? randomUUID)(),
|
|
366
470
|
status: 'running',
|
|
@@ -1025,6 +1129,9 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
1025
1129
|
const run = readRun(resolved.taskDir);
|
|
1026
1130
|
if (!run || run.status !== 'running')
|
|
1027
1131
|
return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
|
|
1132
|
+
if (run.schemaVersion !== 3) {
|
|
1133
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'delegation preparation requires orchestration schemaVersion 3', resolved.taskId);
|
|
1134
|
+
}
|
|
1028
1135
|
if (!isV2Policy(run.modelPolicy))
|
|
1029
1136
|
return failed('ORCHESTRATION_MODEL_EVIDENCE_MISSING', 'running orchestration has no persisted model policy', resolved.taskId);
|
|
1030
1137
|
if (run.pendingDelegation)
|
|
@@ -1045,6 +1152,9 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
1045
1152
|
if (!validModel(input.requestedReasoningEffort)) {
|
|
1046
1153
|
return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', resolved.taskId);
|
|
1047
1154
|
}
|
|
1155
|
+
if (input.client === 'codex' && !input.lifecycleProvenance) {
|
|
1156
|
+
return failed('ORCHESTRATION_CODEX_PROVENANCE_REQUIRED', 'Codex prepare requires lifecycle provenance', resolved.taskId);
|
|
1157
|
+
}
|
|
1048
1158
|
const expectedPolicy = run.modelPolicy[next.role];
|
|
1049
1159
|
if (input.requestedModel !== expectedPolicy.model) {
|
|
1050
1160
|
return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
|
|
@@ -1074,8 +1184,9 @@ function prepareOrchestrationDelegationUnlocked(taskRef, input, options = {}) {
|
|
|
1074
1184
|
requestedModel: input.requestedModel,
|
|
1075
1185
|
requestedReasoningEffort: input.requestedReasoningEffort,
|
|
1076
1186
|
workspaceSnapshotScope: 'task',
|
|
1187
|
+
lifecycleProvenance: input.lifecycleProvenance ?? null,
|
|
1077
1188
|
beforeFingerprint
|
|
1078
|
-
}, { id: options.id, now: options.now });
|
|
1189
|
+
}, { id: options.id, now: options.now, monotonicNow: options.monotonicNow });
|
|
1079
1190
|
const updated = withUpdatedRun(run, {
|
|
1080
1191
|
nextStage: next.stage,
|
|
1081
1192
|
pendingDelegation: receipt,
|
|
@@ -1099,6 +1210,39 @@ function prepareOrchestrationDelegation(taskRef, input, options = {}) {
|
|
|
1099
1210
|
throw error;
|
|
1100
1211
|
}
|
|
1101
1212
|
}
|
|
1213
|
+
function dispatchOrchestrationDelegationUnlocked(taskRef, options = {}) {
|
|
1214
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1215
|
+
if (!resolved.ok)
|
|
1216
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1217
|
+
const run = readRun(resolved.taskDir);
|
|
1218
|
+
if (!run?.pendingDelegation)
|
|
1219
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1220
|
+
if (run.schemaVersion !== 3 || run.status !== 'running') {
|
|
1221
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'spawn dispatch requires a running schemaVersion 3 orchestration', resolved.taskId);
|
|
1222
|
+
}
|
|
1223
|
+
const result = dispatchDelegation(run.pendingDelegation, {
|
|
1224
|
+
now: options.now,
|
|
1225
|
+
monotonicNow: options.monotonicNow
|
|
1226
|
+
});
|
|
1227
|
+
if (!result.ok)
|
|
1228
|
+
return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
1229
|
+
const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
|
|
1230
|
+
saveRun(resolved.taskDir, updated);
|
|
1231
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1232
|
+
}
|
|
1233
|
+
function dispatchOrchestrationDelegation(taskRef, options = {}) {
|
|
1234
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1235
|
+
if (!resolved.ok)
|
|
1236
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1237
|
+
try {
|
|
1238
|
+
return withTaskExecutionLock(resolved.repoRoot, '__repository__', 'task-orchestration.dispatch.repository', () => withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.dispatch.task', () => dispatchOrchestrationDelegationUnlocked(taskRef, options)));
|
|
1239
|
+
}
|
|
1240
|
+
catch (error) {
|
|
1241
|
+
if (error instanceof TaskExecutionLockError)
|
|
1242
|
+
return failed(error.code, error.message, resolved.taskId);
|
|
1243
|
+
throw error;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1102
1246
|
function matchingDelegations(predicate, options) {
|
|
1103
1247
|
const repoRoot = options.repoRoot ?? process.cwd();
|
|
1104
1248
|
const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
|
|
@@ -1108,7 +1252,7 @@ function matchingDelegations(predicate, options) {
|
|
|
1108
1252
|
.filter((entry) => entry.isDirectory())
|
|
1109
1253
|
.flatMap((entry) => {
|
|
1110
1254
|
const run = readRun(path.join(activeRoot, entry.name));
|
|
1111
|
-
if (run?.status !== 'running' || !run.pendingDelegation)
|
|
1255
|
+
if (run?.schemaVersion !== 3 || run.status !== 'running' || !run.pendingDelegation)
|
|
1112
1256
|
return [];
|
|
1113
1257
|
const receipt = run.pendingDelegation;
|
|
1114
1258
|
return predicate(receipt)
|
|
@@ -1124,6 +1268,15 @@ function uniqueMatchingDelegation(client, predicate, options) {
|
|
|
1124
1268
|
return failed('ORCHESTRATION_DELEGATION_AMBIGUOUS', 'multiple lifecycle delegations match the native hook event');
|
|
1125
1269
|
return matches[0];
|
|
1126
1270
|
}
|
|
1271
|
+
function hasActivatableOrchestrationDelegation(client, childId, options = {}) {
|
|
1272
|
+
return matchingDelegations((receipt) => receipt.client === client && (receipt.status === 'prepared'
|
|
1273
|
+
|| (receipt.childId === childId && ['activated', 'stage-completed'].includes(receipt.status))), options).length > 0;
|
|
1274
|
+
}
|
|
1275
|
+
function hasSealableOrchestrationDelegation(client, childId, options = {}) {
|
|
1276
|
+
return matchingDelegations((receipt) => receipt.client === client
|
|
1277
|
+
&& receipt.childId === childId
|
|
1278
|
+
&& ['stage-completed', 'sealed'].includes(receipt.status), options).length > 0;
|
|
1279
|
+
}
|
|
1127
1280
|
function activateMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
1128
1281
|
const role = managedDelegationRole(event.nativeAgent);
|
|
1129
1282
|
if (!role)
|
|
@@ -1137,9 +1290,9 @@ function activateMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
|
1137
1290
|
return matched;
|
|
1138
1291
|
const receipt = replay.run.pendingDelegation;
|
|
1139
1292
|
const sameEvidence = receipt.parentId === event.parentId
|
|
1140
|
-
&& receipt.spawnMode === event.spawnMode
|
|
1141
|
-
&& receipt.actualModel === event.actualModel
|
|
1142
|
-
&& receipt.actualReasoningEffort === event.actualReasoningEffort
|
|
1293
|
+
&& (receipt.spawnMode ?? null) === (event.spawnMode ?? null)
|
|
1294
|
+
&& (receipt.actualModel ?? null) === foldBlankToNull(event.actualModel)
|
|
1295
|
+
&& (receipt.actualReasoningEffort ?? null) === foldBlankToNull(event.actualReasoningEffort)
|
|
1143
1296
|
&& receipt.modelFallbackReason === (event.modelFallbackReason ?? null)
|
|
1144
1297
|
&& receipt.reasoningEffortFallbackReason === (event.reasoningEffortFallbackReason ?? null)
|
|
1145
1298
|
&& receipt.hostEvidence?.hookDefinitionHash === event.hostEvidence?.hookDefinitionHash;
|
|
@@ -1184,7 +1337,11 @@ function sealMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
|
1184
1337
|
childId: event.childId,
|
|
1185
1338
|
exitCode: 0,
|
|
1186
1339
|
afterFingerprint,
|
|
1187
|
-
changedPaths
|
|
1340
|
+
changedPaths,
|
|
1341
|
+
actualModel: event.actualModel,
|
|
1342
|
+
actualReasoningEffort: event.actualReasoningEffort,
|
|
1343
|
+
modelFallbackReason: event.modelFallbackReason,
|
|
1344
|
+
reasoningEffortFallbackReason: event.reasoningEffortFallbackReason
|
|
1188
1345
|
}, options);
|
|
1189
1346
|
}
|
|
1190
1347
|
catch (error) {
|
|
@@ -1217,7 +1374,7 @@ function sealMatchingOrchestrationDelegationWithHostEvidence(client, event, cons
|
|
|
1217
1374
|
});
|
|
1218
1375
|
const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(gitRoot, receipt.beforeFingerprint, afterFingerprint);
|
|
1219
1376
|
const baseEvent = { childId: event.childId, exitCode: 0, afterFingerprint, changedPaths };
|
|
1220
|
-
const validated = sealDelegation(receipt, baseEvent, { now: options.now });
|
|
1377
|
+
const validated = sealDelegation(receipt, baseEvent, { now: options.now, requireHostEvidence: false });
|
|
1221
1378
|
if (!validated.ok) {
|
|
1222
1379
|
return pauseOrchestration(matched.taskId, validated.code, validated.message, true, options);
|
|
1223
1380
|
}
|
|
@@ -1255,7 +1412,15 @@ function activateOrchestrationDelegation(taskRef, event, options = {}) {
|
|
|
1255
1412
|
const run = readRun(resolved.taskDir);
|
|
1256
1413
|
if (!run?.pendingDelegation)
|
|
1257
1414
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1258
|
-
|
|
1415
|
+
if (run.schemaVersion !== 3)
|
|
1416
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'active delegation requires schemaVersion 3', resolved.taskId);
|
|
1417
|
+
if (run.status !== 'running') {
|
|
1418
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1419
|
+
}
|
|
1420
|
+
const result = activateDelegation(run.pendingDelegation, event, {
|
|
1421
|
+
now: options.now,
|
|
1422
|
+
monotonicNow: options.monotonicNow
|
|
1423
|
+
});
|
|
1259
1424
|
if (!result.ok) {
|
|
1260
1425
|
if (result.code === 'DELEGATION_IGNORED')
|
|
1261
1426
|
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
@@ -1265,6 +1430,108 @@ function activateOrchestrationDelegation(taskRef, event, options = {}) {
|
|
|
1265
1430
|
saveRun(resolved.taskDir, updated);
|
|
1266
1431
|
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1267
1432
|
}
|
|
1433
|
+
async function awaitOrchestrationDelegationActivation(taskRef, event, options = {}) {
|
|
1434
|
+
const sleep = options.sleep
|
|
1435
|
+
?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
1436
|
+
while (true) {
|
|
1437
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1438
|
+
if (!resolved.ok)
|
|
1439
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1440
|
+
const run = readRun(resolved.taskDir);
|
|
1441
|
+
if (!run?.pendingDelegation) {
|
|
1442
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1443
|
+
}
|
|
1444
|
+
if (run.schemaVersion !== 3) {
|
|
1445
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'activation barrier requires schemaVersion 3', resolved.taskId);
|
|
1446
|
+
}
|
|
1447
|
+
const receipt = run.pendingDelegation;
|
|
1448
|
+
const matches = receipt.stage === event.stage
|
|
1449
|
+
&& receipt.round === event.round
|
|
1450
|
+
&& receipt.artifact === event.artifact
|
|
1451
|
+
&& receipt.role === event.role;
|
|
1452
|
+
if (!matches) {
|
|
1453
|
+
return pauseOrchestration(taskRef, 'ORCHESTRATION_PROVENANCE_MISMATCH', 'activation barrier identity does not match the pending delegation', true, options);
|
|
1454
|
+
}
|
|
1455
|
+
if (receipt.status === 'activated' || receipt.status === 'stage-completed') {
|
|
1456
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1457
|
+
}
|
|
1458
|
+
if (run.status !== 'running') {
|
|
1459
|
+
return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
|
|
1460
|
+
}
|
|
1461
|
+
if (receipt.status !== 'prepared') {
|
|
1462
|
+
return pauseOrchestration(taskRef, 'ORCHESTRATION_ACTIVATION_STATE_INVALID', `activation barrier found receipt status '${receipt.status}'`, true, options);
|
|
1463
|
+
}
|
|
1464
|
+
if (receipt.activationDeadlineAt == null
|
|
1465
|
+
|| !Number.isFinite(Date.parse(receipt.activationDeadlineAt))) {
|
|
1466
|
+
return pauseOrchestration(taskRef, 'ORCHESTRATION_DELEGATION_NOT_DISPATCHED', 'activation barrier was entered before the native spawn dispatch boundary', true, options);
|
|
1467
|
+
}
|
|
1468
|
+
if (Date.parse((options.now ?? (() => new Date().toISOString()))()) >= Date.parse(receipt.activationDeadlineAt)) {
|
|
1469
|
+
return pauseOrchestration(taskRef, 'ORCHESTRATION_ACTIVATION_TIMEOUT', 'fresh child did not receive trusted activation evidence before the deadline', true, options);
|
|
1470
|
+
}
|
|
1471
|
+
await sleep(100);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
|
|
1475
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1476
|
+
if (!resolved.ok)
|
|
1477
|
+
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
1478
|
+
try {
|
|
1479
|
+
return withTaskExecutionLock(resolved.repoRoot, resolved.taskId, 'task-orchestration.recover-prepared', () => {
|
|
1480
|
+
const run = readRun(resolved.taskDir);
|
|
1481
|
+
const receipt = run?.pendingDelegation;
|
|
1482
|
+
if (!run || !receipt) {
|
|
1483
|
+
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1484
|
+
}
|
|
1485
|
+
if (receipt.status !== 'prepared') {
|
|
1486
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_UNSAFE', 'only a never-activated prepared receipt can recover', resolved.taskId);
|
|
1487
|
+
}
|
|
1488
|
+
const wallNow = Date.parse((options.now ?? (() => new Date().toISOString()))());
|
|
1489
|
+
if (receipt.activationDeadlineAt != null
|
|
1490
|
+
&& Number.isFinite(Date.parse(receipt.activationDeadlineAt))
|
|
1491
|
+
&& wallNow <= Date.parse(receipt.activationDeadlineAt)) {
|
|
1492
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_EARLY', 'prepared receipt activation deadline has not elapsed', resolved.taskId);
|
|
1493
|
+
}
|
|
1494
|
+
const activeLifecycleEvidence = options.hasActiveLifecycleEvidence?.(receipt)
|
|
1495
|
+
?? (receipt.client === 'codex'
|
|
1496
|
+
&& receipt.lifecycleProvenance
|
|
1497
|
+
&& hasActiveCodexLifecycleEvidence(resolveAgentRuntimeStoreRoot({ repoRoot: resolved.repoRoot, store: 'lifecycle' }), {
|
|
1498
|
+
nativeAgent: `agent-infra-lifecycle-${receipt.role}`,
|
|
1499
|
+
hookDefinitionHash: receipt.lifecycleProvenance.hookDefinitionHash
|
|
1500
|
+
}));
|
|
1501
|
+
if (activeLifecycleEvidence) {
|
|
1502
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_ACTIVE_EVIDENCE', 'active Codex lifecycle evidence blocks prepared receipt recovery', resolved.taskId);
|
|
1503
|
+
}
|
|
1504
|
+
if (fs.existsSync(commitIntentPath(resolved.taskDir))) {
|
|
1505
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_UNSAFE', 'active commit intent blocks prepared recovery', resolved.taskId);
|
|
1506
|
+
}
|
|
1507
|
+
const current = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
|
|
1508
|
+
gitRoot: gitRootFor(resolved.repoRoot, options),
|
|
1509
|
+
stateRoot: resolved.repoRoot,
|
|
1510
|
+
taskId: receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null
|
|
1511
|
+
});
|
|
1512
|
+
if (current !== receipt.beforeFingerprint) {
|
|
1513
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_DRIFT', 'workspace changed after delegation preparation', resolved.taskId);
|
|
1514
|
+
}
|
|
1515
|
+
const aborted = abortPreparedDelegation(receipt);
|
|
1516
|
+
if (!aborted.ok)
|
|
1517
|
+
return failed(aborted.code, aborted.message, resolved.taskId);
|
|
1518
|
+
const updated = withUpdatedRun(run, {
|
|
1519
|
+
status: 'running',
|
|
1520
|
+
pause: null,
|
|
1521
|
+
nextStage: null,
|
|
1522
|
+
pendingDelegation: null,
|
|
1523
|
+
receipts: Object.freeze([...run.receipts, aborted.receipt])
|
|
1524
|
+
}, options.now);
|
|
1525
|
+
saveRun(resolved.taskDir, updated);
|
|
1526
|
+
return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
catch (error) {
|
|
1530
|
+
if (error instanceof TaskExecutionLockError)
|
|
1531
|
+
return failed(error.code, error.message, resolved.taskId);
|
|
1532
|
+
return failed('ORCHESTRATION_PREPARED_RECOVERY_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1268
1535
|
function completeOrchestrationStage(taskRef, event, options = {}) {
|
|
1269
1536
|
const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
|
|
1270
1537
|
if (!resolved.ok)
|
|
@@ -1272,6 +1539,8 @@ function completeOrchestrationStage(taskRef, event, options = {}) {
|
|
|
1272
1539
|
const run = readRun(resolved.taskDir);
|
|
1273
1540
|
if (!run)
|
|
1274
1541
|
return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
|
|
1542
|
+
if (run.schemaVersion !== 3)
|
|
1543
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'stage completion requires schemaVersion 3', resolved.taskId);
|
|
1275
1544
|
if (!run.pendingDelegation)
|
|
1276
1545
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending delegation', resolved.taskId);
|
|
1277
1546
|
const result = completeDelegationStage(run.pendingDelegation, event);
|
|
@@ -1294,6 +1563,8 @@ function inspectOrchestrationStage(taskRef, event, options = {}) {
|
|
|
1294
1563
|
}
|
|
1295
1564
|
if (!run)
|
|
1296
1565
|
return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
|
|
1566
|
+
if (run.schemaVersion !== 3)
|
|
1567
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'stage inspection requires schemaVersion 3', resolved.taskId);
|
|
1297
1568
|
const receipt = run.pendingDelegation;
|
|
1298
1569
|
if (!receipt
|
|
1299
1570
|
|| receipt.status !== 'activated'
|
|
@@ -1338,6 +1609,8 @@ function sealOrchestrationDelegation(taskRef, event, options = {}) {
|
|
|
1338
1609
|
const run = readRun(resolved.taskDir);
|
|
1339
1610
|
if (!run?.pendingDelegation)
|
|
1340
1611
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1612
|
+
if (run.schemaVersion !== 3)
|
|
1613
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'delegation sealing requires schemaVersion 3', resolved.taskId);
|
|
1341
1614
|
const result = sealDelegation(run.pendingDelegation, event, { now: options.now });
|
|
1342
1615
|
if (!result.ok)
|
|
1343
1616
|
return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
@@ -1352,6 +1625,8 @@ function advanceOrchestration(taskRef, options = {}) {
|
|
|
1352
1625
|
const run = readRun(resolved.taskDir);
|
|
1353
1626
|
if (!run?.pendingDelegation)
|
|
1354
1627
|
return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
|
|
1628
|
+
if (run.schemaVersion !== 3)
|
|
1629
|
+
return failed('ORCHESTRATION_STATE_INVALID', 'orchestration advance requires schemaVersion 3', resolved.taskId);
|
|
1355
1630
|
const result = consumeDelegation(run.pendingDelegation, { now: options.now });
|
|
1356
1631
|
if (!result.ok)
|
|
1357
1632
|
return pauseOrchestration(taskRef, result.code, result.message, true, options);
|
|
@@ -1380,5 +1655,5 @@ function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
|
|
|
1380
1655
|
saveRun(resolved.taskDir, updated);
|
|
1381
1656
|
return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
|
|
1382
1657
|
}
|
|
1383
|
-
export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, abortCommitIntent, beginCommitIntent, beginOrResumeOrchestration, checkpointCommitIntent, commitOrchestrationStageCompletion, completeCommitIntent, completeOrchestrationStage, inspectOrchestrationStage, orchestrationPath, pauseMatchingOrchestrationDelegation, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, reconcileMatchingOrchestrationDelegation, recoverCommitIntent, routeOrchestration, sealMatchingOrchestrationDelegation, sealMatchingOrchestrationDelegationWithHostEvidence, sealOrchestrationDelegation, startCommitAttempt, statusCommitIntent, statusOrchestration, terminateCommitAttempt };
|
|
1658
|
+
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 };
|
|
1384
1659
|
//# sourceMappingURL=orchestration.js.map
|
|
@@ -8,6 +8,7 @@ import { finalizeReviewSummaryContent } from './review-artifacts.js';
|
|
|
8
8
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
9
9
|
import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
10
10
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
11
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
11
12
|
const STAGES = {
|
|
12
13
|
analysis: { family: 'review-analysis', action: 'Review Analysis' },
|
|
13
14
|
plan: { family: 'review-plan', action: 'Review Plan' },
|
|
@@ -60,7 +61,7 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
|
|
|
60
61
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
61
62
|
if (!resolved.ok)
|
|
62
63
|
return failed(request, resolved.code, resolved.message, resolved.taskId);
|
|
63
|
-
if (resolved.state !== 'active') {
|
|
64
|
+
if (resolved.state !== 'active' && !allowsManualOverride(options.manualOverride, 'review-finalization', 'TASK_STATE_MISMATCH')) {
|
|
64
65
|
return failed(request, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
65
66
|
}
|
|
66
67
|
const parsedArtifact = parseArtifactName(request.artifact);
|
|
@@ -168,7 +169,7 @@ function finalizeReviewSummaryUnlocked(request, options = {}) {
|
|
|
168
169
|
};
|
|
169
170
|
}
|
|
170
171
|
function finalizeReviewSummary(request, options = {}) {
|
|
171
|
-
if (request.dryRun)
|
|
172
|
+
if (request.dryRun || options.lockAlreadyHeld)
|
|
172
173
|
return finalizeReviewSummaryUnlocked(request, options);
|
|
173
174
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
174
175
|
if (!resolved.ok)
|
|
@@ -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,
|