@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
|
@@ -6,6 +6,7 @@ import { LEDGER_COLUMNS, LEDGER_HEADINGS, parseLedger } from './ledger.js';
|
|
|
6
6
|
import { resolveTaskContext } from './resolve-ref.js';
|
|
7
7
|
import { extractSection, extractSubSection, findSectionHeading } from './sections.js';
|
|
8
8
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
9
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
9
10
|
const DECISION_ALIASES = ['人工裁决', 'Human Rulings', 'Human Decisions', 'Human Decision'];
|
|
10
11
|
const ACTIVITY_ALIASES = ['活动日志', 'Activity Log'];
|
|
11
12
|
function failed(code, message, taskId = null, ledgerId = null) {
|
|
@@ -56,7 +57,8 @@ function applyHumanDecision(request, options = {}) {
|
|
|
56
57
|
const resolved = resolveTaskContext(request.taskRef, { repoRoot: options.repoRoot });
|
|
57
58
|
if (!resolved.ok)
|
|
58
59
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
59
|
-
|
|
60
|
+
const stateOverride = allowsManualOverride(options.manualOverride, 'decision-intent', 'TASK_STATE_MISMATCH');
|
|
61
|
+
if (resolved.state !== 'active' && !stateOverride)
|
|
60
62
|
return failed('TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
61
63
|
let content;
|
|
62
64
|
let rows;
|
|
@@ -155,7 +157,7 @@ function applyHumanDecision(request, options = {}) {
|
|
|
155
157
|
kind: 'section', aliases: ACTIVITY_ALIASES, heading: activity.heading,
|
|
156
158
|
body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Human Decision', agent: 'human', note: `${row.id} decided → ${recordId}` })
|
|
157
159
|
});
|
|
158
|
-
const writeResult = writeTask({ taskRef: resolved.taskId, expectedState: 'active', mutations, dryRun: request.dryRun }, {
|
|
160
|
+
const writeResult = writeTask({ taskRef: resolved.taskId, expectedState: stateOverride ? resolved.state : 'active', mutations, dryRun: request.dryRun }, {
|
|
159
161
|
...options,
|
|
160
162
|
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
|
161
163
|
metadataProvider: () => metadata
|
|
@@ -10,7 +10,12 @@ function managedDelegationRole(nativeAgent) {
|
|
|
10
10
|
function fail(code, message) {
|
|
11
11
|
return { ok: false, code, message };
|
|
12
12
|
}
|
|
13
|
+
// 空白串折叠为 null,保持"缺失即 null"的单一表示(Start/Stop 两处共用,避免任一入口把 " " 当成已观察到的值入账)
|
|
14
|
+
function foldBlankToNull(value) {
|
|
15
|
+
return value?.trim() ? value : null;
|
|
16
|
+
}
|
|
13
17
|
function prepareDelegation(input, options = {}) {
|
|
18
|
+
const monotonic = (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1000000n)))();
|
|
14
19
|
return Object.freeze({
|
|
15
20
|
...input,
|
|
16
21
|
id: (options.id ?? randomUUID)(),
|
|
@@ -27,43 +32,97 @@ function prepareDelegation(input, options = {}) {
|
|
|
27
32
|
afterFingerprint: null,
|
|
28
33
|
changedPaths: Object.freeze([]),
|
|
29
34
|
createdAt: (options.now ?? (() => new Date().toISOString()))(),
|
|
35
|
+
preparedMonotonicMs: monotonic,
|
|
36
|
+
spawnDispatchMonotonicMs: null,
|
|
37
|
+
activationDeadlineMonotonicMs: null,
|
|
38
|
+
spawnDispatchedAt: null,
|
|
39
|
+
activationDeadlineAt: null,
|
|
40
|
+
startEvidenceMonotonicMs: null,
|
|
41
|
+
activatedMonotonicMs: null,
|
|
30
42
|
activatedAt: null,
|
|
31
43
|
sealedAt: null,
|
|
32
44
|
consumedAt: null
|
|
33
45
|
});
|
|
34
46
|
}
|
|
47
|
+
function dispatchDelegation(receipt, options = {}) {
|
|
48
|
+
if (receipt.status !== 'prepared') {
|
|
49
|
+
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected prepared`);
|
|
50
|
+
}
|
|
51
|
+
if (receipt.spawnDispatchMonotonicMs != null || receipt.spawnDispatchedAt != null) {
|
|
52
|
+
return fail('DELEGATION_DISPATCH_REPLAY', `delegation ${receipt.id} was already dispatched`);
|
|
53
|
+
}
|
|
54
|
+
const monotonic = (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1000000n)))();
|
|
55
|
+
const dispatchedAt = (options.now ?? (() => new Date().toISOString()))();
|
|
56
|
+
const window = options.activationWindowMs ?? 15_000;
|
|
57
|
+
return { ok: true, receipt: Object.freeze({
|
|
58
|
+
...receipt,
|
|
59
|
+
spawnDispatchMonotonicMs: monotonic,
|
|
60
|
+
activationDeadlineMonotonicMs: monotonic + window,
|
|
61
|
+
spawnDispatchedAt: dispatchedAt,
|
|
62
|
+
activationDeadlineAt: new Date(Date.parse(dispatchedAt) + window).toISOString()
|
|
63
|
+
}) };
|
|
64
|
+
}
|
|
35
65
|
function activateDelegation(receipt, event, options = {}) {
|
|
36
66
|
const managedRole = managedDelegationRole(event.nativeAgent);
|
|
37
67
|
if (!managedRole)
|
|
38
68
|
return fail('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
|
|
39
69
|
if (receipt.status !== 'prepared')
|
|
40
70
|
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected prepared`);
|
|
71
|
+
if (receipt.spawnDispatchMonotonicMs == null
|
|
72
|
+
|| receipt.spawnDispatchedAt == null
|
|
73
|
+
|| receipt.activationDeadlineAt == null
|
|
74
|
+
|| receipt.activationDeadlineMonotonicMs == null
|
|
75
|
+
|| !Number.isFinite(Date.parse(receipt.spawnDispatchedAt))
|
|
76
|
+
|| !Number.isFinite(Date.parse(receipt.activationDeadlineAt))) {
|
|
77
|
+
return fail('DELEGATION_NOT_DISPATCHED', `delegation ${receipt.id} has not reached the native spawn dispatch boundary`);
|
|
78
|
+
}
|
|
79
|
+
const monotonic = (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1000000n)))();
|
|
80
|
+
const wallNow = Date.parse((options.now ?? (() => new Date().toISOString()))());
|
|
81
|
+
if (wallNow > Date.parse(receipt.activationDeadlineAt)) {
|
|
82
|
+
return fail('DELEGATION_ACTIVATION_TIMEOUT', `delegation ${receipt.id} activation evidence arrived after its deadline`);
|
|
83
|
+
}
|
|
41
84
|
if (managedRole !== receipt.role)
|
|
42
85
|
return fail('DELEGATION_ROLE_MISMATCH', `managed role ${managedRole} does not match ${receipt.role}`);
|
|
43
86
|
if (!event.parentId || (receipt.parentId !== null && event.parentId !== receipt.parentId) || !event.childId || event.childId === event.parentId) {
|
|
44
87
|
return fail('DELEGATION_IDENTITY_INVALID', 'native parent/child identity does not match the prepared delegation');
|
|
45
88
|
}
|
|
46
|
-
|
|
89
|
+
// spawnMode 的必需性按 client 判断:claude-code 宿主结构上不提供该字段,跳过;其余 client 维持硬校验
|
|
90
|
+
if (receipt.client !== 'claude-code' && event.spawnMode !== 'fresh') {
|
|
47
91
|
return fail('DELEGATION_FORK_FORBIDDEN', `spawn mode '${event.spawnMode}' is not fresh`);
|
|
48
|
-
const actualModel = event.actualModel;
|
|
49
|
-
if (!actualModel || actualModel.trim() !== actualModel) {
|
|
50
|
-
return fail('DELEGATION_MODEL_IDENTITY_MISSING', 'native start event must provide a non-empty actual model identity');
|
|
51
92
|
}
|
|
52
|
-
|
|
53
|
-
|
|
93
|
+
// model/effort 维度:claude-code 如实记录、不做门禁;其余 client 维持现状硬校验(HDR-2)
|
|
94
|
+
const actualModel = foldBlankToNull(event.actualModel);
|
|
95
|
+
const actualReasoningEffort = foldBlankToNull(event.actualReasoningEffort);
|
|
96
|
+
if (receipt.client !== 'claude-code') {
|
|
97
|
+
if (!actualModel || actualModel.trim() !== actualModel) {
|
|
98
|
+
return fail('DELEGATION_MODEL_IDENTITY_MISSING', 'native start event must provide a non-empty actual model identity');
|
|
99
|
+
}
|
|
54
100
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
101
|
+
if (actualModel !== null && actualModel !== receipt.requestedModel) {
|
|
102
|
+
if (receipt.client !== 'claude-code' && (!event.modelFallbackReason || event.modelFallbackReason.trim() === '')) {
|
|
103
|
+
return fail('DELEGATION_MODEL_FALLBACK_UNRECORDED', 'actual model differs from requested model without a fallback reason');
|
|
104
|
+
}
|
|
58
105
|
}
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
106
|
+
if (receipt.client !== 'claude-code') {
|
|
107
|
+
if (!actualReasoningEffort || actualReasoningEffort.trim() !== actualReasoningEffort) {
|
|
108
|
+
return fail('DELEGATION_REASONING_EFFORT_MISSING', 'native start event must provide a non-empty actual reasoning effort');
|
|
109
|
+
}
|
|
62
110
|
}
|
|
63
|
-
if (
|
|
111
|
+
if (receipt.client === 'codex'
|
|
112
|
+
&& event.hostEvidence?.kind !== 'codex-lifecycle-v2')
|
|
113
|
+
return fail('DELEGATION_HOST_EVIDENCE_REQUIRED', 'Codex activation requires trusted lifecycle-v2 host evidence');
|
|
114
|
+
if (actualReasoningEffort !== null && actualReasoningEffort !== receipt.requestedReasoningEffort) {
|
|
115
|
+
if (receipt.client !== 'claude-code' && (!event.reasoningEffortFallbackReason || event.reasoningEffortFallbackReason.trim() === '')) {
|
|
116
|
+
return fail('DELEGATION_REASONING_EFFORT_FALLBACK_UNRECORDED', 'actual reasoning effort differs from requested effort without a fallback reason');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// "无关 fallback 理由"检查对所有 client 保持不变——防止编造理由的红线,不属于 HDR-2 放宽范围
|
|
120
|
+
// actualModel/actualReasoningEffort 为 null(未观察到)时同样没有"实际不同"这回事,理由一样是无关的
|
|
121
|
+
if ((actualModel === null || actualModel === receipt.requestedModel) && event.modelFallbackReason) {
|
|
64
122
|
return fail('DELEGATION_MODEL_FALLBACK_INVALID', 'model fallback reason is only valid when actual model differs');
|
|
65
123
|
}
|
|
66
|
-
if (actualReasoningEffort === receipt.requestedReasoningEffort
|
|
124
|
+
if ((actualReasoningEffort === null || actualReasoningEffort === receipt.requestedReasoningEffort)
|
|
125
|
+
&& event.reasoningEffortFallbackReason) {
|
|
67
126
|
return fail('DELEGATION_REASONING_EFFORT_FALLBACK_INVALID', 'reasoning-effort fallback reason is only valid when actual effort differs');
|
|
68
127
|
}
|
|
69
128
|
if (event.hostEvidence
|
|
@@ -72,12 +131,38 @@ function activateDelegation(receipt, event, options = {}) {
|
|
|
72
131
|
|| !Number.isSafeInteger(event.hostEvidence.startRevision)
|
|
73
132
|
|| event.hostEvidence.startRevision < 1))
|
|
74
133
|
return fail('DELEGATION_HOST_EVIDENCE_INVALID', 'Codex start evidence reference is invalid');
|
|
134
|
+
if (event.hostEvidence?.kind === 'codex-lifecycle-v2') {
|
|
135
|
+
const expected = receipt.lifecycleProvenance;
|
|
136
|
+
const spawnObservedAt = Date.parse(event.hostEvidence.spawnObservedAt ?? '');
|
|
137
|
+
const spawnDispatchedAt = Date.parse(receipt.spawnDispatchedAt);
|
|
138
|
+
const activationDeadlineAt = Date.parse(receipt.activationDeadlineAt);
|
|
139
|
+
if (!expected
|
|
140
|
+
|| event.hostEvidence.protocolVersion !== expected.protocolVersion
|
|
141
|
+
|| event.hostEvidence.packageVersion !== expected.packageVersion
|
|
142
|
+
|| event.hostEvidence.internalExecutableBuildHash !== expected.internalExecutableBuildHash
|
|
143
|
+
|| event.hostEvidence.lifecycleContractHash !== expected.lifecycleContractHash
|
|
144
|
+
|| event.hostEvidence.hookDefinitionHash !== expected.hookDefinitionHash
|
|
145
|
+
|| event.hostEvidence.hookSource !== expected.hookSource
|
|
146
|
+
|| event.hostEvidence.hookSourcePathDigest !== expected.hookSourcePathDigest
|
|
147
|
+
|| event.hostEvidence.hookSourceHash !== expected.hookSourceHash
|
|
148
|
+
|| event.hostEvidence.capabilitySessionId !== expected.capabilitySessionId
|
|
149
|
+
|| event.hostEvidence.capabilityTurnId !== expected.capabilityTurnId
|
|
150
|
+
|| !event.hostEvidence.spawnToolUseId?.trim()
|
|
151
|
+
|| event.hostEvidence.spawnToolUseId === expected.capabilityToolUseId
|
|
152
|
+
|| !Number.isFinite(spawnObservedAt)
|
|
153
|
+
|| spawnObservedAt < spawnDispatchedAt
|
|
154
|
+
|| spawnObservedAt > activationDeadlineAt
|
|
155
|
+
|| event.parentId !== expected.capabilitySessionId
|
|
156
|
+
|| (event.hostEvidence.controllerInstanceDigest ?? null) !== expected.controllerInstanceDigest
|
|
157
|
+
|| (event.hostEvidence.controlGeneration ?? null) !== expected.controlGeneration)
|
|
158
|
+
return fail('DELEGATION_HOST_EVIDENCE_INVALID', 'Codex lifecycle provenance does not match the prepared receipt');
|
|
159
|
+
}
|
|
75
160
|
return { ok: true, receipt: Object.freeze({
|
|
76
161
|
...receipt,
|
|
77
162
|
status: 'activated',
|
|
78
163
|
parentId: event.parentId,
|
|
79
164
|
childId: event.childId,
|
|
80
|
-
spawnMode: event.spawnMode,
|
|
165
|
+
spawnMode: event.spawnMode ?? null,
|
|
81
166
|
actualModel,
|
|
82
167
|
actualReasoningEffort,
|
|
83
168
|
modelFallbackReason: event.modelFallbackReason ?? null,
|
|
@@ -88,9 +173,20 @@ function activateDelegation(receipt, event, options = {}) {
|
|
|
88
173
|
consumer: null,
|
|
89
174
|
consumedAt: null
|
|
90
175
|
}) : receipt.hostEvidence ?? null,
|
|
91
|
-
|
|
176
|
+
startEvidenceMonotonicMs: monotonic,
|
|
177
|
+
activatedMonotonicMs: (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1000000n)))(),
|
|
178
|
+
activatedAt: new Date(wallNow).toISOString()
|
|
92
179
|
}) };
|
|
93
180
|
}
|
|
181
|
+
function abortPreparedDelegation(receipt) {
|
|
182
|
+
if (receipt.status !== 'prepared') {
|
|
183
|
+
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected prepared`);
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
ok: true,
|
|
187
|
+
receipt: Object.freeze({ ...receipt, status: 'aborted' })
|
|
188
|
+
};
|
|
189
|
+
}
|
|
94
190
|
function completeDelegationStage(receipt, event) {
|
|
95
191
|
if (receipt.status !== 'activated')
|
|
96
192
|
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected activated`);
|
|
@@ -105,6 +201,9 @@ function completeDelegationStage(receipt, event) {
|
|
|
105
201
|
function sealDelegation(receipt, event, options = {}) {
|
|
106
202
|
if (receipt.status !== 'stage-completed')
|
|
107
203
|
return fail('DELEGATION_STATE_INVALID', `delegation ${receipt.id} is ${receipt.status}, expected stage-completed`);
|
|
204
|
+
if (receipt.client === 'codex' && options.requireHostEvidence !== false && !event.hostEvidence) {
|
|
205
|
+
return fail('DELEGATION_HOST_EVIDENCE_REQUIRED', 'Codex sealing requires consumed lifecycle-v2 host evidence');
|
|
206
|
+
}
|
|
108
207
|
if (event.childId !== receipt.childId || event.exitCode !== 0)
|
|
109
208
|
return fail('DELEGATION_STOP_INVALID', 'native stop identity or exit status is invalid');
|
|
110
209
|
if (receipt.role === 'reviewer') {
|
|
@@ -131,6 +230,10 @@ function sealDelegation(receipt, event, options = {}) {
|
|
|
131
230
|
status: 'sealed',
|
|
132
231
|
afterFingerprint: event.afterFingerprint,
|
|
133
232
|
changedPaths: Object.freeze([...event.changedPaths]),
|
|
233
|
+
actualModel: receipt.actualModel ?? foldBlankToNull(event.actualModel),
|
|
234
|
+
actualReasoningEffort: receipt.actualReasoningEffort ?? foldBlankToNull(event.actualReasoningEffort),
|
|
235
|
+
modelFallbackReason: receipt.modelFallbackReason ?? event.modelFallbackReason ?? null,
|
|
236
|
+
reasoningEffortFallbackReason: receipt.reasoningEffortFallbackReason ?? event.reasoningEffortFallbackReason ?? null,
|
|
134
237
|
hostEvidence: event.hostEvidence && receipt.hostEvidence ? Object.freeze({
|
|
135
238
|
...receipt.hostEvidence,
|
|
136
239
|
...event.hostEvidence
|
|
@@ -149,5 +252,5 @@ function consumeDelegation(receipt, options = {}) {
|
|
|
149
252
|
consumedAt: (options.now ?? (() => new Date().toISOString()))()
|
|
150
253
|
}) };
|
|
151
254
|
}
|
|
152
|
-
export { activateDelegation, completeDelegationStage, consumeDelegation, managedDelegationRole, prepareDelegation, sealDelegation };
|
|
255
|
+
export { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, managedDelegationRole, prepareDelegation, sealDelegation };
|
|
153
256
|
//# sourceMappingURL=delegation-receipts.js.map
|
package/dist/lib/task/events.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import { appendActivityEntry, locateActivityLog, pairEntries } from './activity-log.js';
|
|
3
|
-
import { buildArtifactLinkSection, artifactName, parseArtifactName, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
3
|
+
import { buildArtifactLinkSection, artifactName, inspectArtifactDirectory, parseReviewedInputReference, parseCodePlanInputReference, parseArtifactName, resolveArtifactContext, validateCompletedArtifact } from './artifact-lifecycle.js';
|
|
4
|
+
import { ArtifactReceiptError, sha256File, upsertArtifactReceipt } from './artifact-receipts.js';
|
|
4
5
|
import { parseTypedTaskFrontmatter } from './frontmatter.js';
|
|
5
6
|
import { consumeImplementationInput, IMPLEMENTATION_INPUT_ALIASES, parseImplementationInputs, renderImplementationInputs } from './implementation-inputs.js';
|
|
6
7
|
import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
|
|
@@ -11,6 +12,7 @@ import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
|
11
12
|
import { commitOrchestrationStageCompletion } from './orchestration.js';
|
|
12
13
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
13
14
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
15
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
14
16
|
const eventCatalog = [
|
|
15
17
|
'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
|
|
16
18
|
'review-analysis.started', 'review-analysis.completed',
|
|
@@ -21,7 +23,7 @@ const eventCatalog = [
|
|
|
21
23
|
'manual-validation.started', 'manual-validation.completed',
|
|
22
24
|
'validation-run.started', 'validation-run.completed'
|
|
23
25
|
];
|
|
24
|
-
const BASE_FIELDS = new Set(['taskRef', 'event', 'agent', 'dryRun']);
|
|
26
|
+
const BASE_FIELDS = new Set(['taskRef', 'event', 'agent', 'dryRun', 'overrideTicket', 'overrideTarget', 'overrideScope']);
|
|
25
27
|
const SCHEMAS = {
|
|
26
28
|
'analyze.started': { optional: ['round'] },
|
|
27
29
|
'analyze.awaiting-input': { required: ['question'] },
|
|
@@ -237,6 +239,92 @@ function openStartedIdentity(rows, family) {
|
|
|
237
239
|
});
|
|
238
240
|
return matches.length === 1 ? matches[0] : matches.length > 1 ? { conflict: true } : null;
|
|
239
241
|
}
|
|
242
|
+
function reviewInputFamily(family) {
|
|
243
|
+
return family === 'review-analysis' ? 'analysis' : family === 'review-plan' ? 'plan' : 'code';
|
|
244
|
+
}
|
|
245
|
+
function buildCompletionReceipt(taskDir, family, artifact, completedAt, frontmatter) {
|
|
246
|
+
if (family === 'code') {
|
|
247
|
+
let content;
|
|
248
|
+
try {
|
|
249
|
+
content = fs.readFileSync(artifact.path, 'utf8');
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
return { ok: false, message: `cannot read ${artifact.name}: ${String(error)}` };
|
|
253
|
+
}
|
|
254
|
+
const input = parseCodePlanInputReference(content);
|
|
255
|
+
if (!input)
|
|
256
|
+
return { ok: false, message: `${artifact.name} does not reference a canonical plan artifact` };
|
|
257
|
+
const startedInput = typeof frontmatter.code_input_artifact === 'string' ? frontmatter.code_input_artifact : '';
|
|
258
|
+
const startedSha256 = typeof frontmatter.code_input_sha256 === 'string' ? frontmatter.code_input_sha256 : '';
|
|
259
|
+
if (!startedInput || !startedSha256)
|
|
260
|
+
return { ok: false, message: 'code.started plan input context is missing' };
|
|
261
|
+
if (input.name !== startedInput)
|
|
262
|
+
return { ok: false, message: `${artifact.name} plan input '${input.name}' does not match code.started input '${startedInput}'` };
|
|
263
|
+
const plan = inspectArtifactDirectory(taskDir, 'plan');
|
|
264
|
+
if (plan.status !== 'ready' || !plan.latest || plan.latest.name !== input.name) {
|
|
265
|
+
return { ok: false, message: `code input '${input.name}' is not the latest plan artifact` };
|
|
266
|
+
}
|
|
267
|
+
try {
|
|
268
|
+
const inputSha256 = sha256File(plan.latest.path);
|
|
269
|
+
if (inputSha256 !== startedSha256)
|
|
270
|
+
return { ok: false, message: `code input ${input.name} changed after code.started` };
|
|
271
|
+
return {
|
|
272
|
+
ok: true,
|
|
273
|
+
receipt: {
|
|
274
|
+
event: 'code.completed', output: artifact.name, input: input.name,
|
|
275
|
+
inputSha256, completedAt
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
return { ok: false, message: `cannot hash code input plan: ${error instanceof Error ? error.message : String(error)}` };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (!family.startsWith('review-'))
|
|
284
|
+
return null;
|
|
285
|
+
const expectedFamily = reviewInputFamily(family);
|
|
286
|
+
let content;
|
|
287
|
+
try {
|
|
288
|
+
content = fs.readFileSync(artifact.path, 'utf8');
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
return { ok: false, message: `cannot read ${artifact.name}: ${String(error)}` };
|
|
292
|
+
}
|
|
293
|
+
const input = parseReviewedInputReference(content, expectedFamily);
|
|
294
|
+
if (!input)
|
|
295
|
+
return { ok: false, message: `${artifact.name} does not reference a canonical ${expectedFamily} artifact` };
|
|
296
|
+
const startedInput = typeof frontmatter.review_input_artifact === 'string' ? frontmatter.review_input_artifact : '';
|
|
297
|
+
const startedSha256 = typeof frontmatter.review_input_sha256 === 'string' ? frontmatter.review_input_sha256 : '';
|
|
298
|
+
if (!startedInput || !startedSha256)
|
|
299
|
+
return { ok: false, message: 'review started input context is missing' };
|
|
300
|
+
if (input.name !== startedInput)
|
|
301
|
+
return { ok: false, message: `${artifact.name} input '${input.name}' does not match review.started input '${startedInput}'` };
|
|
302
|
+
const current = inspectArtifactDirectory(taskDir, expectedFamily);
|
|
303
|
+
if (current.status !== 'ready' || !current.latest || current.latest.name !== input.name) {
|
|
304
|
+
return { ok: false, message: `review input '${input.name}' is not the latest ${expectedFamily} artifact` };
|
|
305
|
+
}
|
|
306
|
+
const event = family === 'review-analysis'
|
|
307
|
+
? 'review-analysis.completed'
|
|
308
|
+
: family === 'review-plan' ? 'review-plan.completed' : 'review-code.completed';
|
|
309
|
+
try {
|
|
310
|
+
const stat = fs.lstatSync(current.latest.path);
|
|
311
|
+
if (!stat.isFile() || stat.isSymbolicLink())
|
|
312
|
+
throw new Error('review input is not a regular file');
|
|
313
|
+
const inputSha256 = sha256File(current.latest.path);
|
|
314
|
+
if (inputSha256 !== startedSha256)
|
|
315
|
+
return { ok: false, message: `review input ${input.name} changed after review.started` };
|
|
316
|
+
return {
|
|
317
|
+
ok: true,
|
|
318
|
+
receipt: {
|
|
319
|
+
event, output: artifact.name, input: input.name,
|
|
320
|
+
inputSha256, completedAt
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
return { ok: false, message: `cannot hash review input ${input.name}: ${error instanceof Error ? error.message : String(error)}` };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
240
328
|
function applyTaskEventUnlocked(request, options = {}) {
|
|
241
329
|
const invalid = validateTaskEventRequest(request);
|
|
242
330
|
if (invalid)
|
|
@@ -244,7 +332,8 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
244
332
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
245
333
|
if (!resolved.ok)
|
|
246
334
|
return failed(request, { code: resolved.code, message: resolved.message }, { taskId: resolved.taskId });
|
|
247
|
-
|
|
335
|
+
const stateOverride = allowsManualOverride(options.manualOverride, 'task-event', 'TASK_STATE_MISMATCH');
|
|
336
|
+
if (resolved.state !== 'active' && !stateOverride)
|
|
248
337
|
return failed(request, { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected active` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
249
338
|
let content;
|
|
250
339
|
try {
|
|
@@ -316,7 +405,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
316
405
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, done.done, frontmatter, artifactContext);
|
|
317
406
|
if (eventIdentity.phase === 'completed' && completedRows.length > 0 && openRows.length === 0)
|
|
318
407
|
return failed(normalized, { code: 'EVENT_LOG_CONFLICT', message: 'event identity is already completed with a different payload' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
319
|
-
if (eventIdentity.phase === 'completed' && !row?.started)
|
|
408
|
+
if (eventIdentity.phase === 'completed' && !row?.started && !allowsManualOverride(options.manualOverride, 'task-event', 'EVENT_START_MISSING'))
|
|
320
409
|
return failed(normalized, { code: 'EVENT_START_MISSING', message: 'completion requires one open matching started event' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
321
410
|
if (eventIdentity.phase === 'completed') {
|
|
322
411
|
const validated = validateCompletedArtifact(resolved.taskDir, FAMILY[eventIdentity.family].artifact, normalized.artifact, normalized.round);
|
|
@@ -329,7 +418,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
329
418
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, existing.time, frontmatter, artifactContext);
|
|
330
419
|
}
|
|
331
420
|
const allowed = FAMILY[eventIdentity.family][eventIdentity.phase === 'started' ? 'started' : 'completed'];
|
|
332
|
-
if (!allowed.includes(currentStep))
|
|
421
|
+
if (!allowed.includes(currentStep) && !allowsManualOverride(options.manualOverride, 'task-event', 'EVENT_TRANSITION_INVALID'))
|
|
333
422
|
return failed(normalized, { code: 'EVENT_TRANSITION_INVALID', message: `${normalized.event} is not allowed from '${currentStep}'` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
334
423
|
const findingCountError = validateReviewFindingCounts(normalized, content, eventIdentity.family, completedArtifact?.path ?? null);
|
|
335
424
|
if (findingCountError)
|
|
@@ -363,19 +452,69 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
363
452
|
catch (error) {
|
|
364
453
|
return failed(normalized, { code: 'METADATA_CAPTURE_FAILED', message: String(error) }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
365
454
|
}
|
|
455
|
+
let completionReceipt = null;
|
|
456
|
+
if (eventIdentity.phase === 'completed' && completedArtifact) {
|
|
457
|
+
const receipt = buildCompletionReceipt(resolved.taskDir, eventIdentity.family, completedArtifact, metadata.timestamp, frontmatter);
|
|
458
|
+
if (receipt && !receipt.ok) {
|
|
459
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: receipt.message }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
460
|
+
}
|
|
461
|
+
completionReceipt = receipt?.receipt ?? null;
|
|
462
|
+
}
|
|
366
463
|
const step = eventIdentity.phase === 'started' || eventIdentity.target === null ? currentStep : eventIdentity.target;
|
|
367
464
|
const logStep = eventIdentity.phase === 'started' ? `${eventIdentity.action} [started]` : eventIdentity.action;
|
|
368
465
|
const body = appendActivityEntry(section, { time: metadata.timestamp, step: logStep, agent: normalized.agent, note: eventIdentity.note });
|
|
369
466
|
const frontmatterSet = { current_step: step, assigned_to: normalized.agent };
|
|
467
|
+
let frontmatterRemove;
|
|
468
|
+
if (eventIdentity.phase === 'started' && eventIdentity.family === 'code') {
|
|
469
|
+
const planInput = artifactContext?.inputs.find((input) => input.family === 'plan');
|
|
470
|
+
if (!planInput)
|
|
471
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: 'code.started plan input context is unavailable' }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
472
|
+
try {
|
|
473
|
+
frontmatterSet.code_input_artifact = planInput.name;
|
|
474
|
+
frontmatterSet.code_input_sha256 = sha256File(planInput.path);
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot hash code.started plan input: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
else if (eventIdentity.phase === 'completed' && eventIdentity.family === 'code') {
|
|
481
|
+
frontmatterRemove = ['code_input_artifact', 'code_input_sha256'];
|
|
482
|
+
}
|
|
483
|
+
else if (eventIdentity.phase === 'started' && eventIdentity.family.startsWith('review-')) {
|
|
484
|
+
const expectedFamily = reviewInputFamily(eventIdentity.family);
|
|
485
|
+
const input = artifactContext?.inputs.find((candidate) => candidate.family === expectedFamily);
|
|
486
|
+
if (!input)
|
|
487
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `review.started ${expectedFamily} input context is unavailable` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
488
|
+
try {
|
|
489
|
+
frontmatterSet.review_input_artifact = input.name;
|
|
490
|
+
frontmatterSet.review_input_sha256 = sha256File(input.path);
|
|
491
|
+
}
|
|
492
|
+
catch (error) {
|
|
493
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message: `cannot hash review.started input: ${error instanceof Error ? error.message : String(error)}` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, artifactContext });
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
else if (eventIdentity.phase === 'completed' && eventIdentity.family.startsWith('review-')) {
|
|
497
|
+
frontmatterRemove = ['review_input_artifact', 'review_input_sha256'];
|
|
498
|
+
}
|
|
370
499
|
if (eventIdentity.phase === 'started' && normalized.implementationInput)
|
|
371
500
|
frontmatterSet.last_reviewed_commit = '';
|
|
372
501
|
const mutations = [
|
|
373
|
-
{ kind: 'frontmatter', set: frontmatterSet }
|
|
502
|
+
{ kind: 'frontmatter', set: frontmatterSet, remove: frontmatterRemove }
|
|
374
503
|
];
|
|
375
504
|
if (completedArtifact) {
|
|
376
505
|
const link = buildArtifactLinkSection(content, completedArtifact);
|
|
377
506
|
mutations.push({ kind: 'section', aliases: link.aliases, heading: link.heading, body: link.body });
|
|
378
507
|
}
|
|
508
|
+
if (completionReceipt) {
|
|
509
|
+
try {
|
|
510
|
+
const receiptSection = upsertArtifactReceipt(content, completionReceipt);
|
|
511
|
+
mutations.push({ kind: 'section', aliases: receiptSection.aliases, heading: receiptSection.heading, body: receiptSection.body });
|
|
512
|
+
}
|
|
513
|
+
catch (error) {
|
|
514
|
+
const message = error instanceof ArtifactReceiptError ? error.message : String(error);
|
|
515
|
+
return failed(normalized, { code: 'EVENT_ARTIFACT_CONFLICT', message }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, fromStep: currentStep, toStep: currentStep, action: eventIdentity.action, phase: eventIdentity.phase });
|
|
516
|
+
}
|
|
517
|
+
}
|
|
379
518
|
if (eventIdentity.phase === 'completed' && normalized.implementationInput) {
|
|
380
519
|
let implementationRows;
|
|
381
520
|
try {
|
|
@@ -391,7 +530,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
391
530
|
});
|
|
392
531
|
}
|
|
393
532
|
mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
|
|
394
|
-
const result = writeTask({ taskRef: normalized.taskRef, expectedState: 'active', dryRun: normalized.dryRun, mutations }, { ...options, metadataProvider: () => metadata });
|
|
533
|
+
const result = writeTask({ taskRef: normalized.taskRef, expectedState: stateOverride ? resolved.state : 'active', dryRun: normalized.dryRun, mutations }, { ...options, metadataProvider: () => metadata });
|
|
395
534
|
if (result.status === 'failed')
|
|
396
535
|
return failed(normalized, result.error, { taskId: result.taskId, taskMdPath: result.taskMdPath, fromStep: currentStep, toStep: step, action: eventIdentity.action, phase: eventIdentity.phase, timestamp: result.timestamp, agentInfraVersion: result.agentInfraVersion, operations: result.operations, artifactContext });
|
|
397
536
|
if (!normalized.dryRun && orchestrationCompletion) {
|
|
@@ -431,6 +570,8 @@ function applyTaskEvent(request, options = {}) {
|
|
|
431
570
|
if (invalid)
|
|
432
571
|
return failed(request, invalid);
|
|
433
572
|
const parts = eventParts(request.event);
|
|
573
|
+
if (options.lockAlreadyHeld)
|
|
574
|
+
return applyTaskEventUnlocked(request, options);
|
|
434
575
|
if (request.dryRun || parts.phase !== 'completed' || parts.family === 'manual-validation') {
|
|
435
576
|
return applyTaskEventUnlocked(request, options);
|
|
436
577
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const guardProducerCatalog = [
|
|
2
|
+
{ producerId: 'task.resolve', guardId: 'G-01', code: 'TASK_NOT_FOUND' },
|
|
3
|
+
{ producerId: 'task.resolve', guardId: 'G-01', code: 'TASK_IDENTITY_CONFLICT' },
|
|
4
|
+
{ producerId: 'task.write', guardId: 'G-02', code: 'TASK_STATE_MISMATCH' },
|
|
5
|
+
{ producerId: 'task-event', guardId: 'G-04', code: 'TASK_STATE_MISMATCH' },
|
|
6
|
+
{ producerId: 'task-event', guardId: 'G-04', code: 'EVENT_TRANSITION_INVALID' },
|
|
7
|
+
{ producerId: 'task-event', guardId: 'G-04', code: 'EVENT_START_MISSING' },
|
|
8
|
+
{ producerId: 'task-verify', guardId: 'G-05', code: 'VERIFY_TASK_STATE_MISMATCH' },
|
|
9
|
+
{ producerId: 'task-verify', guardId: 'G-05', code: 'VERIFY_ARTIFACT_INVALID' },
|
|
10
|
+
{ producerId: 'verification-engine', guardId: 'G-06', code: 'CHECK_FAILED' },
|
|
11
|
+
{ producerId: 'verification-engine', guardId: 'G-06', code: 'CHECK_BLOCKED' },
|
|
12
|
+
{ producerId: 'activity-intent', guardId: 'G-07', code: 'TASK_STATE_MISMATCH' },
|
|
13
|
+
{ producerId: 'ledger-intent', guardId: 'G-07', code: 'TASK_STATE_MISMATCH' },
|
|
14
|
+
{ producerId: 'decision-intent', guardId: 'G-07', code: 'TASK_STATE_MISMATCH' },
|
|
15
|
+
{ producerId: 'workflow-warning', guardId: 'G-07', code: 'TASK_STATE_MISMATCH' },
|
|
16
|
+
{ producerId: 'review-finalization', guardId: 'G-07', code: 'TASK_STATE_MISMATCH' },
|
|
17
|
+
{ producerId: 'ledger-intent', guardId: 'G-08', code: 'LEDGER_TRANSITION_INVALID' },
|
|
18
|
+
{ producerId: 'lifecycle-execution', guardId: 'G-09', code: 'ORCHESTRATION_STANDALONE_BUSY' },
|
|
19
|
+
{ producerId: 'lifecycle-execution', guardId: 'G-09', code: 'ORCHESTRATION_PROVENANCE_MISMATCH' },
|
|
20
|
+
{ producerId: 'lifecycle-execution', guardId: 'G-09', code: 'ORCHESTRATION_STATE_INVALID' },
|
|
21
|
+
{ producerId: 'platform.issue', guardId: 'G-10', code: 'PLATFORM_BIND_FAILED' },
|
|
22
|
+
{ producerId: 'platform.pull-request', guardId: 'G-10', code: 'PLATFORM_BIND_FAILED' }
|
|
23
|
+
];
|
|
24
|
+
function guardFailureId(producerId, code) {
|
|
25
|
+
return `${producerId}:${code}`;
|
|
26
|
+
}
|
|
27
|
+
function allowsManualOverride(capability, producerId, code) {
|
|
28
|
+
return capability?.failureId === guardFailureId(producerId, code);
|
|
29
|
+
}
|
|
30
|
+
export { allowsManualOverride, guardFailureId, guardProducerCatalog };
|
|
31
|
+
//# sourceMappingURL=guard-override.js.map
|