@sema-agent/server 7.43.0 → 7.44.0

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 (345) hide show
  1. package/USAGE.md +18 -0
  2. package/dist/adoption/plan.js +31 -192
  3. package/dist/adoption/quiesce.js +3 -86
  4. package/dist/adoption/runner.js +3 -140
  5. package/dist/adoption/sql.js +0 -74
  6. package/dist/adoption/wire.js +0 -80
  7. package/dist/approval-ask-machine.js +0 -75
  8. package/dist/approval-card.js +0 -323
  9. package/dist/approval-deny-reasons.js +0 -42
  10. package/dist/approval-hmac.js +0 -35
  11. package/dist/approval-reconciler.js +8 -166
  12. package/dist/approval.js +4 -67
  13. package/dist/audit.js +1 -44
  14. package/dist/auth-bridge.js +4 -47
  15. package/dist/auth-keys.js +0 -23
  16. package/dist/bake-runner/main.js +4 -65
  17. package/dist/bake-runner/protocol.js +3 -58
  18. package/dist/bake-runner/runner.js +5 -91
  19. package/dist/bench/l8/artifact.js +2 -61
  20. package/dist/bench/l8/escape.js +0 -25
  21. package/dist/bench/l8/index.js +0 -14
  22. package/dist/bench/l8/probes.js +0 -60
  23. package/dist/bench/l8/run-probes.js +2 -45
  24. package/dist/bench/s1/arms.js +14 -189
  25. package/dist/bench/s1/live-deps.js +11 -253
  26. package/dist/bench/s1/oracle.js +1 -36
  27. package/dist/bench/s1/repair-oracle-adapter.js +0 -30
  28. package/dist/bench/s1/reviewer.js +0 -34
  29. package/dist/bench/s1/row.js +0 -62
  30. package/dist/bench/s1/run-firm.js +4 -70
  31. package/dist/bench/s1/runner-ctx.js +0 -40
  32. package/dist/bench/s1/tasks.js +0 -115
  33. package/dist/boot/adoption.js +0 -20
  34. package/dist/boot/budget-tracing.js +3 -55
  35. package/dist/boot/config-center.js +43 -551
  36. package/dist/boot/coordinators.js +1 -69
  37. package/dist/boot/crash-last.js +0 -18
  38. package/dist/boot/deferred-sandbox-path-env.js +3 -119
  39. package/dist/boot/execution-env.js +5 -171
  40. package/dist/boot/governance-seams.js +0 -92
  41. package/dist/boot/leader.js +0 -69
  42. package/dist/boot/limit-sync.js +0 -2
  43. package/dist/boot/memory-boundary.js +3 -91
  44. package/dist/boot/org-memory.js +1 -25
  45. package/dist/boot/parked-revive-gate.js +0 -150
  46. package/dist/boot/permission-rules-audit.js +1 -56
  47. package/dist/boot/reapers.js +15 -273
  48. package/dist/boot/resolve-spec.js +9 -768
  49. package/dist/boot/retention-lane.js +4 -87
  50. package/dist/boot/runner-deps.js +3 -297
  51. package/dist/boot/runtime-caps.js +0 -40
  52. package/dist/boot/session-faces.js +2 -133
  53. package/dist/boot/shutdown.js +9 -83
  54. package/dist/boot/side-query-lane.js +2 -137
  55. package/dist/boot/stores.js +15 -215
  56. package/dist/boot/task-list-lane.js +0 -18
  57. package/dist/boot/webfetch-summarize-lane.js +0 -47
  58. package/dist/boot/workflow-orchestration.js +3 -99
  59. package/dist/boot-reclaim.js +0 -20
  60. package/dist/bounded-session-map.js +0 -19
  61. package/dist/brain.js +2 -139
  62. package/dist/budget.js +9 -244
  63. package/dist/capabilities/builtin-tools.js +0 -3
  64. package/dist/capabilities/center-plugins.js +6 -51
  65. package/dist/capabilities/center-prompts.js +6 -71
  66. package/dist/capabilities/code-review-council.js +7 -40
  67. package/dist/capabilities/collab-workflows.js +1 -44
  68. package/dist/capabilities/hands-lane.js +0 -65
  69. package/dist/capabilities/memory-notice.js +0 -70
  70. package/dist/capabilities/prompt.js +0 -14
  71. package/dist/capabilities/prompts/code-review.js +0 -14
  72. package/dist/capabilities/prompts/identity.js +0 -6
  73. package/dist/capabilities/prompts/team.js +0 -4
  74. package/dist/capabilities/repo-tools.js +0 -34
  75. package/dist/capabilities/sandbox-file-send.js +3 -92
  76. package/dist/capabilities/scenarios.d.ts +0 -1
  77. package/dist/capabilities/scenarios.js +18 -279
  78. package/dist/capabilities/select-environment-tool.js +0 -32
  79. package/dist/capabilities/send-user-file-tool.js +2 -66
  80. package/dist/capabilities/skills.d.ts +1 -2
  81. package/dist/capabilities/skills.js +3 -21
  82. package/dist/capabilities/team.d.ts +3 -11
  83. package/dist/capabilities/team.js +3 -51
  84. package/dist/capabilities/tool-defer.js +0 -3
  85. package/dist/config-center/apply-effective.js +15 -330
  86. package/dist/config-center/apply-ledger.js +5 -38
  87. package/dist/config-center/facade.js +0 -41
  88. package/dist/config-center/hot-keys-registry.js +0 -20
  89. package/dist/config-center/http-client.js +2 -125
  90. package/dist/config-center/mcp-revocation.js +2 -34
  91. package/dist/config-center/read-face.js +0 -59
  92. package/dist/config-center/restart-signal.js +1 -85
  93. package/dist/config-center/skills-mcp.d.ts +1 -1
  94. package/dist/config-center/skills-mcp.js +9 -76
  95. package/dist/config-center/stage-limits.js +8 -35
  96. package/dist/config-invariants.js +0 -16
  97. package/dist/config-lkg.js +0 -42
  98. package/dist/config-provider.js +3 -186
  99. package/dist/config-types.js +0 -5
  100. package/dist/config.js +107 -1145
  101. package/dist/degenerate-instrument.js +3 -67
  102. package/dist/deployment-governance.js +0 -124
  103. package/dist/digest-form.js +0 -12
  104. package/dist/elicitation.js +3 -86
  105. package/dist/env-facts.js +7 -75
  106. package/dist/fleet/fleet-bus.js +34 -507
  107. package/dist/fleet/fleet-reconciler.js +9 -149
  108. package/dist/fleet/fleet-terminal-window.js +10 -178
  109. package/dist/fleet/subagent-tail-bus.js +3 -72
  110. package/dist/fleet-client.js +10 -70
  111. package/dist/fleet-lease.js +5 -79
  112. package/dist/git-api-kind.js +0 -3
  113. package/dist/governance-ask-marks.js +2 -78
  114. package/dist/hooks/branch-transcript.js +0 -74
  115. package/dist/hooks/cc-agent-hook-prompt.js +0 -29
  116. package/dist/hooks/cc-stop-prompt.js +1 -46
  117. package/dist/hooks/hook-llm.js +1 -53
  118. package/dist/hooks/hook-runner.js +20 -414
  119. package/dist/http/active-run-conflict.js +4 -129
  120. package/dist/http/cursor-fingerprint.d.ts +5 -0
  121. package/dist/http/cursor-fingerprint.js +5 -0
  122. package/dist/http/idempotency.js +0 -37
  123. package/dist/http/principal-gate.js +3 -40
  124. package/dist/http/route-ctx.js +0 -9
  125. package/dist/http/routes/a2a-serve.js +7 -319
  126. package/dist/http/routes/admin-config-refresh.js +0 -4
  127. package/dist/http/routes/admin-drain.js +0 -10
  128. package/dist/http/routes/adoption.js +1 -29
  129. package/dist/http/routes/agents-roster.js +1 -47
  130. package/dist/http/routes/approvals-assistant.js +32 -395
  131. package/dist/http/routes/attachments.js +4 -22
  132. package/dist/http/routes/capabilities.js +3 -420
  133. package/dist/http/routes/diagnostics.js +1 -81
  134. package/dist/http/routes/fleet.js +9 -185
  135. package/dist/http/routes/images.js +14 -239
  136. package/dist/http/routes/leader.js +0 -13
  137. package/dist/http/routes/memory-bundle.js +1 -61
  138. package/dist/http/routes/memory-policy.js +9 -99
  139. package/dist/http/routes/notify-wake.js +3 -37
  140. package/dist/http/routes/observability.js +2 -19
  141. package/dist/http/routes/retention-ops.js +2 -34
  142. package/dist/http/routes/rules.js +1 -93
  143. package/dist/http/routes/runs.js +64 -873
  144. package/dist/http/routes/session-sync.js +19 -258
  145. package/dist/http/routes/sessions-list.js +17 -43
  146. package/dist/http/routes/sessions.js +34 -226
  147. package/dist/http/routes/shared-memory.js +5 -36
  148. package/dist/http/routes/side-query.js +1 -87
  149. package/dist/http/routes/tasks.js +52 -722
  150. package/dist/http/routes/trace-usage.js +32 -195
  151. package/dist/http/routes/workflows.js +19 -195
  152. package/dist/http/run-meta.js +0 -6
  153. package/dist/http/send.js +0 -32
  154. package/dist/http/server.js +92 -1597
  155. package/dist/http/sse-lifecycle.js +2 -13
  156. package/dist/http/sse-log.js +3 -48
  157. package/dist/http/tar.js +5 -21
  158. package/dist/http/verify-rounds.js +0 -5
  159. package/dist/http/wire-gate.js +0 -9
  160. package/dist/http/workspace-content.js +0 -10
  161. package/dist/images/bake-validate.js +1 -70
  162. package/dist/images/manifest.js +1 -6
  163. package/dist/index.js +0 -21
  164. package/dist/key-resolver.js +2 -17
  165. package/dist/leader/diffout.js +1 -20
  166. package/dist/leader/diffup.js +0 -47
  167. package/dist/leader/endpoint.js +2 -57
  168. package/dist/leader/fanout.js +3 -45
  169. package/dist/leader/grader-env-factory.js +3 -72
  170. package/dist/leader/leader.js +5 -156
  171. package/dist/leader/merge.js +8 -115
  172. package/dist/leader/planner.js +3 -54
  173. package/dist/leader/repair-oracle.js +1 -60
  174. package/dist/leader/repair-wire.js +2 -79
  175. package/dist/leader/wire.js +8 -307
  176. package/dist/lsp/e2b-bridge.js +4 -64
  177. package/dist/lsp/e2b-manager.js +6 -94
  178. package/dist/lsp/lsp-frames.js +0 -12
  179. package/dist/lsp/manager.js +4 -96
  180. package/dist/lsp/ws-transport.js +5 -55
  181. package/dist/lsp-evict.js +1 -15
  182. package/dist/main.js +57 -765
  183. package/dist/memory-bundle-engine.js +0 -55
  184. package/dist/memory-export.js +0 -4
  185. package/dist/memory-posture.js +1 -15
  186. package/dist/memory-scope.js +11 -148
  187. package/dist/memory-sync-client.js +2 -44
  188. package/dist/memory-sync.js +1 -80
  189. package/dist/model-select.js +3 -80
  190. package/dist/observability/cost-quota.js +1 -17
  191. package/dist/observability/cost-taxonomy.js +0 -34
  192. package/dist/observability/fail-open.js +7 -86
  193. package/dist/observability/logger.js +0 -6
  194. package/dist/observability/metrics.js +0 -94
  195. package/dist/observability/otel-exporter.js +3 -13
  196. package/dist/observability/principal-context.js +0 -9
  197. package/dist/observability/prompt-manifest.js +1 -37
  198. package/dist/observability/rate-limit.js +0 -4
  199. package/dist/observability/secret-env-scrub.js +2 -56
  200. package/dist/observability/tool-trace.js +1 -70
  201. package/dist/orchestration/hardened-vm-runner.js +4 -118
  202. package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
  203. package/dist/orchestration/hardened-vm-worker.js +0 -27
  204. package/dist/orchestration/subagent-steer.js +1 -45
  205. package/dist/orchestration/workflow-agent-steer.js +1 -80
  206. package/dist/orchestration/workflow-completion-inbox.js +32 -285
  207. package/dist/orchestration/workflow-notify-journal.js +16 -259
  208. package/dist/org-memory-admission.js +3 -47
  209. package/dist/parent-watch.js +2 -48
  210. package/dist/parked-decide.js +1 -109
  211. package/dist/per-task-image.js +0 -57
  212. package/dist/plan-cache-probe.js +3 -27
  213. package/dist/plugins/adoption-log-sql.js +2 -119
  214. package/dist/plugins/approval-ask-store-memory.js +3 -38
  215. package/dist/plugins/approval-ask-store-sql.js +6 -188
  216. package/dist/plugins/approval-exemption-store.js +2 -28
  217. package/dist/plugins/background-agent-store-sql.js +4 -105
  218. package/dist/plugins/background-shell-support.js +14 -122
  219. package/dist/plugins/blob-backend.js +6 -169
  220. package/dist/plugins/breaker-state-sql.js +8 -46
  221. package/dist/plugins/caching-session-store.js +4 -106
  222. package/dist/plugins/checkpoint-store-sql.js +18 -505
  223. package/dist/plugins/e2b-orphan-reclaim.js +0 -45
  224. package/dist/plugins/file-outcome-sink.js +0 -9
  225. package/dist/plugins/file-resume-anchor-store.js +4 -43
  226. package/dist/plugins/file-run-store.js +26 -364
  227. package/dist/plugins/file-snapshot-store-sql.js +10 -181
  228. package/dist/plugins/fork-routing-session-store.js +8 -111
  229. package/dist/plugins/host-platform.js +2 -91
  230. package/dist/plugins/image-bake-store-sql.js +7 -250
  231. package/dist/plugins/image-index-sql.js +4 -123
  232. package/dist/plugins/k8s-bg-scripts.js +4 -88
  233. package/dist/plugins/k8s-exec-protocol.js +0 -41
  234. package/dist/plugins/leader-run-store-sql.js +0 -101
  235. package/dist/plugins/local-checkpoint-store.js +8 -128
  236. package/dist/plugins/local-session-store.js +34 -296
  237. package/dist/plugins/local-task-attachment-store.js +2 -16
  238. package/dist/plugins/mailbox-store-sql.js +8 -65
  239. package/dist/plugins/memory-embedder-fingerprint.js +5 -166
  240. package/dist/plugins/memory-embedder.js +4 -70
  241. package/dist/plugins/memory-engine-pg.js +6 -170
  242. package/dist/plugins/memory-engine-tidb.js +7 -155
  243. package/dist/plugins/memory-engine-vector-util.js +0 -10
  244. package/dist/plugins/memory-key-guards.js +0 -34
  245. package/dist/plugins/memory-origin-law.js +0 -187
  246. package/dist/plugins/memory-resume-anchor-store.js +0 -17
  247. package/dist/plugins/memory-run-store.js +12 -89
  248. package/dist/plugins/memory-session-policy-store.js +0 -17
  249. package/dist/plugins/memory-sync-store-pg.js +4 -49
  250. package/dist/plugins/memory-sync-store-tidb.js +3 -35
  251. package/dist/plugins/outcome-ledger-sql.js +3 -97
  252. package/dist/plugins/permission-rule-store-file.js +5 -133
  253. package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
  254. package/dist/plugins/permission-rule-store-sql.js +50 -307
  255. package/dist/plugins/pg-cost-quota.js +0 -7
  256. package/dist/plugins/pg-pool.js +0 -92
  257. package/dist/plugins/pg-rate-limiter.js +2 -13
  258. package/dist/plugins/pg-safe-json.js +4 -40
  259. package/dist/plugins/pg-session-storage.js +25 -189
  260. package/dist/plugins/posix-shell-fs.js +1 -31
  261. package/dist/plugins/remote-env-adb.js +12 -101
  262. package/dist/plugins/remote-env-e2b.js +40 -371
  263. package/dist/plugins/remote-env-file-error.js +0 -37
  264. package/dist/plugins/remote-env-host.js +63 -483
  265. package/dist/plugins/remote-env-k8s.js +32 -326
  266. package/dist/plugins/remote-env-local-docker.js +24 -157
  267. package/dist/plugins/remote-env-ssh.js +20 -128
  268. package/dist/plugins/remote-scratchpad.js +2 -32
  269. package/dist/plugins/remote-shell.js +1 -32
  270. package/dist/plugins/resume-anchor-store-sql.js +0 -11
  271. package/dist/plugins/retention-lane-store-sql.js +0 -108
  272. package/dist/plugins/retention-store-sql.js +4 -383
  273. package/dist/plugins/roster-store-sql.js +0 -55
  274. package/dist/plugins/run-store-sql.js +14 -251
  275. package/dist/plugins/s3-presign.js +2 -49
  276. package/dist/plugins/scheduler-support.js +3 -80
  277. package/dist/plugins/send-file-ledger.js +4 -53
  278. package/dist/plugins/send-user-file.js +4 -94
  279. package/dist/plugins/session-placement.js +1 -89
  280. package/dist/plugins/session-policy-store-sql.js +3 -81
  281. package/dist/plugins/session-store.js +0 -57
  282. package/dist/plugins/shared-memory-store-sql.js +4 -167
  283. package/dist/plugins/sql-driver.js +0 -17
  284. package/dist/plugins/sql-errors.js +0 -7
  285. package/dist/plugins/sql-escape.js +0 -8
  286. package/dist/plugins/sql-row-helpers.js +0 -25
  287. package/dist/plugins/store-backend.js +42 -222
  288. package/dist/plugins/store-contracts.js +2 -46
  289. package/dist/plugins/task-attachment-store.js +3 -49
  290. package/dist/plugins/task-list-store-sql.js +0 -79
  291. package/dist/plugins/tidb-cost-quota.js +1 -4
  292. package/dist/plugins/tidb-pool.js +1 -207
  293. package/dist/plugins/tidb-rate-limiter.js +3 -9
  294. package/dist/plugins/tidb-session-storage.js +4 -70
  295. package/dist/plugins/tidb-session-store.js +19 -341
  296. package/dist/plugins/tool-result-store-sql.js +4 -179
  297. package/dist/plugins/usage-window-store-sql.js +0 -8
  298. package/dist/plugins/web-search.js +10 -132
  299. package/dist/plugins/workflow-journal-store-sql.js +2 -58
  300. package/dist/plugins/workflow-run-store-sql.js +6 -91
  301. package/dist/plugins/worktree-isolation.js +6 -126
  302. package/dist/plugins/write-behind-counter.js +16 -75
  303. package/dist/principal-jwt.js +5 -60
  304. package/dist/project-memory.js +15 -146
  305. package/dist/prompts-domain-validate.js +1 -51
  306. package/dist/question.js +2 -111
  307. package/dist/resource-suspend.js +0 -18
  308. package/dist/router/route-orchestration.js +0 -77
  309. package/dist/rules-consent.d.ts +44 -5
  310. package/dist/rules-consent.js +75 -215
  311. package/dist/run-local.js +14 -381
  312. package/dist/runs.js +29 -617
  313. package/dist/runtime-caps-resolver.js +6 -132
  314. package/dist/runtime-governance.js +1 -232
  315. package/dist/sandbox-pkg-source.js +0 -37
  316. package/dist/sealed-key.js +3 -68
  317. package/dist/security.js +6 -291
  318. package/dist/session-leaf-bus.js +0 -32
  319. package/dist/session-sync-content.js +1 -69
  320. package/dist/session-sync-kernel.js +3 -60
  321. package/dist/session-sync.js +3 -66
  322. package/dist/session-titler.js +7 -41
  323. package/dist/session-watch.js +9 -73
  324. package/dist/shared-memory-scope-authorizer.js +0 -18
  325. package/dist/sighup-idle.js +1 -12
  326. package/dist/spec-fields.js +4 -143
  327. package/dist/store-live-probe.js +3 -49
  328. package/dist/task-a2a.js +1 -126
  329. package/dist/task-cwd.js +1 -103
  330. package/dist/task-mcp.js +1 -89
  331. package/dist/task-settings.js +10 -300
  332. package/dist/task-workflow.js +8 -75
  333. package/dist/tool-approval.d.ts +28 -1
  334. package/dist/tool-approval.js +70 -1430
  335. package/dist/trace/artifacts.js +5 -20
  336. package/dist/trace/engine-notice-wire.js +6 -138
  337. package/dist/trace/ledger-sink.js +7 -96
  338. package/dist/trace/project.js +4 -448
  339. package/dist/trace/redact.js +9 -77
  340. package/dist/turn-activity.js +1 -27
  341. package/dist/usage-analytics.js +4 -34
  342. package/dist/wall-clock-jump-guard.js +1 -68
  343. package/package.json +3 -3
  344. package/dist/capabilities/scenario-alias.d.ts +0 -27
  345. package/dist/capabilities/scenario-alias.js +0 -61
