@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.
Files changed (195) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/bin/internal-cli.js +11 -1
  4. package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
  5. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +60 -22
  6. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +175 -11
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
  10. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +86 -69
  11. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
  12. package/dist/lib/decide.js +68 -14
  13. package/dist/lib/git/workflow.js +10 -1
  14. package/dist/lib/init.js +1 -1
  15. package/dist/lib/internal/codex-lifecycle.js +19 -5
  16. package/dist/lib/internal/codex-sandbox-controller.js +6 -13
  17. package/dist/lib/internal/git-workflow.js +5 -7
  18. package/dist/lib/internal/platform-pr.js +20 -7
  19. package/dist/lib/internal/release-workflow.js +3 -4
  20. package/dist/lib/internal/sandbox-control.js +79 -2
  21. package/dist/lib/internal/task-activity.js +57 -7
  22. package/dist/lib/internal/task-event.js +57 -4
  23. package/dist/lib/internal/task-finalization.js +54 -0
  24. package/dist/lib/internal/task-ledger.js +59 -5
  25. package/dist/lib/internal/task-lifecycle.js +59 -8
  26. package/dist/lib/internal/task-orchestration.js +161 -288
  27. package/dist/lib/internal/task-override.js +131 -0
  28. package/dist/lib/internal/task-review.js +64 -6
  29. package/dist/lib/internal/task-verify.js +65 -3
  30. package/dist/lib/internal/task-warning.js +57 -4
  31. package/dist/lib/platform/pr-summary.js +53 -13
  32. package/dist/lib/platform/pull-requests.js +205 -41
  33. package/dist/lib/runtime/agent-runtime.js +56 -0
  34. package/dist/lib/sandbox/commands/create.js +389 -311
  35. package/dist/lib/sandbox/commands/rm.js +316 -55
  36. package/dist/lib/sandbox/control/client.js +190 -6
  37. package/dist/lib/sandbox/control/container-identity.js +1 -1
  38. package/dist/lib/sandbox/control/controller-registration.js +273 -0
  39. package/dist/lib/sandbox/control/executor.js +145 -5
  40. package/dist/lib/sandbox/control/lifecycle.js +395 -73
  41. package/dist/lib/sandbox/control/protocol.js +67 -6
  42. package/dist/lib/sandbox/control/state.js +27 -11
  43. package/dist/lib/sandbox/engine.js +14 -7
  44. package/dist/lib/sandbox/index.js +5 -3
  45. package/dist/lib/sandbox/managed-fs.js +5 -0
  46. package/dist/lib/sandbox/mounts.js +20 -5
  47. package/dist/lib/sandbox/recovery.js +78 -10
  48. package/dist/lib/sandbox/task-resolver.js +50 -6
  49. package/dist/lib/sandbox/workspace-identity.js +27 -0
  50. package/dist/lib/sandbox/workspace-view.js +62 -40
  51. package/dist/lib/sandbox/worktree-safety.js +215 -2
  52. package/dist/lib/server/process-state.js +51 -1
  53. package/dist/lib/task/activity-intent.js +4 -1
  54. package/dist/lib/task/codex-orchestration.js +158 -33
  55. package/dist/lib/task/commands/status.js +59 -1
  56. package/dist/lib/task/commit-operation.js +335 -0
  57. package/dist/lib/task/commit-policy.js +18 -0
  58. package/dist/lib/task/decision-intents.js +4 -2
  59. package/dist/lib/task/delegation-receipts.js +279 -17
  60. package/dist/lib/task/events.js +9 -5
  61. package/dist/lib/task/finalization.js +567 -0
  62. package/dist/lib/task/guard-override.js +31 -0
  63. package/dist/lib/task/human-override.js +1240 -0
  64. package/dist/lib/task/ledger-intents.js +6 -3
  65. package/dist/lib/task/lifecycle.js +69 -14
  66. package/dist/lib/task/operation-outcome.js +33 -0
  67. package/dist/lib/task/orchestration.js +199 -719
  68. package/dist/lib/task/review-finalization.js +3 -2
  69. package/dist/lib/task/short-id.js +59 -1
  70. package/dist/lib/task/task-execution-lock.js +6 -1
  71. package/dist/lib/task/verification-engine.js +138 -59
  72. package/dist/lib/task/verification.js +15 -2
  73. package/dist/lib/task/workflow-warning-intents.js +54 -3
  74. package/dist/lib/task/write.js +2 -1
  75. package/dist/lifecycle-build-manifest.json +3 -3
  76. package/lib/agent-clients/adapters/claude-code.ts +3 -3
  77. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +63 -20
  78. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
  79. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +278 -13
  80. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
  81. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
  82. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
  83. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +112 -93
  84. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
  85. package/lib/decide.ts +59 -14
  86. package/lib/git/workflow.ts +11 -2
  87. package/lib/init.ts +1 -1
  88. package/lib/internal/codex-lifecycle.ts +18 -5
  89. package/lib/internal/codex-sandbox-controller.ts +6 -12
  90. package/lib/internal/git-workflow.ts +5 -6
  91. package/lib/internal/platform-pr.ts +14 -7
  92. package/lib/internal/release-workflow.ts +3 -4
  93. package/lib/internal/sandbox-control.ts +92 -2
  94. package/lib/internal/task-activity.ts +46 -6
  95. package/lib/internal/task-event.ts +53 -4
  96. package/lib/internal/task-finalization.ts +68 -0
  97. package/lib/internal/task-ledger.ts +48 -4
  98. package/lib/internal/task-lifecycle.ts +56 -12
  99. package/lib/internal/task-orchestration.ts +32 -107
  100. package/lib/internal/task-override.ts +138 -0
  101. package/lib/internal/task-review.ts +51 -6
  102. package/lib/internal/task-verify.ts +52 -3
  103. package/lib/internal/task-warning.ts +44 -3
  104. package/lib/platform/pr-summary.ts +65 -14
  105. package/lib/platform/pull-requests.ts +211 -41
  106. package/lib/runtime/agent-runtime.ts +76 -0
  107. package/lib/sandbox/commands/create.ts +88 -4
  108. package/lib/sandbox/commands/rm.ts +413 -61
  109. package/lib/sandbox/config.ts +2 -1
  110. package/lib/sandbox/control/client.ts +255 -6
  111. package/lib/sandbox/control/container-identity.ts +3 -3
  112. package/lib/sandbox/control/controller-registration.ts +347 -0
  113. package/lib/sandbox/control/executor.ts +172 -6
  114. package/lib/sandbox/control/lifecycle.ts +465 -77
  115. package/lib/sandbox/control/protocol.ts +85 -9
  116. package/lib/sandbox/control/state.ts +35 -18
  117. package/lib/sandbox/engine.ts +20 -9
  118. package/lib/sandbox/index.ts +5 -3
  119. package/lib/sandbox/managed-fs.ts +5 -0
  120. package/lib/sandbox/mounts.ts +23 -5
  121. package/lib/sandbox/recovery.ts +90 -9
  122. package/lib/sandbox/task-resolver.ts +58 -7
  123. package/lib/sandbox/workspace-identity.ts +50 -2
  124. package/lib/sandbox/workspace-view.ts +73 -43
  125. package/lib/sandbox/worktree-safety.ts +236 -2
  126. package/lib/server/process-state.ts +48 -1
  127. package/lib/task/activity-intent.ts +4 -2
  128. package/lib/task/codex-orchestration.ts +167 -33
  129. package/lib/task/commands/status.ts +78 -1
  130. package/lib/task/commit-operation.ts +430 -0
  131. package/lib/task/commit-policy.ts +30 -0
  132. package/lib/task/decision-intents.ts +4 -2
  133. package/lib/task/delegation-receipts.ts +304 -19
  134. package/lib/task/events.ts +10 -5
  135. package/lib/task/finalization.ts +719 -0
  136. package/lib/task/guard-override.ts +50 -0
  137. package/lib/task/human-override.ts +1503 -0
  138. package/lib/task/ledger-intents.ts +6 -3
  139. package/lib/task/lifecycle.ts +71 -14
  140. package/lib/task/operation-outcome.ts +48 -0
  141. package/lib/task/orchestration.ts +239 -922
  142. package/lib/task/review-finalization.ts +6 -2
  143. package/lib/task/short-id.ts +65 -1
  144. package/lib/task/task-execution-lock.ts +10 -1
  145. package/lib/task/verification-engine.ts +145 -64
  146. package/lib/task/verification.ts +18 -3
  147. package/lib/task/workflow-warning-intents.ts +61 -4
  148. package/lib/task/write.ts +4 -1
  149. package/package.json +2 -2
  150. package/templates/.agents/QUICKSTART.en.md +2 -2
  151. package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
  152. package/templates/.agents/hooks/lifecycle-delegation.js +9 -24
  153. package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -3
  154. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -3
  155. package/templates/.agents/rules/task-management.en.md +2 -2
  156. package/templates/.agents/rules/task-management.zh-CN.md +2 -2
  157. package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
  158. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
  159. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
  160. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
  161. package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
  162. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
  163. package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
  164. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
  165. package/templates/.agents/skills/commit/SKILL.en.md +41 -125
  166. package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
  167. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  168. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  169. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
  170. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
  171. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  172. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  173. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
  174. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
  175. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
  176. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
  177. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  178. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  179. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
  180. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
  181. package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
  182. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
  183. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
  184. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
  185. package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
  186. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
  187. package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -1
  188. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -1
  189. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
  190. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
  191. package/templates/.codex/hooks.json +4 -4
  192. package/dist/lib/task/commit-finalization.js +0 -305
  193. package/dist/lib/task/commit-intent.js +0 -165
  194. package/lib/task/commit-finalization.ts +0 -419
  195. 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 ?? 15_000;
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
- if (event.spawnMode !== 'fresh')
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
- if (actualModel !== receipt.requestedModel && (!event.modelFallbackReason || event.modelFallbackReason.trim() === '')) {
92
- return fail('DELEGATION_MODEL_FALLBACK_UNRECORDED', 'actual model differs from requested model without a fallback reason');
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
- const actualReasoningEffort = event.actualReasoningEffort;
95
- if (!actualReasoningEffort || actualReasoningEffort.trim() !== actualReasoningEffort) {
96
- return fail('DELEGATION_REASONING_EFFORT_MISSING', 'native start event must provide a non-empty actual reasoning effort');
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
- return fail('DELEGATION_REASONING_EFFORT_FALLBACK_UNRECORDED', 'actual reasoning effort differs from requested effort without a fallback reason');
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
- if (actualModel === receipt.requestedModel && event.modelFallbackReason) {
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 && event.reasoningEffortFallbackReason) {
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
@@ -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
- if (resolved.state !== 'active')
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
  }