@fitlab-ai/agent-infra 0.9.7 → 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 +5 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +2 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +2 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +30 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
- package/dist/lib/decide.js +68 -14
- package/dist/lib/init.js +1 -1
- 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 +11 -3
- 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/pr-summary.js +5 -3
- package/dist/lib/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +389 -311
- package/dist/lib/sandbox/control/container-identity.js +1 -1
- package/dist/lib/sandbox/control/executor.js +10 -2
- package/dist/lib/sandbox/control/lifecycle.js +392 -72
- package/dist/lib/sandbox/control/state.js +27 -11
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/mounts.js +20 -5
- package/dist/lib/sandbox/recovery.js +78 -10
- package/dist/lib/sandbox/workspace-view.js +62 -40
- package/dist/lib/server/process-state.js +51 -1
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +37 -16
- package/dist/lib/task/events.js +9 -5
- 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/lifecycle.js +69 -14
- package/dist/lib/task/orchestration.js +60 -6
- package/dist/lib/task/review-finalization.js +3 -2
- package/dist/lib/task/verification-engine.js +40 -14
- 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 +3 -3
- package/lib/agent-clients/adapters/claude-code.ts +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +2 -1
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +2 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +29 -2
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
- package/lib/decide.ts +59 -14
- package/lib/init.ts +1 -1
- 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 +12 -4
- 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/pr-summary.ts +10 -3
- package/lib/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +88 -4
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/container-identity.ts +3 -3
- package/lib/sandbox/control/executor.ts +10 -2
- package/lib/sandbox/control/lifecycle.ts +463 -76
- package/lib/sandbox/control/protocol.ts +5 -2
- package/lib/sandbox/control/state.ts +35 -18
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/mounts.ts +23 -5
- package/lib/sandbox/recovery.ts +90 -9
- package/lib/sandbox/workspace-view.ts +70 -40
- package/lib/server/process-state.ts +48 -1
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +46 -18
- package/lib/task/events.ts +10 -5
- 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/lifecycle.ts +71 -14
- package/lib/task/orchestration.ts +89 -7
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/verification-engine.ts +42 -16
- 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/hooks/lifecycle-delegation.js +8 -3
- package/templates/.agents/rules/lifecycle-orchestration.en.md +3 -2
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +3 -2
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -1
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -1
|
@@ -4,6 +4,7 @@ import { declareImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplemen
|
|
|
4
4
|
import { LEDGER_COLUMNS, LEDGER_HEADINGS, nextHdId, parseLedger, validateLedgerRows } from './ledger.js';
|
|
5
5
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
6
6
|
import { writeTask } from './write.js';
|
|
7
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
7
8
|
const PREFIX = { analysis: 'AN', plan: 'PL', code: 'CD' };
|
|
8
9
|
const RESPONSE = new Set(['accepted', 'adjusted', 'refuted', 'cannot-judge']);
|
|
9
10
|
const DISPOSITION = new Set(['confirmed', 'closed', 'open', 'needs-human-decision']);
|
|
@@ -84,7 +85,8 @@ function applyLedgerIntent(intent, options = {}) {
|
|
|
84
85
|
const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
|
|
85
86
|
if (!resolved.ok)
|
|
86
87
|
return failed(intent, resolved.code, resolved.message, resolved.taskId);
|
|
87
|
-
|
|
88
|
+
const stateOverride = allowsManualOverride(options.manualOverride, 'ledger-intent', 'TASK_STATE_MISMATCH');
|
|
89
|
+
if (resolved.state !== 'active' && !stateOverride)
|
|
88
90
|
return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
89
91
|
let content;
|
|
90
92
|
let rows;
|
|
@@ -176,7 +178,8 @@ function applyLedgerIntent(intent, options = {}) {
|
|
|
176
178
|
? new Set(['confirmed', 'open', 'needs-human-decision'])
|
|
177
179
|
: before.status === 'cannot-judge' ? new Set(['open', 'needs-human-decision']) : new Set();
|
|
178
180
|
const sameRoundMinorClose = before.status === 'open' && before.severity === 'minor' && intent.status === 'closed';
|
|
179
|
-
if ((!sameRoundMinorClose && !allowed.has(intent.status)) || (intent.status === 'open' && Number(before.round) >= maxHandshakeRounds(resolved.repoRoot)))
|
|
181
|
+
if (((!sameRoundMinorClose && !allowed.has(intent.status)) || (intent.status === 'open' && Number(before.round) >= maxHandshakeRounds(resolved.repoRoot)))
|
|
182
|
+
&& !allowsManualOverride(options.manualOverride, 'ledger-intent', 'LEDGER_TRANSITION_INVALID')) {
|
|
180
183
|
return failed(intent, 'LEDGER_TRANSITION_INVALID', `finding '${intent.id}' cannot transition from ${before.status} to ${intent.status}`, resolved.taskId, intent.id);
|
|
181
184
|
}
|
|
182
185
|
after = { ...before, status: intent.status, evidence };
|
|
@@ -209,7 +212,7 @@ function applyLedgerIntent(intent, options = {}) {
|
|
|
209
212
|
}
|
|
210
213
|
}
|
|
211
214
|
const result = writeTask({
|
|
212
|
-
taskRef: intent.taskRef, expectedState: 'active',
|
|
215
|
+
taskRef: intent.taskRef, expectedState: stateOverride ? resolved.state : 'active',
|
|
213
216
|
mutations: [...ledgerSectionMutation(content), rowMutation(after), ...(implementationMutation ? [implementationMutation] : [])],
|
|
214
217
|
dryRun: 'dryRun' in intent ? intent.dryRun : false
|
|
215
218
|
}, { ...options, taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state } });
|
|
@@ -10,6 +10,36 @@ import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
|
10
10
|
const lifecycleIntentCatalog = [
|
|
11
11
|
'block', 'activate', 'cancel', 'complete', 'close-codescan', 'close-dependabot', 'restore'
|
|
12
12
|
];
|
|
13
|
+
const lifecycleFailureCatalog = [
|
|
14
|
+
'LIFECYCLE_DIRECTORY_RENAME_FAILED',
|
|
15
|
+
'LIFECYCLE_DOCUMENT_INVALID',
|
|
16
|
+
'LIFECYCLE_FINAL_STATE_INVALID',
|
|
17
|
+
'LIFECYCLE_IDENTITY_CONFLICT',
|
|
18
|
+
'LIFECYCLE_IDENTITY_INVALID',
|
|
19
|
+
'LIFECYCLE_INTENT_CONFLICT',
|
|
20
|
+
'LIFECYCLE_JOURNAL_CLEANUP_FAILED',
|
|
21
|
+
'LIFECYCLE_JOURNAL_INVALID',
|
|
22
|
+
'LIFECYCLE_JOURNAL_WRITE_FAILED',
|
|
23
|
+
'LIFECYCLE_LOG_MISSING',
|
|
24
|
+
'LIFECYCLE_METADATA_FAILED',
|
|
25
|
+
'LIFECYCLE_PAYLOAD_INVALID',
|
|
26
|
+
'LIFECYCLE_SHORT_ID_FAILED',
|
|
27
|
+
'LIFECYCLE_SHORT_ID_PRECONDITION',
|
|
28
|
+
'LIFECYCLE_SOURCE_INVALID',
|
|
29
|
+
'LIFECYCLE_STAGING_IDENTITY_INVALID',
|
|
30
|
+
'LIFECYCLE_STAGING_INVALID',
|
|
31
|
+
'LIFECYCLE_TARGET_CONFLICT',
|
|
32
|
+
'LIFECYCLE_TASK_NOT_FOUND',
|
|
33
|
+
'SHORT_ID_CAPACITY_EXCEEDED'
|
|
34
|
+
];
|
|
35
|
+
const lifecycleProducerCatalog = lifecycleFailureCatalog.map((code) => ({
|
|
36
|
+
producerId: 'lifecycle.apply',
|
|
37
|
+
guardId: 'G-03',
|
|
38
|
+
code
|
|
39
|
+
}));
|
|
40
|
+
function allowsManualOverride(options, code) {
|
|
41
|
+
return options.manualOverride?.failureId === `lifecycle.apply:${code}`;
|
|
42
|
+
}
|
|
13
43
|
const STEPS = ['task-written', 'directory-moved', 'registry-committed'];
|
|
14
44
|
const DEFAULT_IO = {
|
|
15
45
|
renameSync: (source, target) => fs.renameSync(source, target),
|
|
@@ -121,10 +151,17 @@ function matchingCompletion(content, request, restoredFiles = 0) {
|
|
|
121
151
|
return section.entries.some((entry) => entry.step === identity.action && entry.agent === request.agent && entry.note === identity.note);
|
|
122
152
|
}
|
|
123
153
|
function validateRestoreStaging(request, repoRoot, taskId) {
|
|
124
|
-
|
|
125
|
-
|
|
154
|
+
let workspace;
|
|
155
|
+
let stagingDir;
|
|
156
|
+
try {
|
|
157
|
+
workspace = fs.realpathSync.native(path.resolve(repoRoot, '.agents', 'workspace'));
|
|
158
|
+
stagingDir = fs.realpathSync.native(path.resolve(request.stagingDir));
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
return { code: 'LIFECYCLE_STAGING_INVALID', message: String(error) };
|
|
162
|
+
}
|
|
126
163
|
const relative = path.relative(workspace, stagingDir);
|
|
127
|
-
if (!relative || relative.startsWith(
|
|
164
|
+
if (!relative || relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) || path.basename(stagingDir) === taskId) {
|
|
128
165
|
return { code: 'LIFECYCLE_STAGING_INVALID', message: 'restore staging must be a non-final directory inside .agents/workspace' };
|
|
129
166
|
}
|
|
130
167
|
let entries;
|
|
@@ -168,8 +205,8 @@ function validateRestoreStaging(request, repoRoot, taskId) {
|
|
|
168
205
|
}
|
|
169
206
|
return { stagingDir, fileCount: entries.filter((entry) => !entry.name.startsWith('.')).length };
|
|
170
207
|
}
|
|
171
|
-
function mutationsFor(request, content, metadata, restoredFiles) {
|
|
172
|
-
const section = locateActivityLog(content);
|
|
208
|
+
function mutationsFor(request, content, metadata, restoredFiles, allowMissingActivityLog = false) {
|
|
209
|
+
const section = locateActivityLog(content) ?? (allowMissingActivityLog ? { heading: 'Activity Log', body: '', entries: [] } : null);
|
|
173
210
|
if (!section)
|
|
174
211
|
return { code: 'LIFECYCLE_LOG_MISSING', message: 'task has no unique Activity Log section' };
|
|
175
212
|
const identity = actionAndNote(request, restoredFiles);
|
|
@@ -230,6 +267,14 @@ function writeJournal(file, journal, io, create = false) {
|
|
|
230
267
|
throw error;
|
|
231
268
|
}
|
|
232
269
|
}
|
|
270
|
+
function rememberJournalFailure(file, journal, io, failure) {
|
|
271
|
+
try {
|
|
272
|
+
writeJournal(file, { ...journal, failure }, io);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// Preserve the original lifecycle failure when the diagnostic receipt cannot be updated.
|
|
276
|
+
}
|
|
277
|
+
}
|
|
233
278
|
function parseJournal(content) {
|
|
234
279
|
const value = JSON.parse(content);
|
|
235
280
|
if (value.version !== 1 || typeof value.taskId !== 'string' ||
|
|
@@ -303,7 +348,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
303
348
|
}
|
|
304
349
|
return failed(request, { code: 'LIFECYCLE_INTENT_CONFLICT', message: `task ${taskId} is already ${spec.target} with a different lifecycle intent` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
305
350
|
}
|
|
306
|
-
if (!spec.sources.includes(sourceState) && !existingJournal) {
|
|
351
|
+
if (!spec.sources.includes(sourceState) && !existingJournal && !allowsManualOverride(options, 'LIFECYCLE_SOURCE_INVALID')) {
|
|
307
352
|
return failed(request, { code: 'LIFECYCLE_SOURCE_INVALID', message: `${request.intent} is not allowed from ${sourceState}` }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
308
353
|
}
|
|
309
354
|
if (sourcePath !== targetPath && fs.existsSync(targetPath)) {
|
|
@@ -319,7 +364,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
319
364
|
}
|
|
320
365
|
if (frontmatter.id !== taskId)
|
|
321
366
|
return failed(request, { code: 'LIFECYCLE_IDENTITY_INVALID', message: 'task.md id does not match its lifecycle task id' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
322
|
-
if (!locateActivityLog(content))
|
|
367
|
+
if (!locateActivityLog(content) && !allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'))
|
|
323
368
|
return failed(request, { code: 'LIFECYCLE_LOG_MISSING', message: 'task has no unique Activity Log section' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
324
369
|
const registryRequest = executeShortIdCommand({
|
|
325
370
|
operation: 'list', activeDir: path.join(repoRoot, '.agents', 'workspace', 'active'),
|
|
@@ -330,10 +375,10 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
330
375
|
}
|
|
331
376
|
const registry = JSON.parse(registryRequest.output || '{"version":1,"ids":{}}');
|
|
332
377
|
const registeredKey = Object.entries(registry.ids).find(([, candidate]) => candidate === taskId)?.[0];
|
|
333
|
-
if (spec.registry === 'release' && sourceState === 'active' && !registeredKey) {
|
|
378
|
+
if (spec.registry === 'release' && sourceState === 'active' && !registeredKey && !allowsManualOverride(options, 'LIFECYCLE_SHORT_ID_PRECONDITION')) {
|
|
334
379
|
return failed(request, { code: 'LIFECYCLE_SHORT_ID_PRECONDITION', message: 'active source has no short-id registry entry' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
335
380
|
}
|
|
336
|
-
if (spec.registry === 'alloc' && !registeredKey && Object.keys(registry.ids).length >= 10 ** configuredShortIdLength(repoRoot) - 1) {
|
|
381
|
+
if (spec.registry === 'alloc' && !registeredKey && Object.keys(registry.ids).length >= 10 ** configuredShortIdLength(repoRoot) - 1 && !allowsManualOverride(options, 'SHORT_ID_CAPACITY_EXCEEDED')) {
|
|
337
382
|
return failed(request, { code: 'SHORT_ID_CAPACITY_EXCEEDED', message: 'short-id registry has no free slot' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
338
383
|
}
|
|
339
384
|
const digest = intentIdentity(request, taskId);
|
|
@@ -347,7 +392,15 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
347
392
|
catch (error) {
|
|
348
393
|
return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: String(error) }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
349
394
|
}
|
|
350
|
-
if (journal.
|
|
395
|
+
if (journal.taskId !== taskId || journal.intent !== request.intent)
|
|
396
|
+
return failed(request, { code: 'LIFECYCLE_INTENT_CONFLICT', message: 'an in-progress lifecycle journal belongs to a different request' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
397
|
+
if (request.dryRun) {
|
|
398
|
+
if (!journal.failure)
|
|
399
|
+
return failed(request, { code: 'LIFECYCLE_JOURNAL_INVALID', message: 'existing lifecycle journal has no durable failure evidence for a read-only probe' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
400
|
+
return failed(request, journal.failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath, completedSteps: journal.completedSteps, pendingSteps: STEPS.filter((step) => !journal.completedSteps.includes(step)) });
|
|
401
|
+
}
|
|
402
|
+
const overrideResumesRecordedFailure = journal.failure && allowsManualOverride(options, journal.failure.code);
|
|
403
|
+
if (journal.intentDigest !== digest && !overrideResumesRecordedFailure)
|
|
351
404
|
return failed(request, { code: 'LIFECYCLE_INTENT_CONFLICT', message: 'an in-progress lifecycle journal belongs to a different request' }, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath: initialJournalPath });
|
|
352
405
|
}
|
|
353
406
|
else {
|
|
@@ -360,7 +413,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
360
413
|
}
|
|
361
414
|
journal = { version: 1, taskId, intent: request.intent, intentDigest: digest, sourceState, targetState: spec.target, metadata, completedSteps: [] };
|
|
362
415
|
if (request.dryRun) {
|
|
363
|
-
const plannedMutations = mutationsFor(request, content, metadata, restoredFiles);
|
|
416
|
+
const plannedMutations = mutationsFor(request, content, metadata, restoredFiles, allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'));
|
|
364
417
|
if ('code' in plannedMutations)
|
|
365
418
|
return failed(request, plannedMutations, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath });
|
|
366
419
|
const taskPlan = writeTask({ taskRef: taskId, expectedState: sourceState === 'blocked' ? 'blocked' : 'active', mutations: plannedMutations, dryRun: true }, {
|
|
@@ -402,7 +455,7 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
402
455
|
completed.add('task-written');
|
|
403
456
|
if (!completed.has('task-written')) {
|
|
404
457
|
const current = io.readFileSync(path.join(sourcePath, 'task.md'));
|
|
405
|
-
const mutations = mutationsFor(request, current, journal.metadata, restoredFiles);
|
|
458
|
+
const mutations = mutationsFor(request, current, journal.metadata, restoredFiles, allowsManualOverride(options, 'LIFECYCLE_LOG_MISSING'));
|
|
406
459
|
if ('code' in mutations)
|
|
407
460
|
return failed(request, mutations, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, completedSteps: [...completed], changed: completed.size > 0 });
|
|
408
461
|
const writeResult = writeTask({ taskRef: taskId, expectedState: sourceState === 'blocked' ? 'blocked' : 'active', mutations }, {
|
|
@@ -432,7 +485,9 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
432
485
|
(options.directoryRenameSync ?? io.renameSync)(sourcePath, targetPath);
|
|
433
486
|
}
|
|
434
487
|
catch (error) {
|
|
435
|
-
|
|
488
|
+
const failure = { code: 'LIFECYCLE_DIRECTORY_RENAME_FAILED', message: String(error) };
|
|
489
|
+
rememberJournalFailure(journalPath, journal, io, failure);
|
|
490
|
+
return failed(request, failure, { taskId, sourceState, targetState: spec.target, sourcePath, targetPath, journalPath, task: { operations: taskOperations }, changed: true, completedSteps: [...completed], timestamp: journal.metadata.timestamp, agentInfraVersion: journal.metadata.agentInfraVersion });
|
|
436
491
|
}
|
|
437
492
|
completed.add('directory-moved');
|
|
438
493
|
journal.completedSteps = [...completed];
|
|
@@ -480,5 +535,5 @@ function applyTaskLifecycle(requestInput, options = {}) {
|
|
|
480
535
|
journalPath, completedSteps: [...completed], pendingSteps: []
|
|
481
536
|
};
|
|
482
537
|
}
|
|
483
|
-
export { lifecycleIntentCatalog, applyTaskLifecycle };
|
|
538
|
+
export { lifecycleIntentCatalog, lifecycleFailureCatalog, lifecycleProducerCatalog, applyTaskLifecycle };
|
|
484
539
|
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -7,8 +7,9 @@ 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, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, 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';
|
|
@@ -176,6 +177,22 @@ function canRecoverCodexUnsupportedPause(run, taskDir) {
|
|
|
176
177
|
&& !fs.existsSync(commitIntentPath(taskDir))
|
|
177
178
|
&& (run.recoveryHistory ?? []).every((entry) => entry.code === 'MODEL_POLICY_SUPPLEMENTED');
|
|
178
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
|
+
}
|
|
179
196
|
function canMigrateRecoverableV2Pause(run, taskDir) {
|
|
180
197
|
return run.schemaVersion === 2
|
|
181
198
|
&& run.status === 'paused'
|
|
@@ -239,6 +256,39 @@ function beginOrResumeOrchestration(taskRef, options = {}) {
|
|
|
239
256
|
}
|
|
240
257
|
}
|
|
241
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
|
+
}
|
|
242
292
|
if (!canRecoverCodexUnsupportedPause(existing, resolved.taskDir)) {
|
|
243
293
|
return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
|
|
244
294
|
}
|
|
@@ -1240,9 +1290,9 @@ function activateMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
|
1240
1290
|
return matched;
|
|
1241
1291
|
const receipt = replay.run.pendingDelegation;
|
|
1242
1292
|
const sameEvidence = receipt.parentId === event.parentId
|
|
1243
|
-
&& receipt.spawnMode === event.spawnMode
|
|
1244
|
-
&& receipt.actualModel === event.actualModel
|
|
1245
|
-
&& 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)
|
|
1246
1296
|
&& receipt.modelFallbackReason === (event.modelFallbackReason ?? null)
|
|
1247
1297
|
&& receipt.reasoningEffortFallbackReason === (event.reasoningEffortFallbackReason ?? null)
|
|
1248
1298
|
&& receipt.hostEvidence?.hookDefinitionHash === event.hostEvidence?.hookDefinitionHash;
|
|
@@ -1287,7 +1337,11 @@ function sealMatchingOrchestrationDelegation(client, event, options = {}) {
|
|
|
1287
1337
|
childId: event.childId,
|
|
1288
1338
|
exitCode: 0,
|
|
1289
1339
|
afterFingerprint,
|
|
1290
|
-
changedPaths
|
|
1340
|
+
changedPaths,
|
|
1341
|
+
actualModel: event.actualModel,
|
|
1342
|
+
actualReasoningEffort: event.actualReasoningEffort,
|
|
1343
|
+
modelFallbackReason: event.modelFallbackReason,
|
|
1344
|
+
reasoningEffortFallbackReason: event.reasoningEffortFallbackReason
|
|
1291
1345
|
}, options);
|
|
1292
1346
|
}
|
|
1293
1347
|
catch (error) {
|
|
@@ -1440,7 +1494,7 @@ function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
|
|
|
1440
1494
|
const activeLifecycleEvidence = options.hasActiveLifecycleEvidence?.(receipt)
|
|
1441
1495
|
?? (receipt.client === 'codex'
|
|
1442
1496
|
&& receipt.lifecycleProvenance
|
|
1443
|
-
&& hasActiveCodexLifecycleEvidence(
|
|
1497
|
+
&& hasActiveCodexLifecycleEvidence(resolveAgentRuntimeStoreRoot({ repoRoot: resolved.repoRoot, store: 'lifecycle' }), {
|
|
1444
1498
|
nativeAgent: `agent-infra-lifecycle-${receipt.role}`,
|
|
1445
1499
|
hookDefinitionHash: receipt.lifecycleProvenance.hookDefinitionHash
|
|
1446
1500
|
}));
|
|
@@ -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)
|
|
@@ -261,7 +261,22 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
261
261
|
&& guards?.commitAuthorizationUnused === true
|
|
262
262
|
&& guards?.completionEvidenceAbsent === true
|
|
263
263
|
&& guards?.commitIntentAbsent === true;
|
|
264
|
-
|
|
264
|
+
const validClaudeCodeRecovery = recovery.code === 'CLIENT_CAPABILITY_ENABLED_NO_MIGRATION'
|
|
265
|
+
&& recovery.previousSchemaVersion === 3
|
|
266
|
+
&& recovery.previousStatus === 'paused'
|
|
267
|
+
&& recovery.previousPause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
|
|
268
|
+
&& recovery.client === 'claude-code'
|
|
269
|
+
&& recovery.resultingStatus === 'running'
|
|
270
|
+
&& exactText(recovery.recoveredAt)
|
|
271
|
+
&& guards?.stepCount === 0
|
|
272
|
+
&& guards?.nextStage === null
|
|
273
|
+
&& guards?.baselineEmpty === true
|
|
274
|
+
&& guards?.receiptCount === 0
|
|
275
|
+
&& guards?.pendingDelegation === false
|
|
276
|
+
&& guards?.commitAuthorizationUnused === true
|
|
277
|
+
&& guards?.completionEvidenceAbsent === true
|
|
278
|
+
&& guards?.commitIntentAbsent === true;
|
|
279
|
+
if (!validModelRecovery && !validCapabilityRecovery && !validClaudeCodeRecovery) {
|
|
265
280
|
return failResult('orchestration-evidence', 'Run recovery history contains invalid provenance');
|
|
266
281
|
}
|
|
267
282
|
}
|
|
@@ -292,26 +307,37 @@ function checkOrchestrationEvidence({ taskDir }) {
|
|
|
292
307
|
}
|
|
293
308
|
const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
|
|
294
309
|
if (activated) {
|
|
295
|
-
|
|
296
|
-
|
|
310
|
+
const isClaudeCode = receipt.client === 'claude-code';
|
|
311
|
+
if (!isClaudeCode) {
|
|
312
|
+
if (!exactText(receipt.actualModel)) {
|
|
313
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual model`);
|
|
314
|
+
}
|
|
315
|
+
if (!exactText(receipt.actualReasoningEffort)) {
|
|
316
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has no host-observed actual reasoning effort`);
|
|
317
|
+
}
|
|
297
318
|
}
|
|
298
|
-
|
|
299
|
-
|
|
319
|
+
// parentId/childId 校验对所有 client 保持统一(不新增分支);spawnMode 检查按 client 判断
|
|
320
|
+
if (!exactText(receipt.parentId) || !exactText(receipt.childId) || receipt.parentId === receipt.childId) {
|
|
321
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid delegation identity`);
|
|
300
322
|
}
|
|
301
|
-
if (!
|
|
323
|
+
if (!isClaudeCode && receipt.spawnMode !== 'fresh') {
|
|
302
324
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has invalid fresh delegation identity`);
|
|
303
325
|
}
|
|
304
|
-
if (receipt.actualModel
|
|
305
|
-
|
|
326
|
+
if (receipt.actualModel != null && receipt.actualModel !== receipt.requestedModel) {
|
|
327
|
+
if (!isClaudeCode && !exactText(receipt.modelFallbackReason)) {
|
|
328
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' model fallback is not justified`);
|
|
329
|
+
}
|
|
306
330
|
}
|
|
307
|
-
if (receipt.actualModel
|
|
331
|
+
if ((receipt.actualModel == null || receipt.actualModel === receipt.requestedModel)
|
|
332
|
+
&& receipt.modelFallbackReason !== null) {
|
|
308
333
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated model fallback reason`);
|
|
309
334
|
}
|
|
310
|
-
if (receipt.actualReasoningEffort !== receipt.requestedReasoningEffort
|
|
311
|
-
&& !exactText(receipt.reasoningEffortFallbackReason)) {
|
|
312
|
-
|
|
335
|
+
if (receipt.actualReasoningEffort != null && receipt.actualReasoningEffort !== receipt.requestedReasoningEffort) {
|
|
336
|
+
if (!isClaudeCode && !exactText(receipt.reasoningEffortFallbackReason)) {
|
|
337
|
+
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' reasoning-effort fallback is not justified`);
|
|
338
|
+
}
|
|
313
339
|
}
|
|
314
|
-
if (receipt.actualReasoningEffort === receipt.requestedReasoningEffort
|
|
340
|
+
if ((receipt.actualReasoningEffort == null || receipt.actualReasoningEffort === receipt.requestedReasoningEffort)
|
|
315
341
|
&& receipt.reasoningEffortFallbackReason !== null) {
|
|
316
342
|
return failResult('orchestration-evidence', `Receipt '${receipt.id ?? '(unknown)'}' has an unrelated reasoning-effort fallback reason`);
|
|
317
343
|
}
|
|
@@ -774,7 +800,7 @@ function checkReviewLedger({ taskDir, config }) {
|
|
|
774
800
|
return passResult("review-ledger", "Disagreement ledger has no entries");
|
|
775
801
|
}
|
|
776
802
|
const stageScope = Array.isArray(config.stage_scope) ? config.stage_scope : null;
|
|
777
|
-
const maxRounds = Number(resolveReviewSetting(config, "
|
|
803
|
+
const maxRounds = Number(resolveReviewSetting(config, "maxHandshakeRounds", DEFAULT_MAX_HANDSHAKE_ROUNDS));
|
|
778
804
|
const problems = [];
|
|
779
805
|
let inScopeCount = 0;
|
|
780
806
|
for (const cells of rows) {
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { verifyInProcess } from './verification-engine.js';
|
|
5
5
|
import { parseArtifactName } from './artifact-lifecycle.js';
|
|
6
6
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
7
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
7
8
|
const gate = (skill, expectedState, artifactFamily) => ({
|
|
8
9
|
skill, expectedState, mode: 'gate', ...(artifactFamily ? { artifactFamily } : {})
|
|
9
10
|
});
|
|
@@ -47,7 +48,7 @@ function verifyTaskEvent(request, options = {}) {
|
|
|
47
48
|
if (!resolved.ok)
|
|
48
49
|
return failure(request, resolved.code, resolved.message, { taskId: resolved.taskId, skill: spec.skill, mode: spec.mode });
|
|
49
50
|
const identity = { taskId: resolved.taskId, taskDir: resolved.taskDir, taskState: resolved.state, skill: spec.skill, mode: spec.mode };
|
|
50
|
-
if (resolved.state !== spec.expectedState) {
|
|
51
|
+
if (resolved.state !== spec.expectedState && !allowsManualOverride(options.manualOverride, 'task-verify', 'VERIFY_TASK_STATE_MISMATCH')) {
|
|
51
52
|
return failure(request, 'VERIFY_TASK_STATE_MISMATCH', `event '${request.event}' requires workspace '${spec.expectedState}', received '${resolved.state}'`, identity);
|
|
52
53
|
}
|
|
53
54
|
if (spec.artifactFamily) {
|
|
@@ -66,12 +67,23 @@ function verifyTaskEvent(request, options = {}) {
|
|
|
66
67
|
if (spec.mode === 'gate') {
|
|
67
68
|
const payload = engine({ mode: 'gate', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [], repositoryRoot: resolved.repoRoot });
|
|
68
69
|
const status = payload.gate;
|
|
70
|
+
if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
|
|
71
|
+
invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, gate: 'pass', humanOverride: 'human-approved', originalGate: status } });
|
|
72
|
+
return {
|
|
73
|
+
status: 'pass', changed: false, event: request.event, requestRef: request.taskRef,
|
|
74
|
+
...identity, artifact: request.artifact ?? null, invocations, error: null
|
|
75
|
+
};
|
|
76
|
+
}
|
|
69
77
|
invocations.push({ status, exitCode: { pass: 0, fail: 1, blocked: 2 }[status], payload });
|
|
70
78
|
}
|
|
71
79
|
else {
|
|
72
80
|
for (const check of spec.checks ?? []) {
|
|
73
81
|
const payload = engine({ mode: 'checks', skillName: spec.skill, taskDir: resolved.taskDir, artifactFile: request.artifact, checks: [check], repositoryRoot: resolved.repoRoot });
|
|
74
82
|
const status = payload.status;
|
|
83
|
+
if (status !== 'pass' && allowsManualOverride(options.manualOverride, 'verification-engine', status === 'blocked' ? 'CHECK_BLOCKED' : 'CHECK_FAILED')) {
|
|
84
|
+
invocations.push({ status: 'pass', exitCode: 0, payload: { ...payload, status: 'pass', humanOverride: 'human-approved', originalStatus: status } });
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
75
87
|
invocations.push({ status, exitCode: { pass: 0, fail: 1, blocked: 2 }[status], payload });
|
|
76
88
|
if (status !== 'pass')
|
|
77
89
|
break;
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
3
3
|
import { WORKFLOW_WARNING_COLUMNS, WORKFLOW_WARNING_HEADINGS, WORKFLOW_WARNING_SEVERITIES, WORKFLOW_WARNING_STATUSES, parseWorkflowWarnings } from './workflow-warnings.js';
|
|
4
4
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
5
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
5
6
|
function failed(intent, code, message, taskId = null, entityId = null) {
|
|
6
7
|
return { status: 'failed', changed: false, intent: intent.kind, taskId, entityId, before: null, after: null, warnings: [], operations: [], error: { code, message } };
|
|
7
8
|
}
|
|
@@ -54,7 +55,8 @@ function applyWorkflowWarningIntent(intent, options = {}) {
|
|
|
54
55
|
const resolved = resolveTaskRef(intent.taskRef, { repoRoot: options.repoRoot });
|
|
55
56
|
if (!resolved.ok)
|
|
56
57
|
return failed(intent, resolved.code, resolved.message, resolved.taskId);
|
|
57
|
-
|
|
58
|
+
const stateOverride = allowsManualOverride(options.manualOverride, 'workflow-warning', 'TASK_STATE_MISMATCH');
|
|
59
|
+
if (resolved.state !== 'active' && !stateOverride)
|
|
58
60
|
return failed(intent, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
59
61
|
let content;
|
|
60
62
|
let rows;
|
|
@@ -114,7 +116,7 @@ function applyWorkflowWarningIntent(intent, options = {}) {
|
|
|
114
116
|
after = { ...before, status: intent.status, resolvedAt: metadata.timestamp, resolution };
|
|
115
117
|
}
|
|
116
118
|
const writeResult = writeTask({
|
|
117
|
-
taskRef: intent.taskRef, expectedState: 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
|
|
119
|
+
taskRef: intent.taskRef, expectedState: stateOverride ? resolved.state : 'active', mutations: [...sectionMutation(content), rowMutation(after)], dryRun: intent.dryRun
|
|
118
120
|
}, {
|
|
119
121
|
...options,
|
|
120
122
|
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
package/dist/lib/task/write.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { VERSION } from '../version.js';
|
|
4
4
|
import { parseTypedTaskFrontmatter, updateTaskFrontmatter } from './frontmatter.js';
|
|
5
5
|
import { resolveTaskRef } from './resolve-ref.js';
|
|
6
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
6
7
|
import { mutateTableRow, upsertSection } from './sections.js';
|
|
7
8
|
const DEFAULT_FILE_SYSTEM = {
|
|
8
9
|
readFileSync: (file) => fs.readFileSync(file, 'utf8'),
|
|
@@ -70,7 +71,7 @@ function writeTask(request, options = {}) {
|
|
|
70
71
|
taskMdPath: resolved.taskMdPath,
|
|
71
72
|
actualState: resolved.state
|
|
72
73
|
};
|
|
73
|
-
if (resolved.state !== request.expectedState) {
|
|
74
|
+
if (resolved.state !== request.expectedState && !allowsManualOverride(options.manualOverride, 'task.write', 'TASK_STATE_MISMATCH')) {
|
|
74
75
|
return failure(request, identity, 'TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected ${request.expectedState}`);
|
|
75
76
|
}
|
|
76
77
|
const io = { ...DEFAULT_FILE_SYSTEM, ...options.fileSystem };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"protocolVersion": 3,
|
|
3
|
-
"packageVersion": "0.9.
|
|
4
|
-
"sourceInputHash": "
|
|
5
|
-
"internalExecutableBuildHash": "
|
|
3
|
+
"packageVersion": "0.9.8",
|
|
4
|
+
"sourceInputHash": "fb481e6579519b3fd10c9ca9bf284d65498f935c034a14c047d023345abeb87c",
|
|
5
|
+
"internalExecutableBuildHash": "d8dcdf602b61001ba9829aa2027cccadcc9c371ccf9e29c3d681fd02d5a3a5af"
|
|
6
6
|
}
|
|
@@ -16,7 +16,7 @@ const claudeCodeAdapter = defineAgentClientAdapter({
|
|
|
16
16
|
commands: { level: 'integrated' },
|
|
17
17
|
hooks: { level: 'integrated' },
|
|
18
18
|
subagents: { level: 'experimental' },
|
|
19
|
-
orchestration: { level: '
|
|
19
|
+
orchestration: { level: 'experimental' },
|
|
20
20
|
sandbox: { level: 'integrated' },
|
|
21
21
|
verification: { level: 'compatible' }
|
|
22
22
|
},
|
|
@@ -26,8 +26,8 @@ const claudeCodeAdapter = defineAgentClientAdapter({
|
|
|
26
26
|
guidance: 'Use the host model picker for the complete model and reasoning-effort catalog.'
|
|
27
27
|
},
|
|
28
28
|
delegationEvidence: {
|
|
29
|
-
actualModel: '
|
|
30
|
-
actualReasoningEffort: '
|
|
29
|
+
actualModel: 'host-event',
|
|
30
|
+
actualReasoningEffort: 'spawn-ack'
|
|
31
31
|
},
|
|
32
32
|
project: {
|
|
33
33
|
ownedPathPrefixes: ['.claude/'],
|
|
@@ -7,6 +7,7 @@ import semver from 'semver';
|
|
|
7
7
|
|
|
8
8
|
import type { ChildProcessWithoutNullStreams } from 'node:child_process';
|
|
9
9
|
import type { CodexLifecycleEvent } from './evidence.ts';
|
|
10
|
+
import { resolveAgentRuntimeStoreRoot } from '../../../runtime/agent-runtime.ts';
|
|
10
11
|
|
|
11
12
|
type JsonObject = Record<string, unknown>;
|
|
12
13
|
type ThreadResolution = Readonly<{
|
|
@@ -558,7 +559,7 @@ async function preflightCodexLifecycleEvidence(
|
|
|
558
559
|
}
|
|
559
560
|
|
|
560
561
|
const hookDefinitionHash = crypto.createHash('sha256').update(hooksRaw).digest('hex');
|
|
561
|
-
const runtimeRoot =
|
|
562
|
+
const runtimeRoot = resolveAgentRuntimeStoreRoot({ repoRoot, store: 'lifecycle' });
|
|
562
563
|
const runtimeLiveness = hasCodexRuntimeLiveness(runtimeRoot, hookDefinitionHash, runtimeIdentity);
|
|
563
564
|
|
|
564
565
|
const transport = new CodexAppServerTransport();
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
|
|
5
5
|
import { verifyLifecycleBuildIdentity } from './build-identity.ts';
|
|
6
6
|
import type { LifecycleBuildIdentity } from './build-identity.ts';
|
|
7
|
+
import { resolveAgentRuntimeStoreRoot } from '../../../runtime/agent-runtime.ts';
|
|
7
8
|
|
|
8
9
|
type CapabilityStatus = 'armed' | 'attested' | 'consumed' | 'expired';
|
|
9
10
|
type CodexControllerBinding = Readonly<{
|
|
@@ -65,7 +66,7 @@ function sameController(
|
|
|
65
66
|
|
|
66
67
|
function createCodexCapabilityStore(options: CodexCapabilityStoreOptions = {}) {
|
|
67
68
|
const root = options.root
|
|
68
|
-
??
|
|
69
|
+
?? resolveAgentRuntimeStoreRoot({ store: 'capabilities' });
|
|
69
70
|
const now = options.now ?? Date.now;
|
|
70
71
|
const createToken = options.token ?? (() => crypto.randomBytes(32).toString('base64url'));
|
|
71
72
|
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|