@@ -1,48 +1,24 @@
1
- // 值导入(非 `import type`):`settledBy`/`resolution` 的闭集判据必须**复用 core 的谓词**——本仓抄
2
- // 一份词表就是值拷贝漂移源(上游加员时两处静默不同步)。settledBy=core 5.18.1 #187;
3
- // resolution 三席=core 5.52.0 根导出([4819] 应诺兑现,[4820] 预挂的闭集换装即此)。
4
1
  import { isApprovalSettledBy, isAskDenyResolution } from "@sema-agent/core";
5
2
  import { redactSecrets, redactDeep } from "./redact.js";
6
- /** E8 (shell-host): drain the per-task model-usage accumulator into an append-only `model_usage` DELTA event.
7
- * Shared by BOTH durable-write legs (runInBackground + the resume leg) so the persisted shape never drifts; the
8
- * append-only deltas make a suspended/resumed run's total resume-safe (each leg appends its own, the echo sums).
9
- *
10
- * core 3.0.0([2296] 装车单 S10,§DESIGN-V2 V2-⑤): every drained row is tagged with `usageBasis:
11
- * "uncached-components-v1"` — the write point for this label (the tracker itself, {@link ModelUsageTracker},
12
- * never sets it; it stays a pure token/cost accumulator). This is what lets {@link aggregateModelUsage} tell
13
- * apart rows written under the current component semantics from older rows (pre-dating this label) when it
14
- * sums across a run's whole event log. */
15
3
  export async function appendModelUsageDelta(append, modelUsage, taskId) {
16
4
  const delta = modelUsage?.drain(taskId);
17
5
  if (delta) {
18
6
  const tagged = {};
19
- // [2373]A-4 记账(2026-08-02):usageBasis(与 TraceTurn.tokens.uncachedIn 同族)五端+黑盒仓零运行时读者——
20
- // 3.0.0 cache 语义翻转的诚实位,契约正确但暂无消费;web/desktop 接账单面时它是现成的。
21
7
  for (const [model, d] of Object.entries(delta))
22
8
  tagged[model] = { ...d, usageBasis: "uncached-components-v1" };
23
9
  await append("model_usage", { usage: tagged });
24
10
  }
25
11
  }
