@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,18 +1,11 @@
1
- /** Line count of a tool-arg string ("" = 0; no trailing-newline inflation). */
2
1
  function lines(v) {
3
2
  if (typeof v !== "string" || v.length === 0)
4
3
  return 0;
5
4
  return v.split("\n").length;
6
5
  }
7
- // Q6 (core 1.161) CC names。5.0.0 RB-476:折叠面退役——本投影跑在历史事件日志上,pre-rename 旧行
8
- // (如 1.171 时代的 "MultiEdit")不再折到 canonical:旧名行按原名滑过 FILE_TOOLS 判定=不进 artifacts
9
- // 归类(诚实历史,读面不再兜旧名;耐久面按 [2364] 令牺牲)。
10
6
  const FILE_TOOLS = new Set(["Write", "Edit", "NotebookEdit"]);
11
7
  const GIT_PUSH_RE = /\bgit\b[^;|&]*\bpush\b\s*(.*)/;
12
- /** Project the durable event log into artifacts. Events must be in seq order (getEvents guarantees it). */
13
8
  export function projectArtifacts(events, run) {
14
- // Pair tool_start args with the matching tool_end by toolCallId — only a SUCCESSFUL call (isError false)
15
- // produces an artifact (a failed write/push produced nothing).
16
9
  const starts = new Map();
17
10
  const byKey = new Map();
18
11
  for (const ev of events) {
@@ -26,33 +19,25 @@ export function projectArtifacts(events, run) {
26
19
  const start = starts.get(d.toolCallId);
27
20
  if (!start)
28
21
  continue;
29
- starts.delete(d.toolCallId); // each call contributes at most once
30
- const toolName = start.toolName; // RAW(5.0.0):折叠面退役,历史旧名行按原名处理(见文件头注)
22
+ starts.delete(d.toolCallId);
23
+ const toolName = start.toolName;
31
24
  if (FILE_TOOLS.has(toolName)) {
32
- // Write/Edit key the write target on `file_path`. core 5.1.0([2390])收窄 schema 为 `file_path` 单形,
33
- // 但**门读侧 over-read 保留**是同批 KEEP 裁定(fail-closed 方向)——这里是耐久历史行的读侧([2373] D 类),
34
- // pre-1.169 emit/旧 durable checkpoint 仍带 `path`,回落臂是对自己旧数据的容忍,不是对上游新帧的兼容。
35
- // 再回落 NotebookEdit 的 `notebook_path`(core 1.163)。三键全缺 → 非带路径调用。
36
25
  const path = typeof start.args["file_path"] === "string" ? start.args["file_path"] : typeof start.args["path"] === "string" ? start.args["path"] : typeof start.args["notebook_path"] === "string" ? start.args["notebook_path"] : undefined;
37
26
  if (!path)
38
27
  continue;
39
- // Hunk-level line stats from the call args themselves (zero new storage; args already redacted).
40
- // Edit is batch-capable (core 1.202: an `edits[]` array — also the shape of a persisted legacy MultiEdit
41
- // row, folded onto "Edit" above): with a batch, sum each edit's new_string/old_string lines; a single
42
- // edit keeps the flat new_string/old_string fields.
43
28
  const edits = Array.isArray(start.args["edits"]) ? start.args["edits"] : [];
44
29
  const add = toolName === "Write" ? lines(start.args["content"])
45
30
  : toolName === "NotebookEdit" ? lines(start.args["new_source"])
46
31
  : edits.length > 0 ? edits.reduce((s, e) => s + lines(e["new_string"]), 0)
47
- : lines(start.args["new_string"]); // Edit (single)
48
- const del = toolName !== "Edit" ? 0 // Write/NotebookEdit carry no old_string (NotebookEdit delete-mode → 0 add 0 del = a touched count)
32
+ : lines(start.args["new_string"]);
33
+ const del = toolName !== "Edit" ? 0
49
34
  : edits.length > 0 ? edits.reduce((s, e) => s + lines(e["old_string"]), 0)
50
35
  : lines(start.args["old_string"]);
51
36
  const key = `file:${path}`;
52
37
  const prev = byKey.get(key);
53
38
  byKey.set(key, { kind: "file", ref: path, count: (prev?.count ?? 0) + 1, ts: ev.ts, add: (prev?.add ?? 0) + add, del: (prev?.del ?? 0) + del });
54
39
  }
55
- else if (toolName === "Bash") { // Q6 (core 1.161): was "bash"; canonical fold above is identity for it today
40
+ else if (toolName === "Bash") {
56
41
  const command = typeof start.args["command"] === "string" ? start.args["command"] : "";
57
42
  const m = GIT_PUSH_RE.exec(command);
58
43
  if (!m)
@@ -1,109 +1,28 @@
1
- /**
2
- * `engine_notice` wire 腿(#310,设计稿 = 黑板 [4630],core 表态 [4631] / cli 表态 [4634])——
3
- * core `EngineNotice` 的**第二终点**:按 sessionId 路由到那条会话的事件投影(SSE + durable 账本)。
4
- *
5
- * ## 为什么是白名单而不是全族上 wire
6
- * `RunnerDeps.onNotice` 是 **deps 级单 sink**(一部署一只),`EngineNotice` 上**没有 runId**——
7
- * 归属只能靠 `detail.sessionId`。于是:
8
- * · `config.*` 族一个都不带 sessionId ⇒ 结构性无处投(投给谁都是猜);
9
- * · core 侧**按 sink 去重**的码(`config.read_face_deployment_clamped` / `config.tool_model_gate_*`)
10
- * 一旦上 per-session 流,语义当场翻面:共享 sink 意味着整进程只发一次,第二条会话的用户看到的是
11
- * 「随机缺席」而不是「没发生」;
12
- * · 运维码(`config.env_timeout_discarded` 一族)推给终端用户 = 跨租户串台面。
13
- * 白名单里的三码,core 侧的去重单位分别是 **session / harvest / prepared-leg**(不是 sink),所以它们
14
- * 上 per-session 流不产生上面第二条的翻面——这正是白名单避开去重陷阱的判据,不是随手挑的三个码。
15
- *
16
- * ## 缺 sessionId 的白名单码:如实不投
17
- * 三码的 `detail.sessionId` 在 core 类型上是**可选**的(部分铸点确无 session 语境)。缺席时本腿
18
- * **不投**(fail-closed 方向 = 宁缺席不串台;猜一个"当前活跃会话"就是串台的定义)。日志终点仍逐字
19
- * 保留 ⇒ 运维观察面零回退,事实一条不丢。
20
- *
21
- * ## 白名单的演化机制(设计稿 ④)—— **注册表已到货(core 5.47/5.48)**
22
- * core [4631] 认领的 `NOTICE_AUDIENCE` **码级注册表**(code → "user"|"operator";缺席读 `"operator"`,
23
- * 保守默认)已随 5.47/5.48 到货,`EngineNotice.sessionId` 顶层键亦然。#292 提货批的处置是
24
- * **消费它当上界,而不是当场把本表整只退役**:
25
- * · 本表是**投递可行性**的判定(有没有 sessionId 归属键、core 侧的去重单位是不是 sink),
26
- * `NOTICE_AUDIENCE` 是**受众**的判定(这条码是给谁看的)——两者是合取,不是同一件事;
27
- * · 于是机器门(`test/engine-notice-wire.test.ts` ⑥)现在**双向**咬:白名单里出现一个 core 判
28
- * `"operator"` 的码 ⇒ 红(本仓不许自造第二份受众语义);core 判 `"user"` 的码没入册也没明拒 ⇒ 红。
29
- * · 真正的整表退役条件 = 上游把「归属键在不在」也纳入注册表(那时本表退化成一次查表),届时删。
30
- * 🔴 固定动作不变:**每一次 core 提货批**对表 core `EngineNotice` 码册的新增码,三选一登记
31
- * (入册 / 明拒 + 理由 / 申裁),并在 `docs/core-consumption/LEDGER.json` 的该批行里留痕。
32
- * 漏对表的后果不是报错而是**静默缺席**:新码永远不上 wire,消费端(cli)无从知道该码存在。
33
- */
34
1
  import { redactSecrets } from "./redact.js";
35
2
  import { recordFailOpen } from "../observability/fail-open.js";
36
- /**
37
- * 上 wire 的通告码 —— **显式闭集常量表**(设计稿 ①,起步三码)。
38
- *
39
- * 三码同族:都是「这条 session 的记忆姿态」的披露,resume 之后仍然相关 ⇒ 也进 durable 账本
40
- * (cli 断连补看走既有 events 重放腿,[4634](ii))。
41
- */
42
3
  export const ENGINE_NOTICE_WIRE_CODES = [
43
- /** design/178 §3:本会话记忆越过一次性 POLLUTED 态。core 去重单位 = **session**。`detail: { reason, sessionId? }` */
44
4
  "memory.session_polluted",
45
- /** design/178 §3:被污染会话的 harvest 执行了容纳。core 去重单位 = **harvest**(检查点收与终局收是两件事)。
46
- * `detail: { count, moved, escalated, reason?, sessionId? }` —— 🔴 `moved` 与 `escalated` **不可相减**
47
- * (就地墓碑同时计入两者,core 顶注);消费端并列呈现,不做算术。 */
48
5
  "memory.harvest_quarantined",
49
- /** design/324 #324①:`memoryDelegationEvidence:"attested-only"` 下,一次静态面本会打的污染标记被豁免。
50
- * core 去重单位 = **prepared leg**。`detail: { reason, subagentType?, sessionId? }` */
51
6
  "memory.delegation_static_mark_waived",
52
- /** design/336 §4/§6.3(core 5.47/5.48 到货,#292 提货批对表入册):**instruction-hold 生命周期**三码。
53
- * 同族三条都是「这条 session 的记忆此刻处在什么姿态」的披露,与既有三码同一条判据:
54
- * · core `NOTICE_AUDIENCE` 明列 **"user"**(到货的码级注册表,见本文件顶注的演化机制);
55
- * · 铸点在 `memoryHoldNotices(report, sessionId)`,`detail.sessionId` 随 harvest 报告一起给 ⇒ 投得出去;
56
- * · core 去重单位 = **harvest**(检查点收与终局收是两件事),不是 sink ⇒ 上 per-session 流不翻面。
57
- * `detail: { count, paths, sessionId? }`(paths 已 neutralize + 截 20 条)。 */
58
7
  "memory.hold_opened",
59
- /** 同族:被扣住的 instruction 条目在写者会话干净结算(或宿主阀门放行)后重走全套门并提交。
60
- * `detail: { count, paths, sessionId? }` */
61
8
  "memory.hold_released",
62
- /** 同族:被扣住的条目移交控制面隔离区。`detail: { count, disposed: [{ path, terminal }], sessionId? }`
63
- * —— 🔴 `terminal:"expired"` 是**超时不是定罪**(core 铸文逐字如此),消费端不得渲染成裁决语气。 */
64
9
  "memory.hold_disposed",
65
10
  ];
66
11
  const WIRE_CODES = new Set(ENGINE_NOTICE_WIRE_CODES);
67
- /** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
68
12
  export function isEngineNoticeWireCode(code) {
69
13
  return WIRE_CODES.has(code);
70
14
  }
71
- /**
72
- * 归属键抽取 —— `detail.sessionId` 提升为顶层(设计稿 ②:归属键一等化)。
73
- *
74
- * 非串 / 空串一律按**缺席**处理(不铸 `"undefined"` 这种看起来合法的假值——`compactionOutcomeEventData`
75
- * 的同款缺键守卫纪律)。core [4631] 的顶层 `EngineNotice.sessionId` 到货后,这里先读顶层再回落 detail
76
- * (两处同值,additive 零 BREAKING),届时本函数是唯一改动点。
77
- */
78
15
  export function engineNoticeSessionId(notice) {
79
- const top = notice.sessionId; // core 5.46+ 顶层键(到货前恒 undefined,读它零代价)
16
+ const top = notice.sessionId;
80
17
  if (typeof top === "string" && top.length > 0)
81
18
  return top;
82
19
  const fromDetail = notice.detail?.sessionId;
83
20
  return typeof fromDetail === "string" && fromDetail.length > 0 ? fromDetail : undefined;
84
21
  }
85
- /** 自由文本(message / detail 里的串)的长度上限。`workspaceChangedEventData` 的 300 是**路径**级 bound;
86
- * 通告的 `reason` 是一整句话(core 侧已 neutralize + bound),这里给它一个不腰斩正常句子的上限。 */
87
22
  const NOTICE_TEXT_MAX = 1000;
88
- /** detail 递归消毒的结构上限。三码的真 detail 都是 ≤5 键的扁平对象 ⇒ 生产上永不触顶;
89
- * 上限存在的理由是**未来码**(以及被污染输入)不得把任意大小的对象推上 wire。 */
90
23
  const DETAIL_MAX_DEPTH = 4;
91
24
  const DETAIL_MAX_KEYS = 32;
92
25
  const DETAIL_MAX_ITEMS = 32;
93
- /**
94
- * `detail` 消毒(设计稿 ②:`redactSecrets` + 尺寸 bound;`workspaceChangedEventData` 先例)。
95
- *
96
- * core 已 neutralize 三码的 `reason`(工具名/子代类型是 host/模型可控输入),**server 按纪律再过一遍**
97
- * ——§E1 的既有姿态:上 wire 的自由文本一律经本仓的脱敏器,不因为上游说过一次就免检。
98
- *
99
- * 契约(逐条,门在 `test/engine-notice-wire.test.ts`):
100
- * · 串 ⇒ `redactSecrets` + 截到 {@link NOTICE_TEXT_MAX};
101
- * · 有限 number / boolean / null ⇒ 逐字(harvest 的 count/moved/escalated 是判据数字,不许变形);
102
- * · 非有限 number(NaN/Infinity)⇒ **丢键**(JSON 里它们会变成 `null`,一个看起来合法的假读数);
103
- * · 数组 ⇒ 逐项消毒,超 {@link DETAIL_MAX_ITEMS} 截断;对象 ⇒ 逐键消毒,超 {@link DETAIL_MAX_KEYS} 丢尾键;
104
- * · 深度超 {@link DETAIL_MAX_DEPTH} ⇒ 该子树丢弃;
105
- * · 其余(function / symbol / bigint / undefined)⇒ 丢键(JSON 不可承载或语义不明)。
106
- */
107
26
  export function sanitizeNoticeDetail(detail) {
108
27
  const out = sanitizeValue(detail ?? {}, 0);
109
28
  return (out ?? {});
@@ -142,15 +61,8 @@ function sanitizeValue(v, depth) {
142
61
  }
143
62
  return rec;
144
63
  }
145
- return undefined; // function / symbol / bigint / undefined
64
+ return undefined;
146
65
  }
147
- /**
148
- * `engine_notice` 载荷构造器(三条终点腿同源:live SSE / durable 账本 / 未来的任何第四条腿)。
149
- *
150
- * 与 `trace/project.ts` 的 `*EventData` 族同纪律(白名单挑键、禁 `{...notice}`),但**不住在那个文件里**:
151
- * 那一族的入参全是 core `TaskEvent` 的臂(有 `wire-whitelist-exhaustiveness` 的臂集编译门看着),
152
- * 而 `EngineNotice` 不是 `TaskEvent` —— 混进去会让那道门的语义变成"两个不同来源的联合",判别力反而降。
153
- */
154
66
  export function engineNoticeEventData(notice, sessionId, nowMs) {
155
67
  return {
156
68
  code: notice.code,
@@ -161,34 +73,12 @@ export function engineNoticeEventData(notice, sessionId, nowMs) {
161
73
  };
162
74
  }
163
75
  let nextLegId = 1;
164
- /**
165
- * sessionId → 该会话活跃 run 腿的投递口。**进程内**(`defaultSubagentTailBus` 同款姿势):
166
- * `onNotice` 是同进程的同步回调,通告与它要投的那条腿必然同副本,跨副本路由这件事结构上不存在。
167
- *
168
- * 🔴 跨 session 隔离靠的是 **Map 键的精确串相等**,不是任何「最近活跃」回落 —— 缺席就是缺席(G5)。
169
- *
170
- * 🔴 **同 session 的跨代隔离靠 legId「最新代独占」**(#310 codex 对抗复审 R1-[high],验真后修)。
171
- * 病(真窗,本仓每一处 in-flight 登记都为它写过 identity-guard):run 行在**流体内**就已 setTerminal /
172
- * setSuspended,而本腿的注销在 `finally` —— 中间那一窗里,同一条 session 上的**下一条腿**(快速续跑 /
173
- * 紧接着的新提交)已经注册好了自己的口。若按「会话内全量扇出」投,新腿的一条通告会**同时**落进:
174
- * · 旧腿那条已 end 的 SSE(顶多记一次 fail-open,无害),以及
175
- * · **旧 taskId 的账本** —— 一条不属于那条 run 的、终态之后的行;resume 复用同 taskId 时还会与新腿
176
- * 抢同一个 `(task_id, seq)` 域。这不是跨租户泄露,是**同租户跨 run 的账本污染**,同样必须关死。
177
- * 药:每次 `registerLeg` 铸一个单调 legId,`route` **只投最新代**;旧代的口留在表里(它自己的注销仍
178
- * 按身份精确删,不会误删新代),但一帧都收不到。方向 = fail-closed(宁可旧腿少一帧,不可新腿的事实
179
- * 写进旧腿的账)。
180
- */
181
76
  export class EngineNoticeRouter {
182
77
  legs = new Map();
183
- /**
184
- * 登记**一条腿**的全部口(live / durable 作为**一代**同进同出)。返回注销函数(幂等,身份精确删)。
185
- *
186
- * 每只口各自 try 隔离在 {@link route} 里,单口抛不牵连同腿另一口。
187
- */
188
78
  registerLeg(sessionId, sinks) {
189
79
  const leg = { legId: nextLegId++, sinks: [...sinks] };
190
80
  const list = this.legs.get(sessionId) ?? [];
191
- list.push(leg); // 追加 = 本代成为最新代
81
+ list.push(leg);
192
82
  this.legs.set(sessionId, list);
193
83
  let done = false;
194
84
  return () => {
@@ -198,39 +88,31 @@ export class EngineNoticeRouter {
198
88
  const cur = this.legs.get(sessionId);
199
89
  if (!cur)
200
90
  return;
201
- const at = cur.indexOf(leg); // 身份精确删:绝不按位置/长度删(会误删新代)
91
+ const at = cur.indexOf(leg);
202
92
  if (at >= 0)
203
93
  cur.splice(at, 1);
204
94
  if (cur.length === 0)
205
95
  this.legs.delete(sessionId);
206
96
  };
207
97
  }
208
- /** 单口腿的便捷形(测试与单终点腿用)。 */
209
98
  register(sessionId, sink) {
210
99
  return this.registerLeg(sessionId, [sink]);
211
100
  }
212
- /** 诊断/门用:某会话此刻**最新代**有几只口在听(旧代不计——它们收不到帧)。 */
213
101
  sinkCount(sessionId) {
214
102
  const list = this.legs.get(sessionId);
215
103
  return list && list.length > 0 ? list[list.length - 1].sinks.length : 0;
216
104
  }
217
- /** 诊断/门用:某会话此刻有几代腿在表里(>1 = 正处在换代窗内)。 */
218
105
  legCount(sessionId) {
219
106
  return this.legs.get(sessionId)?.length ?? 0;
220
107
  }
221
- /**
222
- * 分流器的 wire 半场。返回**真投出去的口数**(0 = 没投:非白名单码 / 缺 sessionId / 该会话无活跃腿)。
223
- *
224
- * 单只口抛错**不吃掉同腿的其它口**(fleet bus 订阅者隔离同理由),并按 F 类登记留痕。
225
- */
226
108
  route(notice, nowMs = Date.now()) {
227
109
  if (!isEngineNoticeWireCode(notice.code))
228
110
  return 0;
229
111
  const sessionId = engineNoticeSessionId(notice);
230
112
  if (sessionId === undefined)
231
- return 0; // 如实不投(顶注:宁缺席不串台)
113
+ return 0;
232
114
  const list = this.legs.get(sessionId);
233
- const leg = list && list.length > 0 ? list[list.length - 1] : undefined; // 只投最新代
115
+ const leg = list && list.length > 0 ? list[list.length - 1] : undefined;
234
116
  if (!leg || leg.sinks.length === 0)
235
117
  return 0;
236
118
  const row = engineNoticeEventData(notice, sessionId, nowMs);
@@ -247,18 +129,7 @@ export class EngineNoticeRouter {
247
129
  return delivered;
248
130
  }
249
131
  }
250
- /** 进程单例(`defaultSubagentTailBus` 同款):分流器与 run 腿直接 import,不穿长参数列表;测试自 new 独立实例。 */
251
132
  export const defaultEngineNoticeRouter = new EngineNoticeRouter();
252
- /**
253
- * run 腿的注册助手 —— 三条腿(sync stream / bg / resume-verify)用**同一个**形,免得各写一份漂。
254
- *
255
- * `sessionId` 缺席(adhoc 无会话腿)⇒ 返回 no-op 注销函数,一个字节不注册(顶注的 fail-closed 方向)。
256
- * `live` / `durable` 各自可缺席:bg 腿没有 live SSE(消费方走 events tail),ephemeral sync 腿没有账本。
257
- *
258
- * 🔴 两口注册成**两只独立 sink**(不是一只闭包里顺序调两次):route 的隔离粒度是 sink,合成一只会让
259
- * live 写(断连的 socket、JSON.stringify 抛)吃掉同一拍的 durable 落账 —— 而 durable 那半正是断连之后
260
- * 唯一还看得见这条通告的地方(G3),两者的失败必须互不牵连。
261
- */
262
133
  export function registerEngineNoticeLeg(opts) {
263
134
  const { sessionId, live, durable } = opts;
264
135
  if (sessionId === undefined || sessionId.length === 0)
@@ -266,9 +137,6 @@ export function registerEngineNoticeLeg(opts) {
266
137
  const sinks = [...(live ? [live] : []), ...(durable ? [durable] : [])];
267
138
  if (sinks.length === 0)
268
139
  return () => undefined;
269
- // 🔴 两口登记成**同一代**(`registerLeg` 一次调用)而不是两次 `register`:代是"腿"的属性,拆成两代
270
- // 会让换代时旧腿的一只口先被超代、另一只还没 —— 半代状态没有任何正确语义。口的失败隔离是 `route`
271
- // 里的 per-sink try(与代无关),两者是正交的两件事。
272
140
  return (opts.router ?? defaultEngineNoticeRouter).registerLeg(sessionId, sinks);
273
141
  }
274
142
  //# sourceMappingURL=engine-notice-wire.js.map
@@ -1,22 +1,6 @@
1
1
  import { redactSecrets } from "./redact.js";
2
2
  import { toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, humanInputEventData, turnEndEventData, contextUsageEventData } from "./project.js";
3
3
  import { NotifiedKeys, taskNotificationStreamKey } from "../orchestration/workflow-completion-inbox.js";
4
- /**
5
- * **单写串行链**的可复用形(#310 codex 对抗复审 R1-[high],验真后修)—— `seq` 的**分配发生在链步内**,
6
- * 于是「分配序 == 发起序 == 提交序」。
7
- *
8
- * 病(实测形,`LedgerSink` 内部早有这条纪律的内联版、其顶注 [1.211 codex H2] 逐字记着它):裸写口
9
- * (`(type, data) => rs.appendEvent(taskId, ++seq, type, data)`)在**同步分配 seq、立刻发起 insert**。
10
- * 主循环 await 自己的写,所以主循环内部有序;但只要有**一个 fire-and-forget 写者**(子代 forward 帧、
11
- * #310 的通告口),它的 N 号 insert 可能在池化 SQL 后端上晚于后发的 N+1 号提交。events tail 的读法是
12
- * `getEvents(id, afterSeq)` —— 读到 N+1 就把游标推过 N,**那一行此后永远不会被投递**(重连也不会,
13
- * 游标只前进)。行在库里,消费端结构性看不见。
14
- *
15
- * 药:所有写口共用一条 promise 链,`++seq` 在链步内取。链上一步失败**不断链**(catch → undefined),
16
- * 调用方仍拿到自己那一步的真实结果(await 者照常收到 reject)——与 `LedgerSink` 内联版逐字同语义。
17
- *
18
- * ⚠️ 用它的前提是**这条腿的全部写口都走它**(混用一个绕开链的写口 = 病灶原样保留)。
19
- */
20
4
  export function createSerialLedgerAppend(appendEvent, startSeq) {
21
5
  let seq = startSeq;
22
6
  let chain = Promise.resolve();
@@ -29,49 +13,35 @@ export function createSerialLedgerAppend(appendEvent, startSeq) {
29
13
  export function createLedgerSink(opts) {
30
14
  const keys = opts.notifiedKeys ?? new NotifiedKeys();
31
15
  let seq = 0;
32
- // [1.211 codex H2] 单写串行链:seq 的「分配」在链步内发生 ⇒ 分配序 == durable 提交序。没有它,
33
- // onForwardEvent 的 fire-and-forget 写与主循环的 awaited 写交错时,高 seq 可先落盘对 events tail 可见,
34
- // 迟到的低 seq 在读方游标(afterSeq)前进后被永久遗漏。链上一步失败不断链(catch → undefined),
35
- // 调用方拿到的是自己那步的真实结果(await 者照常收到 reject)。
36
16
  let chain = Promise.resolve();
37
17
  let text = "";
38
18
  let reasoning = "";
39
- // E18 resume-at: the turn's FIRST text_delta eventId = the message's stable per-message resume handle,
40
- // stamped on the durable `text` event (ONE handle per message even when a mid-turn tool_start flushes the
41
- // text early). Latched on the first DEFINED eventId of the turn (`??=` — a later part after a tool must not
42
- // re-latch), cleared at turn_end — the exact `TurnAnchorCapture.firstTextEventId` semantics. (The bg leg's
43
- // anchor tracks the same value for its own anchor-put; both latches see the same events in the same order,
44
- // so the stamps agree byte-for-byte — the anchor's extra `turnHadTool` state never feeds the stamp.)
45
19
  let textEventId;
46
- // #245 S1:活性打点(顶注契约:两打点位之一;抛错吞掉,绝不拖账本/事件循环下水)。
47
20
  const beat = () => {
48
21
  try {
49
22
  opts.onActivity?.();
50
23
  }
51
24
  catch {
52
- /* 分诊打点失败只丢判别材料 */
53
25
  }
54
26
  };
55
27
  const append = (type, data) => {
56
- beat(); // 打点位②:不经 onEvent 的直写路径(parked/done/forward)在这里被兜住
28
+ beat();
57
29
  const step = chain.then(() => opts.appendEvent(++seq, type, data));
58
30
  chain = step.then(() => undefined, () => undefined);
59
31
  return step;
60
32
  };
61
33
  const flush = async () => {
62
- // Thinking precedes content within a turn → flush a buffered reasoning block FIRST (redacted).
63
34
  if (reasoning) {
64
35
  await append("reasoning", { text: redactSecrets(reasoning) });
65
36
  reasoning = "";
66
37
  }
67
38
  if (text) {
68
- // E18: stamp the turn's first-text eventId as the per-message resume handle (additive; tolerate-absent).
69
- await append("text", { text, ...(textEventId ? { eventId: textEventId } : {}) }); // persist first; clear only on success so a failed write doesn't drop it
39
+ await append("text", { text, ...(textEventId ? { eventId: textEventId } : {}) });
70
40
  text = "";
71
41
  }
72
42
  };
73
43
  const onEvent = async (ev) => {
74
- beat(); // 打点位①:所有流事件(含只进缓冲的 text/reasoning delta)——「事件到达 sink」= turn 在推进
44
+ beat();
75
45
  switch (ev.type) {
76
46
  case "text_delta": {
77
47
  const td = ev;
@@ -80,104 +50,57 @@ export function createLedgerSink(opts) {
80
50
  break;
81
51
  }
82
52
  case "reasoning_delta":
83
- // Persist the agent's thinking for the trace UI (core [R57]: reasoning IS on the live stream but NOT in
84
- // the metadata Tracer — capturing it is the service's call). Gated by TRACE_THINKING; redacted on flush.
85
53
  if (opts.persistThinking)
86
54
  reasoning += ev.delta;
87
55
  break;
88
56
  case "tool_start":
89
57
  await flush();
90
- // E1+E2 (shell-host contract): redacted args + identity via the shared whitelist builder — identical
91
- // persisted shape across every leg.
92
58
  await append("tool_start", toolStartEventData(ev));
93
59
  break;
94
60
  case "tool_end":
95
61
  await flush();
96
- // E1: redacted model-facing output (+truncated, core size-bound) + E2 identity, via the shared builder.
97
62
  await append("tool_end", toolEndEventData(ev));
98
63
  break;
99
64
  case "status":
100
- // S4 (SILENT-FALLBACK P0-a): persist the brain-layer liveness observation. core marks the live frame
101
- // EPHEMERAL (it never replays it) — this durable row is OUR observation record so async/replay readers
102
- // can attribute a stalled turn. flush() first (review MED-2): a reconnect can fire MID-TEXT — without
103
- // the flush the already-streamed text lands AFTER this row and the durable timeline inverts.
104
- // design/158 B2: durable row type aligned to the live frame name ("status", not "brain_status") — the
105
- // two legs were emitting the SAME logical event under two different `type` strings, and the SDK's
106
- // public `AgentEvent` union only declared "status" (a durable-leg reader saw a frame outside its own
107
- // public type). 写新读旧:这里只管写新;**存量行救不了**(任何旧部署的账本里都躺着 `brain_status`
108
- // 行),所以重放边界配了一次性归一 —— `normalizeRunEventType`(trace/project.ts),唯一调用点是
109
- // task_run SSE 的 `formatEvent`(routes/runs.ts)。两半缺一不可:只改写入侧的话,存量行重放时
110
- // 仍会漏出公开联合之外的名字。无 migration、无回填(读边界归一比改历史行便宜且不可逆风险为零)。
111
65
  await flush();
112
66
  await append("status", brainStatusEventData(ev));
113
67
  break;
114
68
  case "context_usage":
115
- // core 1.414 一等分支(server [1669]⑥ 要的上下文用量面)。每个压缩边界上报一次 ⇒ 一次任务多条,
116
- // 消费方取最后一条为当前值。两条口径见 `contextUsageEventData` 旁注(别把 windowTokens 当模型上下文)。
117
69
  await flush();
118
70
  await append("context_usage", contextUsageEventData(ev));
119
71
  break;
120
72
  case "compacted":
121
73
  await flush();
122
- // MF-18 (1.149) trigger/tokensBefore + core 1.219 attachedFiles/preserved_segment via the SHARED
123
- // whitelist builder — the shell renders CC's post-compact `Read {path}` cards + the divider from these.
124
74
  await append("compacted", compactedEventData(ev));
125
75
  break;
126
76
  case "diagnostics":
127
- // core 1.220 (design/121): NEW LSP diagnostics at a turn boundary — the shell's render source for CC's
128
- // "Found N new diagnostic issues" row. §E1: language-server output is UNTRUSTED RAW → builder redacts.
129
77
  await flush();
130
78
  await append("diagnostics", diagnosticsEventData(ev));
131
79
  break;
132
80
  case "steering_injected":
133
- // [1550]/[1552]: core's mid-run system-reminder injection (todo/file-change/date-roll/skills-delta/…) —
134
- // the shell's render source for CC's steering-attachment row. §E1: preview is UNTRUSTED free text →
135
- // builder redacts. Host/self-session only — NOT mirrored into onForwardEvent (a delegated child's own
136
- // reminders are its internal bookkeeping, not host-relevant progress like tool_start/task_progress).
137
81
  await flush();
138
82
  await append("steering_injected", steeringInjectedEventData(ev));
139
83
  break;
140
84
  case "compaction_outcome":
141
- // [2373]A-2/C-2:此前 durable 双腿零 case ⇒ 回放即消失(与 [1622] label / [1611] workflowRunId
142
- // 同族)。共享 builder(§E1 脱敏)三腿同源;差集门 durable-append-arm-parity 看守。
143
85
  await flush();
144
86
  await append("compaction_outcome", compactionOutcomeEventData(ev));
145
87
  break;
146
88
  case "workspace_changed":
147
- // [1559]二 core 裁定:design/99 §E13 旧臂,L1 门扩臂时首次撞见。durable 记账与其余内容型臂
148
- // 同处置(轨迹回放时「何时 cd 到了哪」是合法历史事实,「不当持久态渲染」是 LIVE 消费端纪律,
149
- // 不是不许落账)——共享白名单构造器 + §E1 redact。
150
89
  await flush();
151
90
  await append("workspace_changed", workspaceChangedEventData(ev));
152
91
  break;
153
92
  case "wiring_manifest":
154
- // 🔴 core 5.14.0 design/173:装配自证清单。账本行**同样**要剥 `governance` 段——durable 行可经
155
- // `GET /v1/runs/:id/events` 重放给同一批租户订阅者,是与 sync 腿完全相同的泄露面,所以用**同一个**
156
- // 构造器(两腿分头挑键就是下一次泄露的种子)。core 契约:no projection ⇒ do not disclose。
157
93
  await flush();
158
94
  await append("wiring_manifest", wiringManifestEventData(ev));
159
95
  break;
160
96
  case "human_input":
161
- // core 5.14.0 design/171:人类输入生命周期账本(谁/经哪条载体/投递成没成)。落账是本臂的
162
- // 正当用途——「这条 run 是谁喂的」正是事后对账要的历史事实;身份串的脱敏在共享构造器里。
163
97
  await flush();
164
98
  await append("human_input", humanInputEventData(ev));
165
99
  break;
166
100
  case "task_progress":
167
- // core breakpoint⑤: a subagent's live progress tick → an INLINE "subagent working" card for the tailing
168
- // client (S2-a). Shared whitelist+redact builder (+ E2 identity for the exact concurrent-tick binding).
169
101
  await append("task_progress", taskProgressEventData(ev));
170
102
  break;
171
103
  case "task_notification": {
172
- // core 1.202 (design/115 P2): a background task finished while this harness was live — persist the
173
- // display projection so a tailing client renders it (summary/result are UNTRUSTED free text → shared
174
- // builder redacts). core [784]④a+④b: seq-folded shared key + skip keys this leg already delivered
175
- // (start-of-leg inbox drain or observer arm) — core's pend-drain replays a double-parked idle arrival
176
- // as a stream event; appending it again doubles the frame in the durable tail. codex F1: a key claimed
177
- // by a fire-and-forget arm may have FAILED to land — await the claim's outcome; false ⇒ write anyway
178
- // (this stream replay is the notification's last copy on this leg) and promote the key with THIS
179
- // write's outcome. The claim promise is registered before the awaited append so a concurrent observer
180
- // callback can't double-claim mid-write.
181
104
  await flush();
182
105
  const tn = ev.notification;
183
106
  const tnKey = tn?.task_id ? taskNotificationStreamKey({ task_type: tn.task_type, task_id: tn.task_id, status: String(tn.status), seq: tn.seq }) : undefined;
@@ -185,26 +108,21 @@ export function createLedgerSink(opts) {
185
108
  if (tnPrior === undefined || !(await tnPrior)) {
186
109
  const write = append("task_notification", taskNotificationEventData(ev));
187
110
  if (tnKey !== undefined)
188
- keys.set(tnKey, write.then(() => true, () => false)); // the stream delivered this key
111
+ keys.set(tnKey, write.then(() => true, () => false));
189
112
  await write;
190
113
  }
191
114
  break;
192
115
  }
193
116
  case "turn_end": {
194
117
  await flush();
195
- // core 1.76 (service [41]/core [R59]): per-turn usage rides turn_end.usage — persist it for TraceTurn.tokens.
196
118
  const usage = ev.usage;
197
- await append("turn_end", turnEndEventData(ev)); // 白名单 builder(此前手写 {usage},剥掉了 stopReason/usageMissing)
198
- textEventId = undefined; // next turn latches its own first-text handle (TurnAnchorCapture parity)
119
+ await append("turn_end", turnEndEventData(ev));
120
+ textEventId = undefined;
199
121
  break;
200
122
  }
201
- // `done` / `message_committed` / unknown: deliberate no-ops — see the interface doc.
202
123
  }
203
124
  };
204
125
  const onForwardEvent = (e) => {
205
- // 1.78 (review HIGH) + C1 (core 1.219, spec.forwardSubagentEvents): a delegated child's lifecycle rides the
206
- // forward sink (each stamped parentToolCallId — the shell nests them under the delegation card). Same
207
- // shared builders as the top stream; fire-and-forget (`++seq` is sync → unique seqs vs the awaited loop).
208
126
  const t = e.type;
209
127
  if (t === "task_progress") {
210
128
  void append("task_progress", taskProgressEventData(e)).catch(() => undefined);
@@ -216,15 +134,8 @@ export function createLedgerSink(opts) {
216
134
  void append("tool_end", toolEndEventData(e)).catch(() => undefined);
217
135
  }
218
136
  };
219
- // #255:第五席的写口 —— 一行,但**位置**是它的全部:`append` 在这里被**同步**调用,序号槽在回调进入
220
- // 的那一刻就占住(理由与判据全文见接口上的 doc 注)。别为了「与 onEvent 同形」把它改写成
221
- // `onEvent({type:"status",…})`:那一次 `await flush()` 正是被修掉的病。
222
137
  const appendStatus = (ev) => append("status", brainStatusEventData(ev));
223
- const appendParked = (kind, result) =>
224
- // Never the capability checkpointToken — only the non-secret gate (search [18] Q1b)。
225
- // [4913]:结果若已被 graftParkToolCallId 补过待批 toolCallId(调用方在 park 点先 graft),随行投影;
226
- // 缺席(tool-less park / graft 未接 / 读失败)= 键不铸([1995]② OMITTED 契约,不编 null)。
227
- append(kind, {
138
+ const appendParked = (kind, result) => append(kind, {
228
139
  gate: result?.checkpointGate ?? null,
229
140
  ...(typeof result?.toolCallId === "string" ? { toolCallId: result.toolCallId } : {}),
230
141
  });