@fitlab-ai/agent-infra 0.9.7 → 0.9.9
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +11 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +60 -22
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +175 -11
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +86 -69
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
- package/dist/lib/decide.js +68 -14
- package/dist/lib/git/workflow.js +10 -1
- package/dist/lib/init.js +1 -1
- package/dist/lib/internal/codex-lifecycle.js +19 -5
- package/dist/lib/internal/codex-sandbox-controller.js +6 -13
- package/dist/lib/internal/git-workflow.js +5 -7
- package/dist/lib/internal/platform-pr.js +20 -7
- package/dist/lib/internal/release-workflow.js +3 -4
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-activity.js +57 -7
- package/dist/lib/internal/task-event.js +57 -4
- package/dist/lib/internal/task-finalization.js +54 -0
- 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 +161 -288
- 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 +53 -13
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +389 -311
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/container-identity.js +1 -1
- package/dist/lib/sandbox/control/controller-registration.js +273 -0
- package/dist/lib/sandbox/control/executor.js +145 -5
- package/dist/lib/sandbox/control/lifecycle.js +395 -73
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/control/state.js +27 -11
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/mounts.js +20 -5
- package/dist/lib/sandbox/recovery.js +78 -10
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/workspace-view.js +62 -40
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/server/process-state.js +51 -1
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/codex-orchestration.js +158 -33
- package/dist/lib/task/commands/status.js +59 -1
- package/dist/lib/task/commit-operation.js +335 -0
- package/dist/lib/task/commit-policy.js +18 -0
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +279 -17
- package/dist/lib/task/events.js +9 -5
- package/dist/lib/task/finalization.js +567 -0
- 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/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +199 -719
- package/dist/lib/task/review-finalization.js +3 -2
- package/dist/lib/task/short-id.js +59 -1
- package/dist/lib/task/task-execution-lock.js +6 -1
- package/dist/lib/task/verification-engine.js +138 -59
- package/dist/lib/task/verification.js +15 -2
- package/dist/lib/task/workflow-warning-intents.js +54 -3
- 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 +63 -20
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +278 -13
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +112 -93
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
- package/lib/decide.ts +59 -14
- package/lib/git/workflow.ts +11 -2
- package/lib/init.ts +1 -1
- package/lib/internal/codex-lifecycle.ts +18 -5
- package/lib/internal/codex-sandbox-controller.ts +6 -12
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +3 -4
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-activity.ts +46 -6
- package/lib/internal/task-event.ts +53 -4
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-ledger.ts +48 -4
- package/lib/internal/task-lifecycle.ts +56 -12
- package/lib/internal/task-orchestration.ts +32 -107
- 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 +65 -14
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +88 -4
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/container-identity.ts +3 -3
- package/lib/sandbox/control/controller-registration.ts +347 -0
- package/lib/sandbox/control/executor.ts +172 -6
- package/lib/sandbox/control/lifecycle.ts +465 -77
- package/lib/sandbox/control/protocol.ts +85 -9
- package/lib/sandbox/control/state.ts +35 -18
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/mounts.ts +23 -5
- package/lib/sandbox/recovery.ts +90 -9
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +73 -43
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/server/process-state.ts +48 -1
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/codex-orchestration.ts +167 -33
- package/lib/task/commands/status.ts +78 -1
- package/lib/task/commit-operation.ts +430 -0
- package/lib/task/commit-policy.ts +30 -0
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +304 -19
- package/lib/task/events.ts +10 -5
- package/lib/task/finalization.ts +719 -0
- 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/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +239 -922
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +145 -64
- package/lib/task/verification.ts +18 -3
- package/lib/task/workflow-warning-intents.ts +61 -4
- package/lib/task/write.ts +4 -1
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +2 -2
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +9 -24
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -3
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -3
- package/templates/.agents/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/commit/SKILL.en.md +41 -125
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
- 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
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.codex/hooks.json +4 -4
- package/dist/lib/task/commit-finalization.js +0 -305
- package/dist/lib/task/commit-intent.js +0 -165
- package/lib/task/commit-finalization.ts +0 -419
- package/lib/task/commit-intent.ts +0 -230
|
@@ -1,5 +1,246 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { isAgentClientId } from '../agent-clients/types.js';
|
|
2
3
|
import { normalizeAgentToken } from '../agent-clients/tokens.js';
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function hasExactKeys(value, required, optional = []) {
|
|
8
|
+
if (!isRecord(value))
|
|
9
|
+
return false;
|
|
10
|
+
const keys = Object.keys(value);
|
|
11
|
+
const allowed = new Set([...required, ...optional]);
|
|
12
|
+
return required.every((key) => Object.prototype.hasOwnProperty.call(value, key))
|
|
13
|
+
&& keys.every((key) => allowed.has(key));
|
|
14
|
+
}
|
|
15
|
+
function exactText(value) {
|
|
16
|
+
return typeof value === 'string' && value.length > 0 && value.trim() === value;
|
|
17
|
+
}
|
|
18
|
+
function nullableText(value) {
|
|
19
|
+
return value === null || exactText(value);
|
|
20
|
+
}
|
|
21
|
+
function nullableSafeInteger(value) {
|
|
22
|
+
return value === null || (Number.isSafeInteger(value) && value >= 0);
|
|
23
|
+
}
|
|
24
|
+
const LIFECYCLE_PROVENANCE_KEYS = [
|
|
25
|
+
'protocolVersion', 'packageVersion', 'internalExecutableBuildHash', 'lifecycleContractHash',
|
|
26
|
+
'hookDefinitionHash', 'hookSource', 'hookSourcePathDigest', 'hookSourceHash',
|
|
27
|
+
'capabilitySessionId', 'capabilityTurnId', 'capabilityToolUseId',
|
|
28
|
+
'controllerInstanceDigest', 'controlGeneration'
|
|
29
|
+
];
|
|
30
|
+
function isDelegationLifecycleProvenance(value) {
|
|
31
|
+
if (!hasExactKeys(value, LIFECYCLE_PROVENANCE_KEYS))
|
|
32
|
+
return false;
|
|
33
|
+
return Number.isSafeInteger(value.protocolVersion)
|
|
34
|
+
&& value.protocolVersion > 0
|
|
35
|
+
&& exactText(value.packageVersion)
|
|
36
|
+
&& exactText(value.internalExecutableBuildHash)
|
|
37
|
+
&& exactText(value.lifecycleContractHash)
|
|
38
|
+
&& exactText(value.hookDefinitionHash)
|
|
39
|
+
&& ['project', 'managed', 'isolated-user'].includes(value.hookSource)
|
|
40
|
+
&& exactText(value.hookSourcePathDigest)
|
|
41
|
+
&& exactText(value.hookSourceHash)
|
|
42
|
+
&& exactText(value.capabilitySessionId)
|
|
43
|
+
&& exactText(value.capabilityTurnId)
|
|
44
|
+
&& exactText(value.capabilityToolUseId)
|
|
45
|
+
&& nullableText(value.controllerInstanceDigest)
|
|
46
|
+
&& nullableText(value.controlGeneration);
|
|
47
|
+
}
|
|
48
|
+
const HOST_EVIDENCE_REQUIRED_KEYS = [
|
|
49
|
+
'kind', 'hookDefinitionHash', 'startRevision', 'stopRevision', 'consumer', 'consumedAt'
|
|
50
|
+
];
|
|
51
|
+
const HOST_EVIDENCE_OPTIONAL_KEYS = [
|
|
52
|
+
'protocolVersion', 'packageVersion', 'internalExecutableBuildHash', 'lifecycleContractHash',
|
|
53
|
+
'hookSource', 'hookSourcePathDigest', 'hookSourceHash', 'capabilitySessionId',
|
|
54
|
+
'capabilityTurnId', 'capabilityToolUseId', 'spawnToolUseId', 'spawnObservedAt',
|
|
55
|
+
'controllerInstanceDigest', 'controlGeneration'
|
|
56
|
+
];
|
|
57
|
+
function isDelegationHostEvidence(value) {
|
|
58
|
+
if (!hasExactKeys(value, HOST_EVIDENCE_REQUIRED_KEYS, HOST_EVIDENCE_OPTIONAL_KEYS))
|
|
59
|
+
return false;
|
|
60
|
+
if (!['codex-lifecycle-v1', 'codex-lifecycle-v2'].includes(value.kind))
|
|
61
|
+
return false;
|
|
62
|
+
if (!exactText(value.hookDefinitionHash)
|
|
63
|
+
|| !Number.isSafeInteger(value.startRevision) || value.startRevision < 1
|
|
64
|
+
|| !nullableSafeInteger(value.stopRevision)
|
|
65
|
+
|| !nullableText(value.consumer)
|
|
66
|
+
|| !nullableText(value.consumedAt))
|
|
67
|
+
return false;
|
|
68
|
+
if (value.kind === 'codex-lifecycle-v1')
|
|
69
|
+
return true;
|
|
70
|
+
return Number.isSafeInteger(value.protocolVersion)
|
|
71
|
+
&& value.protocolVersion > 0
|
|
72
|
+
&& exactText(value.packageVersion)
|
|
73
|
+
&& exactText(value.internalExecutableBuildHash)
|
|
74
|
+
&& exactText(value.lifecycleContractHash)
|
|
75
|
+
&& ['project', 'managed', 'isolated-user'].includes(value.hookSource)
|
|
76
|
+
&& exactText(value.hookSourcePathDigest)
|
|
77
|
+
&& exactText(value.hookSourceHash)
|
|
78
|
+
&& exactText(value.capabilitySessionId)
|
|
79
|
+
&& exactText(value.capabilityTurnId)
|
|
80
|
+
&& exactText(value.spawnToolUseId)
|
|
81
|
+
&& exactText(value.spawnObservedAt)
|
|
82
|
+
&& nullableText(value.controllerInstanceDigest)
|
|
83
|
+
&& nullableText(value.controlGeneration);
|
|
84
|
+
}
|
|
85
|
+
function hasCurrentCodexEvidence(receipt) {
|
|
86
|
+
const provenance = receipt.lifecycleProvenance;
|
|
87
|
+
if (!provenance)
|
|
88
|
+
return false;
|
|
89
|
+
const activated = ['activated', 'stage-completed', 'sealed', 'consumed'].includes(receipt.status);
|
|
90
|
+
if (!activated)
|
|
91
|
+
return receipt.hostEvidence === null;
|
|
92
|
+
const host = receipt.hostEvidence;
|
|
93
|
+
if (host?.kind !== 'codex-lifecycle-v2'
|
|
94
|
+
|| host.protocolVersion !== provenance.protocolVersion
|
|
95
|
+
|| host.packageVersion !== provenance.packageVersion
|
|
96
|
+
|| host.internalExecutableBuildHash !== provenance.internalExecutableBuildHash
|
|
97
|
+
|| host.lifecycleContractHash !== provenance.lifecycleContractHash
|
|
98
|
+
|| host.hookDefinitionHash !== provenance.hookDefinitionHash
|
|
99
|
+
|| host.hookSource !== provenance.hookSource
|
|
100
|
+
|| host.hookSourcePathDigest !== provenance.hookSourcePathDigest
|
|
101
|
+
|| host.hookSourceHash !== provenance.hookSourceHash
|
|
102
|
+
|| host.capabilitySessionId !== provenance.capabilitySessionId
|
|
103
|
+
|| host.capabilityTurnId !== provenance.capabilityTurnId
|
|
104
|
+
|| host.controllerInstanceDigest !== provenance.controllerInstanceDigest
|
|
105
|
+
|| host.controlGeneration !== provenance.controlGeneration
|
|
106
|
+
|| receipt.parentId !== provenance.capabilitySessionId
|
|
107
|
+
|| host.spawnToolUseId === provenance.capabilityToolUseId)
|
|
108
|
+
return false;
|
|
109
|
+
const spawnObservedAt = Date.parse(host.spawnObservedAt ?? '');
|
|
110
|
+
const spawnDispatchedAt = Date.parse(receipt.spawnDispatchedAt ?? '');
|
|
111
|
+
const activationDeadlineAt = Date.parse(receipt.activationDeadlineAt ?? '');
|
|
112
|
+
if (!Number.isFinite(spawnObservedAt)
|
|
113
|
+
|| !Number.isFinite(spawnDispatchedAt)
|
|
114
|
+
|| !Number.isFinite(activationDeadlineAt)
|
|
115
|
+
|| spawnObservedAt < spawnDispatchedAt
|
|
116
|
+
|| spawnObservedAt > activationDeadlineAt)
|
|
117
|
+
return false;
|
|
118
|
+
if (['sealed', 'consumed'].includes(receipt.status)) {
|
|
119
|
+
return Number.isSafeInteger(host.stopRevision)
|
|
120
|
+
&& host.stopRevision > host.startRevision
|
|
121
|
+
&& host.consumer === receipt.id
|
|
122
|
+
&& exactText(host.consumedAt);
|
|
123
|
+
}
|
|
124
|
+
return host.stopRevision === null && host.consumer === null && host.consumedAt === null;
|
|
125
|
+
}
|
|
126
|
+
const RECEIPT_KEYS = [
|
|
127
|
+
'id', 'taskId', 'runId', 'role', 'stage', 'round', 'artifact', 'client',
|
|
128
|
+
'requestedModel', 'requestedReasoningEffort', 'actualModel', 'actualReasoningEffort',
|
|
129
|
+
'modelFallbackReason', 'reasoningEffortFallbackReason', 'parentId', 'childId',
|
|
130
|
+
'spawnMode', 'agent', 'status', 'workspaceSnapshotScope', 'lifecycleProvenance',
|
|
131
|
+
'hostEvidence', 'beforeFingerprint', 'afterFingerprint', 'changedPaths', 'createdAt',
|
|
132
|
+
'preparedMonotonicMs', 'spawnDispatchMonotonicMs', 'activationDeadlineMonotonicMs',
|
|
133
|
+
'spawnDispatchedAt', 'activationDeadlineAt', 'startEvidenceMonotonicMs',
|
|
134
|
+
'activatedMonotonicMs', 'activatedAt', 'sealedAt', 'consumedAt'
|
|
135
|
+
];
|
|
136
|
+
function hasStatusBoundEvidence(receipt) {
|
|
137
|
+
const dispatchFields = [
|
|
138
|
+
receipt.spawnDispatchMonotonicMs,
|
|
139
|
+
receipt.activationDeadlineMonotonicMs,
|
|
140
|
+
receipt.spawnDispatchedAt,
|
|
141
|
+
receipt.activationDeadlineAt
|
|
142
|
+
];
|
|
143
|
+
const dispatchEmpty = dispatchFields.every((field) => field === null);
|
|
144
|
+
const dispatchComplete = dispatchFields.every((field) => field !== null);
|
|
145
|
+
if (!dispatchEmpty && !dispatchComplete)
|
|
146
|
+
return false;
|
|
147
|
+
const beforeActivation = ['prepared', 'aborted', 'expired'].includes(receipt.status);
|
|
148
|
+
if (beforeActivation) {
|
|
149
|
+
return receipt.parentId === null
|
|
150
|
+
&& receipt.childId === null
|
|
151
|
+
&& receipt.spawnMode === null
|
|
152
|
+
&& receipt.actualModel === null
|
|
153
|
+
&& receipt.actualReasoningEffort === null
|
|
154
|
+
&& receipt.modelFallbackReason === null
|
|
155
|
+
&& receipt.reasoningEffortFallbackReason === null
|
|
156
|
+
&& receipt.agent === null
|
|
157
|
+
&& receipt.hostEvidence === null
|
|
158
|
+
&& receipt.startEvidenceMonotonicMs === null
|
|
159
|
+
&& receipt.activatedMonotonicMs === null
|
|
160
|
+
&& receipt.activatedAt === null
|
|
161
|
+
&& receipt.afterFingerprint === null
|
|
162
|
+
&& receipt.changedPaths.length === 0
|
|
163
|
+
&& receipt.sealedAt === null
|
|
164
|
+
&& receipt.consumedAt === null;
|
|
165
|
+
}
|
|
166
|
+
if (!dispatchComplete
|
|
167
|
+
|| !exactText(receipt.parentId)
|
|
168
|
+
|| !exactText(receipt.childId)
|
|
169
|
+
|| receipt.parentId === receipt.childId
|
|
170
|
+
|| receipt.startEvidenceMonotonicMs === null
|
|
171
|
+
|| receipt.activatedMonotonicMs === null
|
|
172
|
+
|| !exactText(receipt.activatedAt))
|
|
173
|
+
return false;
|
|
174
|
+
if (receipt.status === 'activated') {
|
|
175
|
+
return receipt.agent === null
|
|
176
|
+
&& receipt.afterFingerprint === null
|
|
177
|
+
&& receipt.changedPaths.length === 0
|
|
178
|
+
&& receipt.sealedAt === null
|
|
179
|
+
&& receipt.consumedAt === null;
|
|
180
|
+
}
|
|
181
|
+
if (!exactText(receipt.agent)
|
|
182
|
+
|| normalizeAgentToken(receipt.agent) !== normalizeAgentToken(receipt.client))
|
|
183
|
+
return false;
|
|
184
|
+
if (receipt.status === 'stage-completed') {
|
|
185
|
+
return receipt.afterFingerprint === null
|
|
186
|
+
&& receipt.changedPaths.length === 0
|
|
187
|
+
&& receipt.sealedAt === null
|
|
188
|
+
&& receipt.consumedAt === null;
|
|
189
|
+
}
|
|
190
|
+
if (!exactText(receipt.afterFingerprint) || !exactText(receipt.sealedAt))
|
|
191
|
+
return false;
|
|
192
|
+
return receipt.status === 'sealed'
|
|
193
|
+
? receipt.consumedAt === null
|
|
194
|
+
: receipt.status === 'consumed' && exactText(receipt.consumedAt);
|
|
195
|
+
}
|
|
196
|
+
function isDelegationReceipt(value) {
|
|
197
|
+
if (!hasExactKeys(value, RECEIPT_KEYS))
|
|
198
|
+
return false;
|
|
199
|
+
const structurallyValid = exactText(value.id)
|
|
200
|
+
&& exactText(value.taskId)
|
|
201
|
+
&& exactText(value.runId)
|
|
202
|
+
&& ['executor', 'reviewer'].includes(value.role)
|
|
203
|
+
&& ['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code', 'commit'].includes(value.stage)
|
|
204
|
+
&& Number.isSafeInteger(value.round)
|
|
205
|
+
&& value.round > 0
|
|
206
|
+
&& exactText(value.artifact)
|
|
207
|
+
&& isAgentClientId(value.client)
|
|
208
|
+
&& exactText(value.requestedModel)
|
|
209
|
+
&& exactText(value.requestedReasoningEffort)
|
|
210
|
+
&& nullableText(value.actualModel)
|
|
211
|
+
&& nullableText(value.actualReasoningEffort)
|
|
212
|
+
&& nullableText(value.modelFallbackReason)
|
|
213
|
+
&& nullableText(value.reasoningEffortFallbackReason)
|
|
214
|
+
&& nullableText(value.parentId)
|
|
215
|
+
&& nullableText(value.childId)
|
|
216
|
+
&& nullableText(value.spawnMode)
|
|
217
|
+
&& nullableText(value.agent)
|
|
218
|
+
&& ['prepared', 'activated', 'stage-completed', 'sealed', 'consumed', 'aborted', 'expired'].includes(value.status)
|
|
219
|
+
&& value.workspaceSnapshotScope === 'task'
|
|
220
|
+
&& (value.lifecycleProvenance === null || isDelegationLifecycleProvenance(value.lifecycleProvenance))
|
|
221
|
+
&& (value.hostEvidence === null || isDelegationHostEvidence(value.hostEvidence))
|
|
222
|
+
&& exactText(value.beforeFingerprint)
|
|
223
|
+
&& nullableText(value.afterFingerprint)
|
|
224
|
+
&& Array.isArray(value.changedPaths)
|
|
225
|
+
&& value.changedPaths.every(exactText)
|
|
226
|
+
&& exactText(value.createdAt)
|
|
227
|
+
&& nullableSafeInteger(value.preparedMonotonicMs)
|
|
228
|
+
&& value.preparedMonotonicMs !== null
|
|
229
|
+
&& nullableSafeInteger(value.spawnDispatchMonotonicMs)
|
|
230
|
+
&& nullableSafeInteger(value.activationDeadlineMonotonicMs)
|
|
231
|
+
&& nullableText(value.spawnDispatchedAt)
|
|
232
|
+
&& nullableText(value.activationDeadlineAt)
|
|
233
|
+
&& nullableSafeInteger(value.startEvidenceMonotonicMs)
|
|
234
|
+
&& nullableSafeInteger(value.activatedMonotonicMs)
|
|
235
|
+
&& nullableText(value.activatedAt)
|
|
236
|
+
&& nullableText(value.sealedAt)
|
|
237
|
+
&& nullableText(value.consumedAt);
|
|
238
|
+
if (!structurallyValid)
|
|
239
|
+
return false;
|
|
240
|
+
const receipt = value;
|
|
241
|
+
return hasStatusBoundEvidence(receipt)
|
|
242
|
+
&& (receipt.client !== 'codex' || hasCurrentCodexEvidence(receipt));
|
|
243
|
+
}
|
|
3
244
|
const MANAGED_AGENTS = {
|
|
4
245
|
'agent-infra-lifecycle-executor': 'executor',
|
|
5
246
|
'agent-infra-lifecycle-reviewer': 'reviewer'
|
|
@@ -10,6 +251,10 @@ function managedDelegationRole(nativeAgent) {
|
|
|
10
251
|
function fail(code, message) {
|
|
11
252
|
return { ok: false, code, message };
|
|
12
253
|
}
|
|
254
|
+
// 空白串折叠为 null,保持"缺失即 null"的单一表示(Start/Stop 两处共用,避免任一入口把 " " 当成已观察到的值入账)
|
|
255
|
+
function foldBlankToNull(value) {
|
|
256
|
+
return value?.trim() ? value : null;
|
|
257
|
+
}
|
|
13
258
|
function prepareDelegation(input, options = {}) {
|
|
14
259
|
const monotonic = (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1000000n)))();
|
|
15
260
|
return Object.freeze({
|
|
@@ -49,7 +294,7 @@ function dispatchDelegation(receipt, options = {}) {
|
|
|
49
294
|
}
|
|
50
295
|
const monotonic = (options.monotonicNow ?? (() => Number(process.hrtime.bigint() / 1000000n)))();
|
|
51
296
|
const dispatchedAt = (options.now ?? (() => new Date().toISOString()))();
|
|
52
|
-
const window = options.activationWindowMs ??
|
|
297
|
+
const window = options.activationWindowMs ?? 60_000;
|
|
53
298
|
return { ok: true, receipt: Object.freeze({
|
|
54
299
|
...receipt,
|
|
55
300
|
spawnDispatchMonotonicMs: monotonic,
|
|
@@ -82,30 +327,43 @@ function activateDelegation(receipt, event, options = {}) {
|
|
|
82
327
|
if (!event.parentId || (receipt.parentId !== null && event.parentId !== receipt.parentId) || !event.childId || event.childId === event.parentId) {
|
|
83
328
|
return fail('DELEGATION_IDENTITY_INVALID', 'native parent/child identity does not match the prepared delegation');
|
|
84
329
|
}
|
|
85
|
-
|
|
330
|
+
// spawnMode 的必需性按 client 判断:claude-code 宿主结构上不提供该字段,跳过;其余 client 维持硬校验
|
|
331
|
+
if (receipt.client !== 'claude-code' && event.spawnMode !== 'fresh') {
|
|
86
332
|
return fail('DELEGATION_FORK_FORBIDDEN', `spawn mode '${event.spawnMode}' is not fresh`);
|
|
87
|
-
const actualModel = event.actualModel;
|
|
88
|
-
if (!actualModel || actualModel.trim() !== actualModel) {
|
|
89
|
-
return fail('DELEGATION_MODEL_IDENTITY_MISSING', 'native start event must provide a non-empty actual model identity');
|
|
90
333
|
}
|
|
91
|
-
|
|
92
|
-
|
|
334
|
+
// model/effort 维度:claude-code 如实记录、不做门禁;其余 client 维持现状硬校验(HDR-2)
|
|
335
|
+
const actualModel = foldBlankToNull(event.actualModel);
|
|
336
|
+
const actualReasoningEffort = foldBlankToNull(event.actualReasoningEffort);
|
|
337
|
+
if (receipt.client !== 'claude-code') {
|
|
338
|
+
if (!actualModel || actualModel.trim() !== actualModel) {
|
|
339
|
+
return fail('DELEGATION_MODEL_IDENTITY_MISSING', 'native start event must provide a non-empty actual model identity');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (actualModel !== null && actualModel !== receipt.requestedModel) {
|
|
343
|
+
if (receipt.client !== 'claude-code' && (!event.modelFallbackReason || event.modelFallbackReason.trim() === '')) {
|
|
344
|
+
return fail('DELEGATION_MODEL_FALLBACK_UNRECORDED', 'actual model differs from requested model without a fallback reason');
|
|
345
|
+
}
|
|
93
346
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
347
|
+
if (receipt.client !== 'claude-code') {
|
|
348
|
+
if (!actualReasoningEffort || actualReasoningEffort.trim() !== actualReasoningEffort) {
|
|
349
|
+
return fail('DELEGATION_REASONING_EFFORT_MISSING', 'native start event must provide a non-empty actual reasoning effort');
|
|
350
|
+
}
|
|
97
351
|
}
|
|
98
352
|
if (receipt.client === 'codex'
|
|
99
353
|
&& event.hostEvidence?.kind !== 'codex-lifecycle-v2')
|
|
100
354
|
return fail('DELEGATION_HOST_EVIDENCE_REQUIRED', 'Codex activation requires trusted lifecycle-v2 host evidence');
|
|
101
|
-
if (actualReasoningEffort !== receipt.requestedReasoningEffort
|
|
102
|
-
&& (!event.reasoningEffortFallbackReason || event.reasoningEffortFallbackReason.trim() === '')) {
|
|
103
|
-
|
|
355
|
+
if (actualReasoningEffort !== null && actualReasoningEffort !== receipt.requestedReasoningEffort) {
|
|
356
|
+
if (receipt.client !== 'claude-code' && (!event.reasoningEffortFallbackReason || event.reasoningEffortFallbackReason.trim() === '')) {
|
|
357
|
+
return fail('DELEGATION_REASONING_EFFORT_FALLBACK_UNRECORDED', 'actual reasoning effort differs from requested effort without a fallback reason');
|
|
358
|
+
}
|
|
104
359
|
}
|
|
105
|
-
|
|
360
|
+
// "无关 fallback 理由"检查对所有 client 保持不变——防止编造理由的红线,不属于 HDR-2 放宽范围
|
|
361
|
+
// actualModel/actualReasoningEffort 为 null(未观察到)时同样没有"实际不同"这回事,理由一样是无关的
|
|
362
|
+
if ((actualModel === null || actualModel === receipt.requestedModel) && event.modelFallbackReason) {
|
|
106
363
|
return fail('DELEGATION_MODEL_FALLBACK_INVALID', 'model fallback reason is only valid when actual model differs');
|
|
107
364
|
}
|
|
108
|
-
if (actualReasoningEffort === receipt.requestedReasoningEffort
|
|
365
|
+
if ((actualReasoningEffort === null || actualReasoningEffort === receipt.requestedReasoningEffort)
|
|
366
|
+
&& event.reasoningEffortFallbackReason) {
|
|
109
367
|
return fail('DELEGATION_REASONING_EFFORT_FALLBACK_INVALID', 'reasoning-effort fallback reason is only valid when actual effort differs');
|
|
110
368
|
}
|
|
111
369
|
if (event.hostEvidence
|
|
@@ -145,7 +403,7 @@ function activateDelegation(receipt, event, options = {}) {
|
|
|
145
403
|
status: 'activated',
|
|
146
404
|
parentId: event.parentId,
|
|
147
405
|
childId: event.childId,
|
|
148
|
-
spawnMode: event.spawnMode,
|
|
406
|
+
spawnMode: event.spawnMode ?? null,
|
|
149
407
|
actualModel,
|
|
150
408
|
actualReasoningEffort,
|
|
151
409
|
modelFallbackReason: event.modelFallbackReason ?? null,
|
|
@@ -213,6 +471,10 @@ function sealDelegation(receipt, event, options = {}) {
|
|
|
213
471
|
status: 'sealed',
|
|
214
472
|
afterFingerprint: event.afterFingerprint,
|
|
215
473
|
changedPaths: Object.freeze([...event.changedPaths]),
|
|
474
|
+
actualModel: receipt.actualModel ?? foldBlankToNull(event.actualModel),
|
|
475
|
+
actualReasoningEffort: receipt.actualReasoningEffort ?? foldBlankToNull(event.actualReasoningEffort),
|
|
476
|
+
modelFallbackReason: receipt.modelFallbackReason ?? event.modelFallbackReason ?? null,
|
|
477
|
+
reasoningEffortFallbackReason: receipt.reasoningEffortFallbackReason ?? event.reasoningEffortFallbackReason ?? null,
|
|
216
478
|
hostEvidence: event.hostEvidence && receipt.hostEvidence ? Object.freeze({
|
|
217
479
|
...receipt.hostEvidence,
|
|
218
480
|
...event.hostEvidence
|
|
@@ -231,5 +493,5 @@ function consumeDelegation(receipt, options = {}) {
|
|
|
231
493
|
consumedAt: (options.now ?? (() => new Date().toISOString()))()
|
|
232
494
|
}) };
|
|
233
495
|
}
|
|
234
|
-
export { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, managedDelegationRole, prepareDelegation, sealDelegation };
|
|
496
|
+
export { activateDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation };
|
|
235
497
|
//# sourceMappingURL=delegation-receipts.js.map
|
package/dist/lib/task/events.js
CHANGED
|
@@ -12,6 +12,7 @@ import { validateLifecycleExecution } from './lifecycle-execution.js';
|
|
|
12
12
|
import { commitOrchestrationStageCompletion } from './orchestration.js';
|
|
13
13
|
import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
|
|
14
14
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
15
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
15
16
|
const eventCatalog = [
|
|
16
17
|
'analyze.started', 'analyze.awaiting-input', 'analyze.completed',
|
|
17
18
|
'review-analysis.started', 'review-analysis.completed',
|
|
@@ -22,7 +23,7 @@ const eventCatalog = [
|
|
|
22
23
|
'manual-validation.started', 'manual-validation.completed',
|
|
23
24
|
'validation-run.started', 'validation-run.completed'
|
|
24
25
|
];
|
|
25
|
-
const BASE_FIELDS = new Set(['taskRef', 'event', 'agent', 'dryRun']);
|
|
26
|
+
const BASE_FIELDS = new Set(['taskRef', 'event', 'agent', 'dryRun', 'overrideTicket', 'overrideTarget', 'overrideScope']);
|
|
26
27
|
const SCHEMAS = {
|
|
27
28
|
'analyze.started': { optional: ['round'] },
|
|
28
29
|
'analyze.awaiting-input': { required: ['question'] },
|
|
@@ -331,7 +332,8 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
331
332
|
const resolved = resolveTaskRef(request.taskRef, { repoRoot: options.repoRoot });
|
|
332
333
|
if (!resolved.ok)
|
|
333
334
|
return failed(request, { code: resolved.code, message: resolved.message }, { taskId: resolved.taskId });
|
|
334
|
-
|
|
335
|
+
const stateOverride = allowsManualOverride(options.manualOverride, 'task-event', 'TASK_STATE_MISMATCH');
|
|
336
|
+
if (resolved.state !== 'active' && !stateOverride)
|
|
335
337
|
return failed(request, { code: 'TASK_STATE_MISMATCH', message: `task ${resolved.taskId} is ${resolved.state}, expected active` }, { taskId: resolved.taskId, taskMdPath: resolved.taskMdPath });
|
|
336
338
|
let content;
|
|
337
339
|
try {
|
|
@@ -403,7 +405,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
403
405
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, done.done, frontmatter, artifactContext);
|
|
404
406
|
if (eventIdentity.phase === 'completed' && completedRows.length > 0 && openRows.length === 0)
|
|
405
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 });
|
|
406
|
-
if (eventIdentity.phase === 'completed' && !row?.started)
|
|
408
|
+
if (eventIdentity.phase === 'completed' && !row?.started && !allowsManualOverride(options.manualOverride, 'task-event', 'EVENT_START_MISSING'))
|
|
407
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 });
|
|
408
410
|
if (eventIdentity.phase === 'completed') {
|
|
409
411
|
const validated = validateCompletedArtifact(resolved.taskDir, FAMILY[eventIdentity.family].artifact, normalized.artifact, normalized.round);
|
|
@@ -416,7 +418,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
416
418
|
return successNoOp(normalized, resolved.taskId, resolved.taskMdPath, currentStep, eventIdentity, existing.time, frontmatter, artifactContext);
|
|
417
419
|
}
|
|
418
420
|
const allowed = FAMILY[eventIdentity.family][eventIdentity.phase === 'started' ? 'started' : 'completed'];
|
|
419
|
-
if (!allowed.includes(currentStep))
|
|
421
|
+
if (!allowed.includes(currentStep) && !allowsManualOverride(options.manualOverride, 'task-event', 'EVENT_TRANSITION_INVALID'))
|
|
420
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 });
|
|
421
423
|
const findingCountError = validateReviewFindingCounts(normalized, content, eventIdentity.family, completedArtifact?.path ?? null);
|
|
422
424
|
if (findingCountError)
|
|
@@ -528,7 +530,7 @@ function applyTaskEventUnlocked(request, options = {}) {
|
|
|
528
530
|
});
|
|
529
531
|
}
|
|
530
532
|
mutations.push({ kind: 'section', aliases: ['活动日志', 'Activity Log'], heading: section.heading, body });
|
|
531
|
-
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 });
|
|
532
534
|
if (result.status === 'failed')
|
|
533
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 });
|
|
534
536
|
if (!normalized.dryRun && orchestrationCompletion) {
|
|
@@ -568,6 +570,8 @@ function applyTaskEvent(request, options = {}) {
|
|
|
568
570
|
if (invalid)
|
|
569
571
|
return failed(request, invalid);
|
|
570
572
|
const parts = eventParts(request.event);
|
|
573
|
+
if (options.lockAlreadyHeld)
|
|
574
|
+
return applyTaskEventUnlocked(request, options);
|
|
571
575
|
if (request.dryRun || parts.phase !== 'completed' || parts.family === 'manual-validation') {
|
|
572
576
|
return applyTaskEventUnlocked(request, options);
|
|
573
577
|
}
|