26
- /** [998]② drain the pending prompt-manifest records (whitelisted at RECORD time, budget.ts — ids/digests/
27
- * counts only, no prompt bodies by core's manifest contract) into durable `prompt_assembled` events, one per
28
- * prepare IN EMISSION ORDER (a cascade/verify leg re-prepares under one taskId — every rung's manifest
29
- * persists; release-review finding). Shared by both durable-write legs; a no-op when nothing is pending
30
- * (one Map.get), so callers fire it liberally. */
31
12
  export async function appendPromptManifest(append, tracker, taskId) {
32
13
  const records = tracker?.drain(taskId);
33
14
  if (records)
34
15
  for (const m of records)
35
16
  await append("prompt_assembled", m);
36
- // [1301]③:config.assembled 同拍同门落 durable(config_assembled 事件)——与 prompt_assembled 完全同
37
- // 生命周期(register 门/lease clear/所有 drain 落点自动覆盖:bg 首跑、resume 两径、verify 径);
38
- // 逐条保序(codex F2:cascade 每 rung 的生效配置各自成帧,与其 prompt manifest 可配对)。
39
17
  const cfgs = tracker?.drainConfig?.(taskId);
40
18
  if (cfgs)
41
19
  for (const cfg of cfgs)
42
20
  await append("config_assembled", cfg);
43
21
  }
44
- /** E8: at a TERMINAL done — flush the final delta, then SUM all `model_usage` events (every process leg) into the
45
- * result's `stats.modelUsage` echo. Returns the result unchanged when no usage was tracked (shell soft-degrades). */
46
22
  export async function attachModelUsage(result, ctx) {
47
23
  if (!ctx.modelUsage)
48
24
  return result;
@@ -52,35 +28,10 @@ export async function attachModelUsage(result, ctx) {
52
28
  return result;
53
29
  return { ...result, stats: { ...result.stats, modelUsage: usage } };
54
30
  }
55
- /** E8 (shell-host contract): SUM the per-turn `model_usage` delta events in a run's durable event log into the
56
- * cumulative per-model total for `TaskStats.modelUsage`. The tracer drains deltas at each turn boundary
57
- * (append-only) → this is resume-safe: it sums across ALL process legs of a suspended/resumed run. Returns
58
- * undefined when the run logged no model usage (→ the shell soft-degrades to aggregate cost only).
59
- *
60
- * #59([2052]①):事件体是 **DB 里的 JSON**(旧版本 / 别的 producer 都可能写),此前的 `?? 0` 只接住
61
- * null/undefined——NaN 与字符串会原样进 `+`,一个坏键就把该模型的整 run 总量变成 NaN 或字符串拼接,
62
- * 再顺着 `TaskStats.modelUsage` 回声流进 usage-analytics 的整窗聚合。守卫改「非有限数 = 缺席、按 0 计」,
63
- * 与 usage-analytics 读侧同口径。
64
- *
65
- * 🔴 RB-368(core 2.3.0,[2083] 提货单①)——**缺席必须活着走完整条链**。此前本折叠对「costMicroUsd
66
- * 缺席」与「=0」输出相同(都是 0),那时无所谓:core 恒发数字,缺席没有 producer。现在 unpriced 部署
67
- * 下 core 行级缺席、budget.ts 如实透传落库,若在这里折 0,诚实就只活到落库为止——回声上又变回
68
- * 「花了 0」,行级消歧白做。故改**未知传染**:该模型任一贡献行缺 costMicroUsd 键 ⇒ 总量的该键**整个不在**
69
- * (和里含一个不知道的项,这个和就不知道)。传染按**模型隔离**,不毒别的模型,更不毒整窗。
70
- *
71
- * 判别轴(两种"没数"不同罪):
72
- * · **键缺席** = 契约允许的「未知」(SDK ModelUsageDelta 五键全可选)⇒ 传染,输出也缺席。
73
- * · **键在场但值坏**(NaN / 字符串 / null)= 数据损坏,写了键的 producer 是在声称自己知道 ⇒ 维持既有
74
- * 折叠(按 0 计,不传染,已知量不受损),否则一条陈年坏行就能把整个模型的成本抹成"未知"。
75
- * 下游补偿件早已就位:usage-analytics 的 numOf + `estimated` 标记(缺数值键 ⇒ 总量偏低并诚实标注)。 */
76
31
  export function aggregateModelUsage(events) {
77
32
  const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : 0);
78
33
  const total = {};
79
- const costUnknown = new Set(); // RB-368:该模型至少有一行没带 costMicroUsd 键 ⇒ 总量不可知
80
- // S10([2296] 装车单,§DESIGN-V2 V2-⑤):usageBasis 只在**这个模型贡献的每一行**都带同一个值时才透传到
81
- // 回声行——混窗(升级前落盘的旧行 / 坏行 / 未来换了别的 basis 字符串)一律诚实省略,不猜、不传染出一个
82
- // 假的一致标记。Map 记「目前为止这个模型见过的一致值」:第一行定值;之后任何一行不同(含缺席,记作
83
- // undefined)⇒ 永久锁定为 undefined(一旦分歧就回不去)。
34
+ const costUnknown = new Set();
84
35
  const basisSeen = new Map();
85
36
  let any = false;
86
37
  for (const ev of events) {
@@ -91,11 +42,9 @@ export function aggregateModelUsage(events) {
91
42
  continue;
92
43
  for (const [model, d] of Object.entries(usage)) {
93
44
  any = true;
94
- // F3(2026-07-30 复审):null/非对象行的守卫必须在任何取值**之前**——原来它排在 `d.inputTokens`
95
- // 之后,对唯一能判别的形(null)先抛,守卫不可达;坏行按整行 0 折(#59 同口径),cost 记未知。
96
45
  if (d === null || typeof d !== "object") {
97
46
  costUnknown.add(model);
98
- basisSeen.set(model, undefined); // 坏行 = 不可知,视同分歧,不透传 basis
47
+ basisSeen.set(model, undefined);
99
48
  continue;
100
49
  }
101
50
  const cur = total[model] ?? { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costMicroUsd: 0 };
@@ -103,8 +52,6 @@ export function aggregateModelUsage(events) {
103
52
  cur.outputTokens += num(d.outputTokens);
104
53
  cur.cacheReadTokens += num(d.cacheReadTokens);
105
54
  cur.cacheWriteTokens += num(d.cacheWriteTokens);
106
- // `d` 是 DB 里的 JSON:用 hasOwn 判「键在不在」(值为 undefined 的显式键与缺键同罪——落库后
107
- // 两者本就无法区分,JSON.stringify 会把显式 undefined 抹掉)。
108
55
  if (d !== null && typeof d === "object" && Object.hasOwn(d, "costMicroUsd") && d.costMicroUsd !== undefined)
109
56
  cur.costMicroUsd = num(cur.costMicroUsd) + num(d.costMicroUsd);
110
57
  else
@@ -124,31 +71,15 @@ export function aggregateModelUsage(events) {
124
71
  total[model].usageBasis = basis;
125
72
  return any ? total : undefined;
126
73
  }
127
- /** Identity fields core stamps on each content event (1.121.0 `TaskEventIdentity`, shell-host contract E2):
128
- * `eventId` (core-mint uuidv7, the stable handle for dedup / the E18 resume-at anchor) + `parentToolCallId`
129
- * (present only when the run is a sub-agent task spawned under a parent tool call). Both additive / tolerate-absent. */
130
74
  function identityFields(src) {
131
75
  return {
132
76
  ...(src.eventId ? { eventId: String(src.eventId) } : {}),
133
77
  ...(src.parentToolCallId ? { parentToolCallId: String(src.parentToolCallId) } : {}),
134
78
  };
135
79
  }
136
- /** The durable `tool_start` event payload, shared by BOTH append sites (runInBackground + the resume leg) so the
137
- * persisted shape never drifts: redacted args (UNTRUSTED) + E2 identity. `label`([1543]§三族B:durable 腿
138
- * 此前剥掉——同一 run live 观看有展示名、replay/resume 退化成 wire 名):core 展示名 verbatim(tail 面
139
- * 1.251 复审同裁,两腿同处理)。 */
140
80
  export function toolStartEventData(ev) {
141
81
  return { toolCallId: ev.toolCallId, toolName: ev.toolName, ...(ev.label !== undefined ? { label: ev.label } : {}), args: redactDeep(ev.args), ...identityFields(ev) };
142
82
  }
143
- /** The durable `tool_end` payload (shared by both append sites). E1: the model-facing `output` is UNTRUSTED RAW
144
- * from core (file/shell/API bodies) — core only SIZE-bounds it (`truncated`), REDACTION is ours (same contract as
145
- * `tool_start.args`); never re-fed to a model. E2: identity. Absent `output` ⇒ the tool produced no content body.
146
- * core 1.203: `structured` = the tool's CC-shaped `details` card (edit/create/bash/… — core
147
- * allowlists the card `type` and size-caps the whole card, omit-whole on overflow, so a subagent's child-TaskResult
148
- * can never appear here) — SAME UNTRUSTED RAW contract as `output` (a card carries `originalFile`/stdout bodies),
149
- * so it gets the same deep redaction before it is persisted or echoed. `label`(core 展示名,同 tool_start 的
150
- * label 待遇 verbatim)白名单排查(2026-07-24)补齐进函数本体——此前外包给调用点各自手动拼接,7 处只有
151
- * 3 处记得拼,resume leg + ledger-sink 落盘两处漏了,持久化记录本身缺 label 一写盘就永久追不回来。 */
152
83
  export function toolEndEventData(ev) {
153
84
  return {
154
85
  toolCallId: ev.toolCallId,
@@ -157,151 +88,50 @@ export function toolEndEventData(ev) {
157
88
  isError: ev.isError,
158
89
  ...(ev.output !== undefined ? { output: redactDeep(ev.output) } : {}),
159
90
  ...(ev.truncated ? { truncated: true } : {}),
160
- // core 1.442([1898] RB-210):截断诚实体量(各 block 真实大小之和,不含 JSON 包装开销)。
161
91
  ...(Number.isFinite(ev.totalChars) ? { totalChars: ev.totalChars } : {}),
162
92
  ...(ev.structured !== undefined ? { structured: redactDeep(ev.structured) } : {}),
163
- // core 5.9.0 W3([2535];2026-08-04 复审纠偏):errorCode = core 在 isError 时对**任意工具 ToolResult
164
- // 的 `details.code`** 的透传 —— 开集,无引擎白名单(runtask 直读 result.details.code,structuredFrom
165
- // 的 CC_DETAIL_TYPES 白名单只管 structured 键)。引擎码 `gate.parked`/`tool.not_found` 只是两例,
166
- // 工具层现役即有 `path_not_in_root`/`readonly_out_of_root` 等,自注册工具可加新码 —— 消费端必须
167
- // 带 default 臂,禁按两值穷举。verbatim 依据:码是机器短串标识符、非用户内容(不脱敏),边界靠
168
- // 显式上限而非词表 —— >128 字符=非法形,整键丢弃(不截断:截断会铸出 core 从未发过的码)。
169
93
  ...(typeof ev.errorCode === "string" && ev.errorCode.length <= 128 ? { errorCode: ev.errorCode } : {}),
170
- // core 5.18.1 #187([3110] 提货批):`settledBy` = 这一次调用的**结算来源**("human"/"timeout"/
171
- // "aborted"),只挂在经审批结算的那个 tool_end 上。与上面的 errorCode **不同族**,两条差别都承重:
172
- // ① 它是**闭集**,判据走 core 导出的 `isApprovalSettledBy`(单一属主)—— 不在本仓抄词表,也不拿
173
- // 长度上限当边界(那是开集的兜底姿势,对闭集等于没门:出格词照样上 wire,下游按闭集写的
174
- // switch 会撞 default 或更坏地把它当成一次真结算)。上游加员时判据自动跟随。
175
- // ② **缺席不是语义**:core 契约逐字写「Absence = an older caller — consumers must not infer
176
- // semantics from a missing key」;姿态臂(headless auto-deny / 整体放行)本就不填。所以缺席
177
- // 一律不铸键 —— 既不折 "human" 也不折 null,把「不知道」如实表达成「没有这个键」。
178
- // 值是引擎铸的枚举短串、非用户内容 ⇒ 不脱敏。
179
94
  ...(isApprovalSettledBy(ev.settledBy) ? { settledBy: ev.settledBy } : {}),
180
- // core 5.51.0 #127([4816] 提货单件①):`resolution` = ask 解析席**拒绝臂**的分类(九码闭集
181
- // `AskDenyResolution`)——比 settledBy 更细(那个只说结算是什么种类,这个说拒绝走了哪条臂),
182
- // 铸点在 resolveAsk 的组臂处、经引擎结算 sideband 逐字携带,**不可由 tool/policy 自declare**——
183
- // core 契约逐字:「an out-of-vocabulary or self-declared word is dropped at the screens」,即
184
- // **筛点单点在 core**。判据=闭集谓词 `isAskDenyResolution`(core 5.52.0 三席根导出到货,
185
- // [4819]/[4820] 过渡形〔string 非空+≤64〕退役,单一属主纪律不抄词表)。
186
- // 缺席不是语义(执行了的调用/policy 直拒/durable 腿都不带)。
187
95
  ...(isAskDenyResolution(ev.resolution) ? { resolution: ev.resolution } : {}),
188
- // core 5.35.0 design/252 G-7(#263 提货批,[4022] G3):`approver` = 这次结算**是谁的**——审批通道
189
- // 自报的结算方标识串,core `screenApproverAttribution` 筛形后逐字转录在 `settledBy` 旁,两键**合读**
190
- // (`settledBy:"timeout"` + approver = 超时的那条队列名,不是拒绝的人)。与 settledBy 同族三条纪律:
191
- // ① 只挂在经审批结算的 tool_end 上;缺席 = 通道没报名字,**永不**意味「没人批」或「有人批」;
192
- // ② core 只筛形不鉴别(转录不是认证)—— server 同姿势,不折不铸,typeof 判形即过境;
193
- // ③ 值是**宿主派生的身份串**(login/email/账号 id),照 humanInputEventData 的 issuer/actor.id
194
- // 先例过 redactSecrets(对正常标识恒等,对密钥形打码;core 已拒控制符/超长,无需重复筛)。
195
96
  ...(typeof ev.approver === "string" && ev.approver !== "" ? { approver: redactSecrets(ev.approver) } : {}),
196
- // core 5.55.0 #333/[4918](#337 提货):`gatedCallId` = durable park 正扣着**哪个** tool call 的 id——
197
- // core runtask 铸点从 committed checkpoint 的 pendingAction 直读(**禁由工具自报**:core 契约原话,
198
- // 能自报的 id 是对「另一个 call」的断言,工具能写它就能把审批 UI 指向一个没人等的 call),只挂在
199
- // park 污染帧上。id 串非用户内容不脱敏;缺席=park 不扣 call(resource_limit/plan_review 形挂的
200
- // pendingAction 没有 tool call),core 契约「ABSENT, never guessed」——缺席不铸键。
201
97
  ...(typeof ev.gatedCallId === "string" && ev.gatedCallId !== "" ? { gatedCallId: ev.gatedCallId } : {}),
202
98
  ...identityFields(ev),
203
99
  };
204
100
  }
205
- /** The `task_progress` payload (a subagent's live progress tick) — EXPLICIT WHITELIST shared by all
206
- * three legs (bg runs.ts + resume append + the sync live SSE frame): taskId/parent linkage/token rollup verbatim,
207
- * `name` UNTRUSTED (core sanitizes for rendering, does NOT secret-redact — a taskName can be secret-shaped) →
208
- * `redactSecrets`. Never the delegated objective/args. E2 identity rides (eventId/
209
- * parentToolCallId are core-mint uuidv7 identifiers, not content — consumers bind concurrent subagent ticks
210
- * exactly by them; the 1.72 whitelist dropped them, leaving a FIFO heuristic). */
211
101
  export function taskProgressEventData(ev) {
212
102
  return {
213
103
  taskId: ev.taskId,
214
- // core 5.36.0(#258,[4066] 提货①):这一拍所报的 registry 行**代际号**(fresh spawn = 1,每次
215
- // 复活翻转 +1)—— 与 `TaskNotificationPayload.seq` / `BackgroundChildEvent.seq` **同一条轴**,
216
- // core 原话「不是第三种拼法」。它回答的是消费端在帧迟到时otherwise判不了的那一个问题:
217
- // 「这是我已知那一代的迟到首帧(同值),还是一次我还没折叠的复活(更大)?」
218
- // 🔴 **缺席是事实不是缺口**:没有 `a*` registry 行的 run(同步委派子代 / workflow `wa*` / 顶层 run)
219
- // 根本没有代际概念,缺席**禁**读作 cycle 1。core-mint 数值,无用户内容 ⇒ verbatim,缺席不铸键。
220
104
  ...(typeof ev.seq === "number" ? { seq: ev.seq } : {}),
221
- // core 5.14.0([2854] 六车批②):委派种类判别键(core `DelegationTaskType` =
222
- // "background_agent" | "workflow")—— 消费端据此路由一条 progress 帧,不必再从 id 形状猜。
223
- // 形参取 `string` 而非闭枚举:该类型**没有**从 core 包根导出(亲验 index.d.ts 零命中,与发车帖
224
- // 「新导出」的说法不符),抄一份闭集就是值拷贝漂移源;`stoppedBy` 先例同款——core 的开集串
225
- // verbatim,消费端按契约容忍未知值。缺席=同步子代(无 fleet 行),缺席即不铸键。
226
105
  ...(typeof ev.taskType === "string" && ev.taskType.length > 0 ? { taskType: ev.taskType } : {}),
227
106
  ...(ev.parentTaskId ? { parentTaskId: ev.parentTaskId } : {}),
228
107
  ...(ev.name ? { name: redactSecrets(ev.name) } : {}),
229
- // core [1414]#3 settle 终态 tick("completed"/"failed"——[1543]§三族B:此前被剥,消费方只见最后
230
- // 一拍 running 后行消失,恰是该 tick 要修的体验)。core enum verbatim。
231
108
  ...(ev.status !== undefined ? { status: ev.status } : {}),
232
109
  ...(ev.usage ? { usage: ev.usage } : {}),
233
- // core 1.262.0: the child's most recent tool intent as one human line ("Bash npm
234
- // test") — derived from the child's tool_start (tool name + argument head), so the ARGUMENT part is
235
- // UNTRUSTED free text (a command line can be secret-shaped) → `redactSecrets`, same boundary as `name`.
236
110
  ...(ev.currentAction ? { currentAction: redactSecrets(ev.currentAction) } : {}),
237
- // core 1.401.0([1611]→[1616]/[1617] 方案 B):后台 workflow 子代的 tick additive 带这两个字段,
238
- // 让下游(cli)能把这条前台裸 uuid 行识别为"fleet 那边已有一份"从而跳过重复渲染。workflowRunId 是
239
- // core-mint 的不透明 run id(不含用户内容)→ 同 identityFields 待遇直传;workflowAgentLabel 是脚本
240
- // 作者可控的展示字符串(UNTRUSTED,同 name 的信任边界)→ redactSecrets。
241
111
  ...(ev.workflowRunId ? { workflowRunId: ev.workflowRunId } : {}),
242
112
  ...(ev.workflowAgentLabel ? { workflowAgentLabel: redactSecrets(ev.workflowAgentLabel) } : {}),
243
113
  ...identityFields(ev),
244
114
  };
245
115
  }
246
- /** The `task_notification` payload (core 1.202 design/115 P2: a background task finished — this event is the
247
- * LEDGER/display projection of the OBSERVED terminal state, emitted the moment core observes it. It does NOT
248
- * attest that the `<task-notification>` XML has entered the model context: core's steer targets the NEXT TURN
249
- * BOUNDARY (design/116 §7 — turn 在飞时不注入), so the frame can precede actual injection by minutes ([1818]
250
- * clay live: 3m54s; the previous wording here — "core already injected in-process" — was the same wrong
251
- * assumption that broke the shell's dedup). `injected: false` says exactly that on the wire: a consumer must
252
- * NOT treat this frame as an echo of model-context injection. EXPLICIT WHITELIST (never `{...ev}` — the 1.72 task_progress lesson: a
253
- * future core field must not ride raw): ids/status/usage verbatim; `summary`/`result`/`output_file` are UNTRUSTED
254
- * free text (core bounds+sanitizes for XML, does NOT secret-redact) → `redactSecrets` at OUR wire boundary. Shared
255
- * by all three legs (bg runs.ts + resume append + the sync live SSE frame) so the shape never drifts. Takes the
256
- * EVENT (not just the payload) so the E2 identity fields core stamps on it (eventId/parentToolCallId) ride the
257
- * frame like every other content event (review round 2: the payload-only version silently dropped them). */
258
116
  export function taskNotificationEventData(ev) {
259
117
  const n = ev.notification;
260
118
  return {
261
- // [1818]§四 (b)案:显式判别位 —— 本帧=台账观测,不证已入模型上下文(见 docstring)。恒 false 是
262
- // 如实声明:server 观测不到 core 的 turn 边界注入时刻;若未来 core 给出注入回声 seam,echo 帧以
263
- // injected:true 骑同帧族。
264
119
  injected: false,
265
120
  task_id: n.task_id,
266
121
  task_type: n.task_type,
267
- // core 1.238: the child's sessionId — the stable a*↔uuid cross-system anchor (the killed
268
- // receipt renders off summary+sessionId). Optional (bash lane has none).
269
122
  ...(n.sessionId !== undefined ? { sessionId: n.sessionId } : {}),
270
123
  ...(n.toolUseId !== undefined ? { toolUseId: n.toolUseId } : {}),
271
124
  status: n.status,
272
- // The design/135 Monitor + design/134 stoppedBy fields were MISSING
273
- // from this whitelist — the shell had no dedup discriminator (`seq` is what keeps successive Monitor event
274
- // batches from collapsing into one, per core's taskNotificationDedupKey) and no batched line text / stop
275
- // attribution. `seq` is a core counter (verbatim); `lines` is watched-process stdout = UNTRUSTED free text
276
- // (redact each line, same boundary as result); `stoppedBy` is core's OPEN enum (verbatim string — consumers
277
- // tolerate unknown values by contract).
278
125
  ...(n.seq !== undefined ? { seq: n.seq } : {}),
279
126
  ...(n.lines !== undefined ? { lines: n.lines.map((l) => redactSecrets(l)) } : {}),
280
127
  ...(n.stoppedBy !== undefined ? { stoppedBy: n.stoppedBy } : {}),
281
- // P1-3(core 1.452):完成幂等锚 verbatim(消费端跨通路去重;[1925] 一轮批)。
282
128
  ...(n.completionId !== undefined ? { completionId: n.completionId } : {}),
283
- // RB-386(core 2.4.0 wire note,2.5.0 提货收编):bg agent 失败面归因两键——error 是引擎侧
284
- // 失败说明(可能含网络地址等)→ redactSecrets;errorCode 是机器码 verbatim。缺席=非失败帧。
285
129
  ...(n.error !== undefined ? { error: redactSecrets(n.error) } : {}),
286
130
  ...(n.errorCode !== undefined ? { errorCode: n.errorCode } : {}),
287
- // [1543]§二 server①:白名单滞后 core payload 四键补齐——
288
- // source:external 通知来源标(caller 供 = UNTRUSTED)→ redactSecrets;
289
- // exitCode:数值 verbatim(bash 腿);
290
- // partial:boolean verbatim——**killed 残果诚实标**(缺它 cli 把残果渲染得像完整结果,core
291
- // 特意打的标到不了用户面,本键是四键里的主案);
292
- // diagnostics:[1409] 教学文本 = UNTRUSTED 自由文本 → redactSecrets。
293
131
  ...(n.source !== undefined ? { source: redactSecrets(n.source) } : {}),
294
132
  ...(n.exitCode !== undefined ? { exitCode: n.exitCode } : {}),
295
133
  ...(n.partial !== undefined ? { partial: n.partial } : {}),
296
134
  ...(n.diagnostics !== undefined ? { diagnostics: redactSecrets(n.diagnostics) } : {}),
297
- // core 1.262.0 五个补面——additive passthrough, same posture as
298
- // lines/stoppedBy above. Fields PICKED (never spread — a future core field must not ride raw):
299
- // recentSteps: `tool` = core tool name (verbatim); `target` (file path / command / pattern) and
300
- // `outcome` (tool result first line) are UNTRUSTED free text (a command or output line can be
301
- // secret-shaped) → `redactSecrets`, same boundary as `result`/`lines`. Bounded upstream (≤10).
302
- // editedFiles: `path` is engine working-file derived but still crosses our wire → `redactSecrets`
303
- // (the compacted attachedFiles posture); `edits` numeric verbatim.
304
- // resumable: core boolean verbatim (E — parent decides continue-vs-restart without trial-and-error).
305
135
  ...(n.recentSteps !== undefined
306
136
  ? { recentSteps: n.recentSteps.map((s) => ({ tool: s.tool, target: redactSecrets(s.target), outcome: redactSecrets(s.outcome) })) }
307
137
  : {}),
@@ -314,18 +144,6 @@ export function taskNotificationEventData(ev) {
314
144
  ...identityFields(ev),
315
145
  };
316
146
  }
317
- /** The `compacted` payload — EXPLICIT WHITELIST shared by all three legs (bg runs.ts + resume append + the sync
318
- * live SSE frame; the 1.72 task_progress lesson: never `{...ev}`). trigger/tokensBefore (MF-18) verbatim;
319
- * `preserved_segment.firstKeptEntryId` verbatim (a core-mint entryId, the shell's compaction-divider anchor);
320
- * core 1.219 `attachedFiles` (attachWorkingFiles default-ON — the shell renders CC's post-compact `Read {path}`
321
- * cards from it): `path` is the engine's own working-file path (env-derived, not free text) but still crosses our
322
- * wire → `redactSecrets` (a project path can embed a secret-shaped dir name); chars/truncated are numeric/bool
323
- * verbatim. Absent fields stay absent (feature off / nothing attached / no kept-tail floor). */
324
- /** The `diagnostics` payload (core 1.220 design/121 — NEW LSP diagnostics drained at a turn boundary; the model
325
- * already got the `<new-diagnostics>` context message in-process, this frame is the shell's render source).
326
- * EXPLICIT WHITELIST shared by all three legs. UNTRUSTED RAW (§E1: language-server output over workspace files —
327
- * a diagnostic message can quote file content, a secret-shaped uri/source is possible) → `redactSecrets` on the
328
- * string fields; severity/range/code numbers verbatim. E2 identity rides. */
329
147
  export function diagnosticsEventData(ev) {
330
148
  return {
331
149
  files: (Array.isArray(ev.files) ? ev.files : []).map((f) => ({
@@ -342,11 +160,6 @@ export function diagnosticsEventData(ev) {
342
160
  ...identityFields(ev),
343
161
  };
344
162
  }
345
- /** The `steering_injected` payload([1550]/[1552] 裁定透传;core `TaskEvent` 联合臂 `type: "steering_injected"`,
346
- * shell 渲染源 = CC 的系统提醒条:core 在 turn 之间给模型注入一条系统提示[todo 提醒/文件变化/日期跨天/
347
- * 技能列表变化…]时随手抛的展示投影,一附件一帧)。EXPLICIT WHITELIST(source=封闭枚举 verbatim;
348
- * preview=注入文本的展示摘录,UNTRUSTED[core 未做秘密脱敏]→ `redactSecrets`)。共享三腿(sync 实时 SSE +
349
- * bg/resume 落账,ledger-sink 同门)。 */
350
163
  export function steeringInjectedEventData(ev) {
351
164
  return {
352
165
  source: String(ev.source),
@@ -354,14 +167,7 @@ export function steeringInjectedEventData(ev) {
354
167
  ...identityFields(ev),
355
168
  };
356
169
  }
357
- /** `compaction_outcome`(core 2.5.0 [2105]②:压缩非 compacted 结局——manual /compact 的 mooted/failed
358
- * 两条真路)。[2373]A-2/C-2:此前它是**唯一**不走共享 builder、只在 live 腿 res.write 内联挑键的
359
- * 内容帧 ⇒ 两条 durable 腿零 case、回放即消失、三方普查各自失明。收进共享白名单 builder,三腿同源。
360
- * outcome/trigger=闭枚举 verbatim;reason=引擎说明文本(可能引用配置值)→ redactSecrets;identity 同款。 */
361
170
  export function compactionOutcomeEventData(ev) {
362
- // 形参照 contextUsageEventData 先例:全可选 unknown + 内部窄化,call site 免宽铸型(棘轮零新增)。
363
- // 🔴 缺键守卫(同款「不合形即缺席」):无条件 String() 会把缺席/非串的键铸成字面 "undefined"/"null"
364
- // 落进 durable 行——回放端读到的是一个看起来合法的枚举值,比缺席更难发现。
365
171
  const s = (v) => (typeof v === "string" ? v : undefined);
366
172
  return {
367
173
  ...(s(ev.outcome) !== undefined ? { outcome: s(ev.outcome) } : {}),
@@ -370,78 +176,16 @@ export function compactionOutcomeEventData(ev) {
370
176
  ...identityFields(ev),
371
177
  };
372
178
  }
373
- /** The `workspace_changed` payload([1559]二 core 裁定:design/99 §E13 旧臂,L1 门本车首次撞见——非新增,
374
- * 是本门扩 TaskEvent 臂集时第一次显式处置。语义 = bash `cd`/EnterWorktree 移动任务逻辑 cwd 后,在移动
375
- * 它的工具结算后发一帧,给 shell/宿主 UI 一个路径提示行。cwd 与已展示的工具 args 同信任级[UNTRUSTED,
376
- * RAW 逻辑路径,symlink 不解析]→ `redactSecrets` + 长度 bound[300,与 brainStatusEventData.detail 同
377
- * 惯例]。core 只在真变化时才发(不播初始 cwd);渲染面(cli)不得当持久态——跨 suspend/resume 重置为
378
- * 根,这是消费端纪律,服务端只如实转发单帧不做状态缓存。 */
379
179
  export function workspaceChangedEventData(ev) {
380
180
  return {
381
181
  cwd: redactSecrets(String(ev.cwd)).slice(0, 300),
382
182
  ...identityFields(ev),
383
183
  };
384
184
  }
385
- /**
386
- * `wiring_manifest`(core 5.14.0 design/173 新 `TaskEvent` 臂)—— 每条腿(root/child/resume)在 prepare
387
- * 时自证「我到底接了什么线」的清单帧。
388
- *
389
- * 🔴 **本构造器存在的唯一理由是剥掉 `governance` 段**。core 给该段盖了 `audience: "operator"` 并成文
390
- * 了服务层义务:*a serving layer forwarding this event to a multi-tenant stream MUST project the governance
391
- * section for operators only; **no projection ⇒ do not disclose***。我方的三条腿(sync SSE / durable 账本 /
392
- * resume 账本)都是租户可读面,而 operator 投影是 #154 的独立大件——按 core 写死的失败方向,**没有投影
393
- * 就不得披露**,所以本批先把整段剥掉止泄(此前 sync 腿的裸 catch-all 会把它连同 `lockedConfig` /
394
- * `compliance` / `memoryAdmission` / `retention` 四个治理面在场位原样透传给任意订阅者)。
395
- * operator 投影是**另一个**构造器({@link wiringManifestOperatorEventData},live 腿按调用方身份挑),
396
- * 不是往这里加键 —— 两条 durable 腿没有调用方身份可挑,所以它们恒用本构造器。
397
- * ⚠️ 剥段还必须连**对该段取的哈希**一起剥:`configFingerprint` 同样不投影,理由见下方它那一行的旁注
398
- * (无盐哈希 + 4 布尔 = 16 组合的谕示器,把「结构上藏起来」的段原样还原出来)。
399
- *
400
- * 其余段逐键挑(禁 `{...manifest}`:1.72 task_progress 教训——未来 core 加段必须由人显式处置,而不是
401
- * 顺着 spread 裸上线,尤其当新段可能又是一个 audience 受限面时)。所有值都是引擎派生的闭枚举/布尔/
402
- * 有限数/不透明摘要串,**非用户内容 ⇒ 不脱敏**;畸形值一律按缺席处理(不铸假值,与 compacted 同规)。
403
- *
404
- * 🔴 **未标签面的准入裁定逐面记录在此,缺省不是「放行」**(#154 裁定 C)。core 只给 `governance` 标了
405
- * audience,其余段没有标签 —— 没标签**不等于**可以默认投给租户,每一段都要有一次人的裁定,写在这里:
406
- * · `interaction.posture`(三值:interactive / headless / absent)—— **裁:租户可见**。它是部署形的
407
- * 自述(这条腿有没有人能答问题),消费端据此决定渲不渲交互面;不是治理秘密,也不构成任何治理位的
408
- * 侧信道(它不参与 governance 那 4 个布尔的还原)。
409
- * · `permissionRules.storeWired`(布尔;core 5.18.0 design/179 新段)—— **裁:租户可见**。同族判据:
410
- * 它与 `parkLane.capable` / `elicit.seamWired` / `fleet.backgroundAgentStore` 是同一类「这条腿接没接
411
- * 某个可选设施」的部署形自述,而那三个早已租户可见。消费端的真需求也在租户侧:审批卡要不要渲染
412
- * 「不再询问」这一格,取决于这台 worker 有没有规则店 —— 剥掉它,客户端只能靠试(提交后被拒)才知道。
413
- * 它**不在** `governance` 段里(core 自己把它放在段外),也不构成那 4 个治理布尔的侧信道。
414
- * ⚠️ 恒在段(core 侧 `permissionRules` 无 `?`):两个真值都可能出现,**别把 false 读成缺席**。
415
- * 本仓自 #154 车二 / design/203 起真接了规则店(`main.ts` 在 `PERMISSION_RULES_ENABLED` 为真 ∧
416
- * backend 在场时把 `permissionRuleStore` 装进 `RunnerDeps`,SQL 与 File 两形)⇒ 那样的部署报
417
- * `storeWired: true`;旋钮关掉或后端缺席的部署仍报 `false`。消费端读到 false 就是「这台 worker
418
- * 今天没有这条车道」,不许当成「这版引擎还没这个概念」。
419
- * · `permissionRules.syncWired` / `permissionRules.orgGoverned`(布尔两位;core 5.23.0 design/182 §9/§7
420
- * 给**同一段**加的员,[3372] 提货批 codex 复审 high 抓获——首版把它们静默剥了,正是本仓建这份台账要防
421
- * 的「投影白名单剥键」病)—— **裁:租户可见,与同段的 `storeWired` 同待遇**。判据三条:
422
- * ① **同段同类**:core 把它们放进 `permissionRules` 而不是 `governance`(唯一带 audience 标签的段),
423
- * 且形状与 `storeWired` 逐字同构(恒在的布尔,present-and-false 表示「有这个概念,这里没开」);
424
- * ② **消费端真需求在租户侧**:这两位说的是「按下『不再询问』之后,这条同意到底落到多大的信任域里」——
425
- * `syncWired` = 这台 worker 会把规则并进跨设备/传输/服务端的**同一个同意信任域**;`orgGoverned` =
426
- * 组织治理层武装着(且它的 fail-closed 契约意味着读不到 org 快照时**每一次**放行都会被抬成真人审批)。
427
- * 同一张审批卡上的同一个动作,后果因这两位而不同,剥掉它们等于让客户端渲一格语义不明的开关;
428
- * ③ **不是治理侧信道**:两位都不参与 `governance` 那 4 个布尔的还原(它们描述规则店这条车道自身的
429
- * 形状,与 lockedConfig/compliance/memoryAdmission/retention 无函数关系)。
430
- * ⚠️ 与 `storeWired` **不同**:本仓这两条至今未接(`RunnerDeps.permissionRuleSyncWired` /
431
- * `permissionRuleOrg` 全树零声明点,2026-08-10 #204 复扫亲验)⇒ 今天恒 `false`,这是**真值**不是
432
- * 缺席。哪天接上其中任一条,连本句一起改 —— 这一行是它的登记处。
433
- * 未来 core 加一段而它没有 audience 标签时,**先在这里补一条裁定再决定挑不挑键** —— 段级完备性钉
434
- * (test/wiring-manifest-projection.test.ts)会在那一刻先把人拦下来。
435
- */
436
185
  export function wiringManifestEventData(ev) {
437
- // 形参照 `compactionOutcomeEventData` / `contextUsageEventData` 先例:全可选 `unknown` + 内部逐层窄化。
438
- // 两个理由,不是风格:①三条腿里有两条(ledger-sink.onEvent / resume switch)手里只有 `{type:string}`,
439
- // 窄形参会逼出 `as unknown as` 双铸型(类型卫生棘轮门当场红);②窄化在这里做,账本回放/第三方 producer
440
- // 送来的畸形段也走同一道门,而不是只保护 live 腿。
441
186
  const str = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
442
187
  const bool = (v) => (typeof v === "boolean" ? v : undefined);
443
188
  const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
444
- /** 段读:非对象即缺席(不铸空段)。 */
445
189
  const sec = (v) => (typeof v === "object" && v !== null && !Array.isArray(v) ? { ...v } : undefined);
446
190
  const nonEmpty = (o) => (Object.keys(o).length > 0 ? o : undefined);
447
191
  const m = sec(ev.manifest) ?? {};
@@ -462,7 +206,6 @@ export function wiringManifestEventData(ev) {
462
206
  const question = nonEmpty({
463
207
  ...(str(questionIn.wired) !== undefined ? { wired: questionIn.wired } : {}),
464
208
  ...(str(questionIn.provenance) !== undefined ? { provenance: questionIn.provenance } : {}),
465
- // 组装谎言标(交互工具在场却没有投递面)——只在 core 真铸时带,缺席不折 false。
466
209
  ...(questionIn.interactiveToolsWithoutDeliveryFace === true ? { interactiveToolsWithoutDeliveryFace: true } : {}),
467
210
  });
468
211
  const elicit = nonEmpty({
@@ -471,8 +214,6 @@ export function wiringManifestEventData(ev) {
471
214
  });
472
215
  const parkLane = nonEmpty({
473
216
  ...(bool(parkIn.capable) !== undefined ? { capable: parkIn.capable } : {}),
474
- // `effective` 是三态(true / false / "unresolved")——布尔与字符串两支都得过。折成布尔会把
475
- // 「还没定」谎报成「不能 park」,而那正是这个字段被造出来要区分的那一格。
476
217
  ...(bool(parkIn.effective) !== undefined || str(parkIn.effective) !== undefined ? { effective: parkIn.effective } : {}),
477
218
  ...(Array.isArray(parkIn.reasons) ? { reasons: parkIn.reasons.filter((r) => typeof r === "string") } : {}),
478
219
  ...(str(parkIn.checkpointDurability) !== undefined ? { checkpointDurability: parkIn.checkpointDurability } : {}),
@@ -481,9 +222,6 @@ export function wiringManifestEventData(ev) {
481
222
  ...(bool(fleetIn.backgroundAgentStore) !== undefined ? { backgroundAgentStore: fleetIn.backgroundAgentStore } : {}),
482
223
  ...(bool(fleetIn.hostChildEventSink) !== undefined ? { hostChildEventSink: fleetIn.hostChildEventSink } : {}),
483
224
  });
484
- // core 5.18.0 design/179 段 + 5.23.0 design/182 §9/§7 两位加员(裁定见头注:三位同判租户可见)。
485
- // 逐键挑,与其余段同规:非布尔按缺席处理(不铸假值——一个编出来的 `false` 会被读成「确实没接/没开」,
486
- // 而真相是这段读不出来)。
487
225
  const permissionRules = nonEmpty({
488
226
  ...(bool(rulesIn.storeWired) !== undefined ? { storeWired: rulesIn.storeWired } : {}),
489
227
  ...(bool(rulesIn.syncWired) !== undefined ? { syncWired: rulesIn.syncWired } : {}),
@@ -500,46 +238,11 @@ export function wiringManifestEventData(ev) {
500
238
  ...(str(sessionIn.store) !== undefined ? { session: { store: sessionIn.store } } : {}),
501
239
  ...(fleet !== undefined ? { fleet } : {}),
502
240
  ...(permissionRules !== undefined ? { permissionRules } : {}),
503
- // governance —— **刻意不在此**(见头注:受众标 operator,无投影不披露)。
504
- // configFingerprint —— **也刻意不在此**(codex 复审 2026-08-06 F1,验真后采纳)。剥掉一个段却把
505
- // **对它取的哈希**发出去,等于没剥:core 的算法是
506
- // `sha256(canonicalize(manifest 去掉 leg 与两个 provenance))` 取前 16 hex —— 无盐、输入里含整个
507
- // governance 段,而 governance 只有 4 个布尔(audience 是常量)⇒ **16 种组合**。租户手里已经有其余
508
- // 全部哈希输入(它们正是我们投影出去的那些段),把 16 种组合逐个拼进去重算一遍即可比中,
509
- // lockedConfig / compliance / memoryAdmission / retention 四位当场还原。
510
- // 「结构上藏起来、经哈希谕示器泄出去」仍然是泄出去,core 写死的失败方向(no projection ⇒ do not
511
- // disclose)对侧信道一样适用。它的广告用途(判两条腿是不是同一套装配)在本仓与 SDK **今天零消费端**,
512
- // 所以直接不发,而不是另铸一个「只对已投影字段取哈希」的服务端自造值(那是新 wire 语义,要跨仓对表)。
513
- // 带 core 真 fingerprint 的是 {@link wiringManifestOperatorEventData}(explicitOperatorOk 为真的 live
514
- // 连接才走那条),对那个受众而言四布尔本就在同一份产物里,哈希不再是侧信道。
515
241
  ...identityFields(ev),
516
242
  };
517
243
  }
518
- /**
519
- * `wiring_manifest` 的 **operator 投影**(#154 件一)—— {@link wiringManifestEventData} 的租户形加上
520
- * `governance` 段与 core 的真 `configFingerprint`。
521
- *
522
- * 🔴 分叉发生在**读口**,不在账本:两条 durable 腿(ledger-sink / resume)恒存剥后形,因为账本写的那一刻
523
- * 不知道未来的读者是谁(`GET /v1/runs/:id/events` 任何 run owner 都读得到),往账本存全量 = 把 operator-only
524
- * 面写进租户可读介质;存两份则是双写漂移源。所以只有 live 腿在**知道调用方身份**的那一刻挑构造器。
525
- * 代价是 operator 看不到历史腿的 governance 段 —— 治理四布尔是部署形事实(不随 leg 漂),静态自检面
526
- * (`GET /v1/diagnostics/wiring`)随时给出当前真值。
527
- *
528
- * 准入判据是 `explicitOperatorOk`(**不是** `isOperator`):空 `OPERATOR_PRINCIPALS` 必须是「没有人是
529
- * operator」,不能是「所有人都是」—— 后者会让一个没配 operator 名单的部署把治理面发给每一个订阅者。
530
- *
531
- * `configFingerprint` 只在本构造器里透传:它是 `sha256(canonicalize(manifest 去 leg/去两个 provenance))`
532
- * 前 16 hex,**无盐**且输入含整个 governance 段,而 governance 只有 4 个布尔 ⇒ 16 种组合。租户手里已有其余
533
- * 全部哈希输入,发给租户等于把剥掉的四位原样还原(谕示器);发给 operator 无损 —— 同一份产物里四布尔本就
534
- * 在场。
535
- *
536
- * governance **逐键挑**(禁 `{...governance}`):core 往这个段里加第五键时必须由人显式处置,而不是顺着
537
- * spread 裸上线 —— 这个段的每一键都是部署治理面,新键的默认待遇必须是「不发」。
538
- */
539
244
  export function wiringManifestOperatorEventData(ev) {
540
- // 租户形整体复用(不是复制一份键表):operator 面 ⊇ 租户面 是结构性的,两个构造器不可能在共有段上漂。
541
245
  const tenant = wiringManifestEventData(ev);
542
- // 窄读器与租户构造器内的孪生同形而独立 —— 那只构造器被 16 组合谕示器钉钉着,本件不改它一个字节。
543
246
  const str = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
544
247
  const bool = (v) => (typeof v === "boolean" ? v : undefined);
545
248
  const sec = (v) => (typeof v === "object" && v !== null && !Array.isArray(v) ? { ...v } : undefined);
@@ -558,24 +261,6 @@ export function wiringManifestOperatorEventData(ev) {
558
261
  ...(str(m.configFingerprint) !== undefined ? { configFingerprint: m.configFingerprint } : {}),
559
262
  };
560
263
  }
561
- /**
562
- * `human_input`(core 5.14.0 design/171 新 `TaskEvent` 臂)—— 「谁把什么喂进了这条 run」的生命周期账本
563
- * 帧(objective / 实时 steer / nextTurn / parked-steer 续跑 / wake 五个载体共用一个投影点)。
564
- *
565
- * 信任划线(与 tool_approval 帧同族):
566
- * · `inputId` / `sessionSeq` / `carrier` / `source` / `delivery` / `entryId` —— 引擎铸的标识与枚举值,
567
- * **非用户内容** ⇒ verbatim。注意本帧**不带正文**(core 的臂里就没有 text 字段),所以没有内容脱敏面。
568
- * · `issuer` 与 `actor.id` / `actor.issuer` —— 宿主从入口凭据派生的**身份串**,自由文本形(`slack:U123`
569
- * 这类命名空间串里可以嵌任何东西)⇒ 过 `redactSecrets`,与 `sourceAgentName` 同待遇。
570
- * · `actor.hostAsserted` —— 布尔,verbatim。**它是消费端唯一能判「这个署名可信吗」的位**(core 的
571
- * `[from …]` 渲染就是靠它决定加不加 `(unverified)`),剥掉它比剥掉 id 更坏:剩下一个看不出真假的名字。
572
- * · `principal` —— **刻意不投影**(与 ToolApprovalFrame 的 `principal` 同裁):帧本就经 owner-scoped 流
573
- * 投递,租户身份不必再由帧自述。要它请先提消费需求,别顺手加。
574
- *
575
- * `actor` 逐键挑(禁 spread):core 已声明 `ActorAssertion` 是冻结三键形,但白名单的意义正是「core 加第
576
- * 四键时由人处置」——身份对象上多出来的键裸上线是本文件反复登记的病族。
577
- * 形参全可选 `unknown` 的理由同 {@link wiringManifestEventData}(两条 durable 腿手里只有 `{type:string}`)。
578
- */
579
264
  export function humanInputEventData(ev) {
580
265
  const str = (v) => (typeof v === "string" && v.length > 0 ? v : undefined);
581
266
  const a = typeof ev.actor === "object" && ev.actor !== null && !Array.isArray(ev.actor) ? { ...ev.actor } : {};
@@ -585,8 +270,6 @@ export function humanInputEventData(ev) {
585
270
  ...(str(a.issuer) !== undefined ? { issuer: redactSecrets(String(a.issuer)) } : {}),
586
271
  };
587
272
  return {
588
- // 缺键守卫同 compactionOutcomeEventData:无条件 String() 会把缺席铸成字面 "undefined" 落进 durable 行,
589
- // 回放端读到一个看起来合法的 id/枚举值,比缺席更难发现。
590
273
  ...(str(ev.inputId) !== undefined ? { inputId: ev.inputId } : {}),
591
274
  ...(typeof ev.sessionSeq === "number" && Number.isFinite(ev.sessionSeq) ? { sessionSeq: ev.sessionSeq } : {}),
592
275
  ...(str(ev.carrier) !== undefined ? { carrier: ev.carrier } : {}),
@@ -598,10 +281,6 @@ export function humanInputEventData(ev) {
598
281
  ...identityFields(ev),
599
282
  };
600
283
  }
601
- /** S4 (SILENT-FALLBACK P0-a): the durable `brain_status` observation row, shared by the bg + resume append
602
- * sites. core marks the live `status` frame EPHEMERAL (never replayed by core on resume) — this row is the
603
- * SERVICE's own observation record (why a turn stalled: rate_limited/retrying/reconnecting/circuit_open),
604
- * not a core event replay. Whitelist: the closed phase union + neutral bounded detail (redacted) + retryInSec. */
605
284
  export function brainStatusEventData(ev) {
606
285
  const num = (v) => typeof v === "number" && Number.isFinite(v);
607
286
  return {
@@ -616,12 +295,7 @@ export function brainStatusEventData(ev) {
616
295
  };
617
296
  }
618
297
  export function compactedEventData(ev) {
619
- // Bounded-numeric guard for the 1.289 additive scalars: finite numbers verbatim, anything else absent
620
- // (whitelist discipline — a malformed field must not ride the wire, same class as the 1.72 lesson).
621
298
  const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
622
- // core 1.289 `phaseDurations` (CompactionPhaseDurations: prepareMs/summaryMs?/persistMs/ptlRetries) — a
623
- // CLOSED numeric struct rebuilt field-by-field (never `{...ev.phaseDurations}`: an unexpected extra key must
624
- // not leak onto the wire); non-numeric members are dropped, an all-empty struct stays absent.
625
299
  const phaseDurations = (() => {
626
300
  const p = ev.phaseDurations;
627
301
  if (!p || typeof p !== "object")
@@ -637,14 +311,6 @@ export function compactedEventData(ev) {
637
311
  return {
638
312
  ...(ev.eventId !== undefined ? { eventId: ev.eventId } : {}),
639
313
  ...(ev.parentToolCallId !== undefined ? { parentToolCallId: ev.parentToolCallId } : {}),
640
- // 🔴 2026-07-25:这三处此前**违反本函数注释自己写的那条不变量**(「畸形字段不得上线,与 1.72 同类」)——
641
- // ① `trigger`/`tokensBefore` 是**裸赋值**:`tokensBefore` 为 `NaN` 时 `JSON.stringify` 把它变成 **`null`**,
642
- // 于是压缩面板渲染成「压缩前 0 tokens」而不是「未知」;裸赋值还会留下 present-with-undefined 的键
643
- // (JSON 会丢,但非 JSON 后端的进程内读者看得到)⇒ 统一走 `num()`/类型判据 + 条件 spread。
644
- // ② `attachedFiles` 的元素**逐字段照抄**:一个畸形元素(如 `{}`)会被**造成**
645
- // `{path:"undefined", chars:undefined, truncated:undefined}` —— 那是**编造值**,比丢弃更坏
646
- // (下游看到一个叫 "undefined" 的文件路径)。改成**逐元素校验后丢弃畸形项**,全丢光则整键省略
647
- // (与既有的 `length > 0` 同口径)。
648
314
  ...(typeof ev.trigger === "string" ? { trigger: ev.trigger } : {}),
649
315
  ...(num(ev.tokensBefore) !== undefined ? { tokensBefore: num(ev.tokensBefore) } : {}),
650
316
  ...(ev.preserved_segment?.firstKeptEntryId ? { preserved_segment: { firstKeptEntryId: ev.preserved_segment.firstKeptEntryId } } : {}),
@@ -657,75 +323,33 @@ export function compactedEventData(ev) {
657
323
  .map((f) => ({ path: redactSecrets(f.path), chars: f.chars, truncated: f.truncated }));
658
324
  return files.length > 0 ? { attachedFiles: files } : {};
659
325
  })(),
660
- // core 1.286 additive fields ([792]②b, design/145 compression batch): modelFallback/fallbackReason = the
661
- // compaction summarizer fell back to a larger-window model; clampedRatio/clampReason = the keep ratio was
662
- // clamped and why. Closed literal bool/enum/number — verbatim; absent stays absent.
663
326
  ...(ev.modelFallback === true ? { modelFallback: true } : {}),
664
327
  ...(ev.fallbackReason !== undefined ? { fallbackReason: ev.fallbackReason } : {}),
665
- ...(num(ev.clampedRatio) !== undefined ? { clampedRatio: num(ev.clampedRatio) } : {}), // 同上:NaN 不得以 null 上线
328
+ ...(num(ev.clampedRatio) !== undefined ? { clampedRatio: num(ev.clampedRatio) } : {}),
666
329
  ...(ev.clampReason !== undefined ? { clampReason: ev.clampReason } : {}),
667
- // core 1.289 additive fields (codex F7 — parity batch, same posture as [792]②b above): tokensAfter/
668
- // triggerTokensBefore/durationMs are engine-derived scalars, verbatim when finite numbers; phaseDurations
669
- // is the closed bounded struct built above. Absent stays absent on all three wire legs (bg runs.ts append,
670
- // the resume append, and the sync live SSE frame — all funnel through THIS builder).
671
330
  ...(num(ev.tokensAfter) !== undefined ? { tokensAfter: num(ev.tokensAfter) } : {}),
672
331
  ...(num(ev.triggerTokensBefore) !== undefined ? { triggerTokensBefore: num(ev.triggerTokensBefore) } : {}),
673
332
  ...(num(ev.durationMs) !== undefined ? { durationMs: num(ev.durationMs) } : {}),
674
333
  ...(phaseDurations !== undefined ? { phaseDurations } : {}),
675
334
  };
676
335
  }
677
- /**
678
- * `context_usage` 的白名单投影(core 1.414 新增一等 `TaskEvent` 分支;server [1669]⑥ 点名要的上下文用量面)。
679
- *
680
- * 🔴 两条 core 明确交代的口径,必须原样传下去、不要在服务端"聪明地"重算:
681
- * · `usedTokens > compactAtTokens` **就是引擎自己喂 `shouldCompact` 的谓词** —— 消费方直接用,别重新推导阈值。
682
- * · `windowTokens` 是 **autocompact 窗**,**不是**「模型上下文大小」(模型声明 `autoCompactTokens` 时物理请求
683
- * 窗更大)。渲染成"模型上下文"会给用户一个错的分母。
684
- * ⚠️ 它在**每个压缩边界**上报(不只压缩真发生时),所以一次任务里会有多条 —— 消费方按最后一条取当前值。
685
- * ⚠️ 它**没有**挂在 `turn_end.usage` 上(core 解释:边界检查跑在 turn_end 之后、对 flush 后的消息集,
686
- * 挂那儿会是"另一次更早的测量顶着同一个名字")。去 turn_end 找是找不到的。
687
- * 三个字段都是引擎派生的有限数标量 ⇒ 走 finite 门(与 compacted 的 1.289 标量同姿势),非有限即缺席。
688
- */
689
- // [2373]A-3 记账(2026-08-02):SDK 有臂、五端零读点(webC Composer 读的是本端自己的 state.context,
690
- // 非 wire 三键)——半死:契约面已就位、消费端一个都没接。头注下方的三条口径纪律警告的消费者还不存在;
691
- // 谁要做压缩面板,这是真正需要的量,先上黑板认领。
692
336
  export function contextUsageEventData(ev) {
693
337
  const n = (v) => (typeof v === "number" && Number.isFinite(v) ? v : undefined);
694
338
  return {
695
339
  ...(n(ev.usedTokens) !== undefined ? { usedTokens: n(ev.usedTokens) } : {}),
696
340
  ...(n(ev.windowTokens) !== undefined ? { windowTokens: n(ev.windowTokens) } : {}),
697
341
  ...(n(ev.compactAtTokens) !== undefined ? { compactAtTokens: n(ev.compactAtTokens) } : {}),
698
- // [2557] 2.4:本分支带 TaskEventIdentity 而 builder 曾剥——差集门补进三构造器当场红出(compacted 同病先例)。
699
342
  ...identityFields(ev),
700
343
  };
701
344
  }
702
- /**
703
- * `turn_end` 的白名单投影 —— 🔴 2026-07-25 补:`turn_end` 此前是**唯一没有 builder 的内容臂**,两条持久化点
704
- * (ledger-sink 与 resume 腿)各自手写 `{ usage }`,于是 core 的另两个字段被静默剥掉:
705
- * · `stopReason` —— **这一轮为什么停**(max_tokens / stop_sequence / …);
706
- * · `usageMissing: true` —— **用量不可信**的诚实标记。缺了它,"没有 usage"会被读成"用量是 0",
707
- * 而不是"未知";这正是那种"回放后看起来一切正常"的丢失。
708
- * 两者**在 live 腿看得见、durable 腿回放后永久消失** —— 与 [1622] 的 `label`、[1611] 的 workflowRunId 同一族
709
- * (白名单没跟上 core 新增字段)。补 builder 的同时也就把它纳入了 `wire-whitelist-exhaustiveness` 的编译期差集门。
710
- */
711
345
  export function turnEndEventData(ev) {
712
346
  return {
713
347
  ...(ev.usage ? { usage: ev.usage } : {}),
714
348
  ...(ev.usageMissing === true ? { usageMissing: true } : {}),
715
349
  ...(typeof ev.stopReason === "string" ? { stopReason: ev.stopReason } : {}),
716
- // [2557] 2.4:同 contextUsage——identity 键(eventId/parentToolCallId)曾被剥,差集门红出补齐。
717
350
  ...identityFields(ev),
718
351
  };
719
352
  }
720
- /** Map core's `turn_end.usage` (1.76 / core [R59]: inputTokens/outputTokens/cacheReadTokens/cacheWriteTokens/
721
- * costMicroUsd; core 3.0.0 adds required `totalInputTokens`) → the contract's `TraceTurn.tokens`.
722
- * Absent/empty → undefined.
723
- *
724
- * core 3.0.0([2296] 装车单 S3,§DESIGN-V2 V2-②,codex 复审「异议」采纳):`TraceTurn.tokens` 是本服务自有
725
- * 的投影契约,不是 wire verbatim——`in` 保持它一直以来的**总量**语义,3.0.0 起改取 `totalInputTokens`
726
- * (归一化总输入含 cache)优先,`inputTokens` 回落(旧帧/尚未升级的 core 下 `totalInputTokens` 缺席时,
727
- * `in` 落回旧值,读端零伤)。`uncachedIn` 只在**能确认自己踩在新契约帧上**(`totalInputTokens` 在场)时
728
- * 才发——旧帧的 `inputTokens` 是族依赖的旧语义,标成「uncachedIn(cache-MISS)」会是谎言。 */
729
353
  export function toContractTokens(usage) {
730
354
  if (!usage || typeof usage !== "object")
731
355
  return undefined;
@@ -740,10 +364,6 @@ export function toContractTokens(usage) {
740
364
  ...(u.cacheWriteTokens != null ? { cacheWrite: u.cacheWriteTokens } : {}),
741
365
  };
742
366
  }
743
- /** [2373]C-13:tool_start/tool_end 的**共享挑键**——`projectEvents`(turns 面)与 `mapTraceEvent`
744
- * (trace SSE 面)对同一账本行的字段选择逐字同构,此前两处独立手写,structured/totalChars/label
745
- * 三次扩字段各改两遍(见两处对偶注)。收一份:两面各自包装键名皮(id/name/input vs 同名),
746
- * 字段集合从此单点演进。 */
747
367
  export function toolCallFieldsOf(d) {
748
368
  return {
749
369
  id: String(d.toolCallId ?? ""),
@@ -760,26 +380,14 @@ export function toolResultFieldsOf(d) {
760
380
  ...(d.truncated ? { truncated: true } : {}),
761
381
  ...(Number.isFinite(d.totalChars) ? { totalChars: d.totalChars } : {}),
762
382
  ...(d.structured !== undefined ? { structured: d.structured } : {}),
763
- // core 5.9.0 W3([2535]):机器可判别的 park/死投影码(`gate.parked`/`tool.not_found`)——写侧
764
- // toolEndEventData 已带,读腿必须同键 verbatim(absent⇒absent),否则冷回放退回锚 abort 文案字面。
765
383
  ...(typeof d.errorCode === "string" ? { errorCode: d.errorCode } : {}),
766
- // core 5.18.1 #187:结算来源(闭集)——写侧 toolEndEventData 已带,读腿同键 verbatim(absent⇒absent)。
767
- // 🔴 读腿的闭集门是**独立的一道**,不是写侧那道的复述:账本行可以来自任何引擎世代、任何第三方
768
- // producer,写侧的门管不到它们;这里放行 = 把一个 core 从未发过的词直接送上 wire。
769
384
  ...(isApprovalSettledBy(d.settledBy) ? { settledBy: d.settledBy } : {}),
770
- // core 5.35.0 G-7(#263):结算归属(settledBy 旁的 WHOSE)——写侧已 redactSecrets 后落账,读腿
771
- // 同键过境;判形独立一道(同上款理由),开集身份串故只判 string 非空,不设词表。
772
385
  ...(typeof d.approver === "string" && d.approver !== "" ? { approver: d.approver } : {}),
773
- // 🔴 #337 红先补(A-067 F1a 现行案):`resolution` #326 提货时写侧落账、TraceBlock 留了槽位,本
774
- // 共享挑键漏挑 ⇒ live 有、turns/trace 冷回放丢——[1622]/label 剥键族在「单点挑键」建立之后的复发。
775
- // 读腿闭集门独立一道(账本行可来自任何世代/第三方 producer),判据同 core 谓词。
776
386
  ...(isAskDenyResolution(d.resolution) ? { resolution: d.resolution } : {}),
777
- // core 5.55.0 #333(#337 提货):park 扣着的 call id——写侧已判形落账,读腿独立同判(string 非空)。
778
387
  ...(typeof d.gatedCallId === "string" && d.gatedCallId !== "" ? { gatedCallId: d.gatedCallId } : {}),
779
388
  ...identityFields(d),
780
389
  };
781
390
  }
782
- /** Project a per-task event slice (seq-ascending) into ordered `TraceTurn[]`. Pure + total (never throws). */
783
391
  export function projectEvents(events, opts = {}) {
784
392
  const turns = [];
785
393
  if (opts.objective) {
@@ -810,16 +418,10 @@ export function projectEvents(events, opts = {}) {
810
418
  blocks.push({ type: "tool-call", ...toolCallFieldsOf(d) });
811
419
  break;
812
420
  case "tool_end": {
813
- // E1: output is read straight off the (already-redacted-at-append) event — no session join. Absent ⇒ undefined.
814
- // `structured` (core 1.203 CC card) was redacted at APPEND (toolEndEventData) — passthrough on the read
815
- // path so a cold-replay/turns consumer renders the same rich card the live wire carried (additive field).
816
421
  blocks.push({ type: "tool-result", ...toolResultFieldsOf(d), output: d.output, ...(d.isError ? { isError: true } : {}) });
817
422
  break;
818
423
  }
819
424
  case "prompt_assembled":
820
- // [998]②: whitelisted at APPEND (budget.ts promptManifestRecordOf — ids/digests/counts only);
821
- // passthrough on the read path (the `structured` posture). Lands ahead of the turn's first content
822
- // block, so the "Prompt 组成" card renders at the top of the turn it prepared.
823
425
  firstTs ??= ev.ts;
824
426
  blocks.push({
825
427
  type: "prompt-assembled",
@@ -833,17 +435,14 @@ export function projectEvents(events, opts = {}) {
833
435
  case "turn_end":
834
436
  flushTurn(ev.seq, ev.ts, toContractTokens(d.usage));
835
437
  break;
836
- // compacted / done / suspended / failed are lifecycle, not turn output — status comes from the run record.
837
438
  }
838
439
  }
839
- // A trailing in-progress turn (events after the last turn_end — e.g. a live task mid-turn).
840
440
  if (blocks.length > 0) {
841
441
  const last = events[events.length - 1];
842
442
  flushTurn(last.seq, last.ts);
843
443
  }
844
444
  return turns;
845
445
  }
846
- /** Structured `lastStep` (contract 2.1) from the latest event — UI maps an icon, never free text. */
847
446
  export function lastStep(latestEventType, status) {
848
447
  if (status !== "running")
849
448
  return { type: "idle" };
@@ -858,11 +457,7 @@ export function lastStep(latestEventType, status) {
858
457
  return { type: "responding" };
859
458
  }
860
459
  }
861
- /** A run record → the cheap list summary (contract 2.1). Token/cost/turns come from the terminal result.stats;
862
- * scenario isn't denormalized on the run row (omitted — a later push-index/column refinement). */
863
460
  export function taskSummary(run, latestEventType) {
864
- // [2373]D-1(活 bug 修):core 2.0.0 起浮点 costUsd 族整删,stats 只有整数 costMicroUsd —— 旧读点
865
- // (costUsd × 1e6)对现役 core 恒缺席,verbatim 透传才是唯一正确形。
866
461
  const stats = (run.result?.stats ?? {});
867
462
  const terminal = run.status !== "running";
868
463
  return {
@@ -880,28 +475,13 @@ export function taskSummary(run, latestEventType) {
880
475
  lastStep: lastStep(latestEventType, run.status),
881
476
  };
882
477
  }
883
- /** design/158 B2(lens4 N8)—— 重放边界的**读旧**归一:把历史账本里的 `brain_status` 行改叫 `status`。
884
- *
885
- * 同一条「大脑存活性」观测曾在两条腿上叫两个名字:live 腿(`POST /v1/tasks/stream`)发 `type:"status"`,
886
- * durable 腿的账本行落 `brain_status`,而 SDK 的公开 `AgentEvent` 联合**只有** `status` —— 于是走
887
- * `GET /v1/runs/:id/events` 的消费端会收到一个公开联合之外的帧(穷举式 closed-oneOf 消费者当场抛)。
888
- *
889
- * 写入侧已经改叫 `status`(ledger-sink.ts 与 resume 腿的 append 两处),但**存量行救不了**:任何
890
- * <1.317 的部署账本里都躺着 `brain_status` 行,重放它们仍会漏出旧名。所以「写新」必须配一个「读旧」——
891
- * 这个函数就是那个读边界,唯一调用点是 task_run SSE 的 `formatEvent`(routes/runs.ts)。
892
- *
893
- * 🔴 刻意**只**认这一个键,不做成映射表:帧名归一是历史包袱的一次性偿还,不是一条可以长胖的兼容层。
894
- * 再有第二个名字对不齐,应当在源头修,而不是往这里加一行。 */
895
478
  export function normalizeRunEventType(type) {
896
479
  return type === "brain_status" ? "status" : type;
897
480
  }
898
- /** Map one durable event → a sema-registry SSE trace event (contract 2.3). null = not a client event. The
899
- * durable log is block-grained, so each block ships as one "delta" the client appends (stream mode = "delta"). */
900
481
  export function mapTraceEvent(type, seq, data) {
901
482
  const mapped = mapTraceEventInner(type, seq, data);
902
483
  if (mapped === null)
903
484
  return null;
904
- // [2373]C-12:data 里补 `type`(=event 名)双发(fleet 流同批;error 帧 arm 自带 type 保持原值)。
905
485
  return { event: mapped.event, data: { type: mapped.event, ...mapped.data } };
906
486
  }
907
487
  function mapTraceEventInner(type, seq, data) {
@@ -913,51 +493,27 @@ function mapTraceEventInner(type, seq, data) {
913
493
  case "tool_start":
914
494
  return { event: "tool-call", data: { seq, ...toolCallFieldsOf(data) } };
915
495
  case "tool_end":
916
- // E1: output/truncated are already redacted+bounded at append; pass through. E2: identity additive.
917
496
  return { event: "tool-result", data: { seq, ...toolResultFieldsOf(data), isError: Boolean(data.isError), ...(data.output !== undefined ? { output: data.output } : {}) } };
918
497
  case "turn_end": {
919
498
  const tokens = toContractTokens(data.usage);
920
499
  return { event: "turn", data: { seq, ...(tokens ? { tokens } : {}) } };
921
500
  }
922
501
  case "prompt_assembled":
923
- // [998]②: whitelisted at append (ids/digests/counts, no prompt bodies) — passthrough, own event name.
924
502
  return { event: "prompt-assembled", data: { seq, ...data } };
925
503
  case "done":
926
504
  return { event: "done", data: { seq } };
927
- // 🔴 park 的账本事件名有**两个**(扫描P2,status/kind 同名词混淆病族):`suspended` 与
928
- // `needs_review` —— 写点是 `trace/ledger-sink.ts` 的 `appendParked(kind)`(kind 逐字取自
929
- // `result.status`)、`runs.ts` 的 bg 腿、`http/server.ts` 的两条 resume 腿;`needs_review` 就是
930
- // plan_review / dry_run 两种 gate 的 park 落名。只映射前者 ⇒ 一条 plan_review park 在 trace 流上
931
- // 落进 `default → null`,**一帧都不发**,消费端只看到流无声中止(既不是 done 也不是 error)。
932
- // 契约 2.3 的 park 标记只有 `suspended: true` 这一个位 ⇒ 两个词共用同一张帧(gate 细分在
933
- // `GET /v1/runs/:id`,不在这条流上)。
934
505
  case "suspended":
935
506
  case "needs_review":
936
507
  return { event: "done", data: { seq, suspended: true } };
937
508
  case "failed":
938
- // BL-13: redact at the read boundary too (defense-in-depth) — covers any errorMessage that reached the
939
- // log un-redacted via another write path; idempotent on the BL-12-redacted catch-path message.
940
- // A1/A6/B(复审 2026-07-29):error 帧统一为 `{ type:"error", errorCode, message }` —— 与 sse-log /
941
- // approvals 的帧同形。这一处是 3.0.0 摘 legacy `code` 键时的漏网:帧在 src/trace/ 铸,而当时的门只扫
942
- // src/http/,于是"SSE error 帧机器键统一为 errorCode"的公开声明与 wire 上跑的东西不一致(门是绿的)。
943
509
  return { event: "error", data: { type: "error", errorCode: "WORKER_DOWN", message: redactSecrets(String(data.errorMessage ?? "run failed")) } };
944
- // 🔴 **显式不投影**(A-032 P1-③ 裁定,2026-08-14)。这四型是账本写口(`trace/ledger-sink.ts` 与
945
- // resume 腿都 append 它们)却从来不在任何 case 臂上,于是走的是下面那个 `default` —— 而「刻意不发帧」
946
- // 与「忘了接线」在码面上**同形**,两者的下一步动作却相反(前者什么都不做,后者是缺陷)。写成显式臂
947
- // 就是把裁定钉在源码上。裁的是「不投影」,理由不是懒:契约 2.3 的帧集是**闭集**(thinking/content/
948
- // tool/turn/prompt/done/error 七类),给它加一个新帧名会让穷举式 closed-oneOf 消费端收到公开联合之外
949
- // 的帧 —— design/158 B2 那次(durable 腿发 `brain_status`、公开联合只有 `status`)真咬过的同形。
950
- // 这四型的消费面是 `GET /v1/runs/:id/events` 裸回放,不是这条流。特别地 `human_input` **不能**折成
951
- // content/thinking:它是「谁经哪条载体喂了这条 run」的归属事实,映射成 agent 内容就是编造。
952
- // 要改成投影 ⇒ 那是一次 wire 帧集扩张(能力发布件,须点名消费方表态),不是顺手加一行。
953
- // 闭合门:test/probe-wire-1.test.ts「A-032 P1-③ 账本写口 ↔ mapTraceEvent 投影的三方闭合门」。
954
510
  case "context_usage":
955
511
  case "compaction_outcome":
956
512
  case "wiring_manifest":
957
513
  case "human_input":
958
514
  return null;
959
515
  default:
960
- return null; // compacted etc. — not a client trace event
516
+ return null;
961
517
  }
962
518
  }
963
519
  //# sourceMappingURL=project.js.map