@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,9 +1,5 @@
1
1
  const DEFAULT_BUCKETS = [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60];
2
2
  function labelKey(labels) {
3
- // Escape the delimiters (`\` `,` `=`) IN THE VALUE so distinct label sets can't collide onto one series:
4
- // without this, {a:"1,b=2"} and {a:"1",b:"2"} both key to "a=1,b=2" and silently merge (tool names come from
5
- // MCP, model ids from config — not strongly constrained). This key is internal series identity only;
6
- // Prometheus rendering escapes separately via renderLabels/esc.
7
3
  return Object.keys(labels)
8
4
  .sort()
9
5
  .map((k) => `${k}=${String(labels[k]).replace(/\\/g, "\\\\").replace(/,/g, "\\,").replace(/=/g, "\\=")}`)
@@ -39,11 +35,6 @@ export class Metrics {
39
35
  if (!this.histos.has(name))
40
36
  this.histos.set(name, { help, buckets, series: new Map() });
41
37
  }
42
- // 根因升类:inc 对未注册名曾是静默 no-op — 15 个只 inc 未 counter() 的计数器
43
- // (permission_denied_total / compaction_events_total / hook_llm_calls_total / fleet_lease_* /
44
- // metric_label_overflow_total[溢出护栏自己的计数器!]等)整批从未出现在 /metrics。观测面的
45
- // fail-silent 正是观测要消灭的东西 → inc/setGauge/addGauge 改为首用自动注册(help 标注
46
- // auto-registered,提醒补显式注册拿正经 HELP 文案);已知 15 名已在 createMetrics 补显式注册。
47
38
  inc(name, labels = {}, n = 1) {
48
39
  let c = this.counters.get(name);
49
40
  if (!c) {
@@ -87,11 +78,6 @@ export class Metrics {
87
78
  s.count += 1;
88
79
  h.series.set(k, s);
89
80
  }
90
- /** [2557] 2-j:dynamic gauge 求值抛错不再折成 0 —— 0 是该 gauge 的合法读数,折 0 让故障态与真实 0
91
- * 在 wire 上逐字节不可区分(与上方 inc 静默 no-op 同族:观测面的 fail-silent 正是观测要消灭的东西)。
92
- * 披露方向:错帧**不发样本行**(缺席=读数未知,面板显示 no data 而非假 0)+ 计
93
- * `metric_gauge_eval_errors_total{name}`(响亮、可告警)。每次求值(每次刮取/snapshot)错一次计一次;
94
- * 仍然绝不 propagate(/metrics 不因单个读数器坏而 500)。 */
95
81
  evalDynamic(name, fn) {
96
82
  try {
97
83
  return fn();
@@ -101,9 +87,7 @@ export class Metrics {
101
87
  return undefined;
102
88
  }
103
89
  }
104
- /** Prometheus text exposition (content-type text/plain; version=0.0.4). */
105
90
  render() {
106
- // dynamic 先求值(2-j):错帧的 inc 要落进**本次**刮取的 counters 段,不能等下一次。
107
91
  const dyn = [];
108
92
  for (const [name, d] of this.dynamic) {
109
93
  const v = this.evalDynamic(name, d.fn);
@@ -136,10 +120,7 @@ export class Metrics {
136
120
  }
137
121
  return out.join("\n") + "\n";
138
122
  }
139
- /** Structured snapshot of the whole registry — the OTLP exporter maps this to OTel data points
140
- * (counters→Sum monotonic, gauges/dynamic→Gauge, histograms→Histogram). Read-only copy. */
141
123
  snapshot() {
142
- // dynamic 先求值(2-j,同 render):错帧的 inc 要进本次快照的 counters。
143
124
  const dynVals = [...this.dynamic].map(([name, d]) => ({ name, help: d.help, v: this.evalDynamic(name, d.fn) }));
144
125
  const counters = [...this.counters].map(([name, c]) => ({
145
126
  name,
@@ -151,7 +132,6 @@ export class Metrics {
151
132
  help: g.help,
152
133
  series: [...g.series.values()].map((s) => ({ labels: s.labels, value: s.value })),
153
134
  }));
154
- // 错帧 series 空:名字/HELP 仍披露(指标存在),但绝不铸 {value:0} 假数据点。
155
135
  for (const { name, help, v } of dynVals)
156
136
  gauges.push({ name, help, series: v === undefined ? [] : [{ labels: {}, value: v }] });
157
137
  const histograms = [...this.histos].map(([name, h]) => ({
@@ -162,9 +142,6 @@ export class Metrics {
162
142
  }));
163
143
  return { counters, gauges, histograms };
164
144
  }
165
- /** Curated health summary (friendly JSON) for a lightweight fleet dashboard — the sema-registry's
166
- * fleet page fetches this per worker and renders cards + computes rates over polls (no Prometheus
167
- * needed). Counters are cumulative totals; histograms are reported as averages (sum/count). */
168
145
  summarize() {
169
146
  const ctotal = (name) => [...(this.counters.get(name)?.series.values() ?? [])].reduce((a, s) => a + s.value, 0);
170
147
  const cby = (name, label) => {
@@ -186,9 +163,6 @@ export class Metrics {
186
163
  tokensTotal: ctotal("task_tokens_total"),
187
164
  costUsd: ctotal("model_cost_micro_usd_total") / 1e6,
188
165
  costUsdByModel: Object.fromEntries(Object.entries(cby("model_cost_micro_usd_total", "model")).map(([k, v]) => [k, v / 1e6])),
189
- // D5([2122] 挂账,additive v1):unpriced 部署下 costUsd 恒 0 而消费端读成「零花费」。
190
- // costUnknown=true ⇒ costUsd/costUsdByModel 是**下界**(RB-368 缺席=未知语义;显式 0 = declared
191
- // free 不计入 unpriced)。计数点在 budget.ts 的 brain.call 臂。
192
166
  costUnknown: ctotal("brain_calls_unpriced_total") > 0,
193
167
  unpricedCalls: ctotal("brain_calls_unpriced_total"),
194
168
  taskDurationAvgSec: havg("task_duration_seconds"),
@@ -215,7 +189,6 @@ export class Metrics {
215
189
  };
216
190
  }
217
191
  }
218
- /** Register the service's standard metric set. */
219
192
  export function createMetrics() {
220
193
  const m = new Metrics();
221
194
  m.counter("metric_gauge_eval_errors_total", "Dynamic gauge eval errors by gauge name (an erroring gauge emits NO sample that scrape — absence means unknown, not 0)");
@@ -228,20 +201,10 @@ export function createMetrics() {
228
201
  m.gauge("runs_active", "Async runs currently executing");
229
202
  m.counter("council_runs_total", "Code-review council runs (L1+L3)");
230
203
  m.histogram("council_tokens", "Total model tokens per council review (lenses + arbiter)");
231
- // Prefix-cache effectiveness (core 1.22): per-task hit rate (cost-critical) + a count of tasks the
232
- // Runner flagged as low-hit (unstable/poisoned prefix) so it can be alerted on. The observed value is
233
- // `result.stats.cacheHitRate` — core-computed (core 3.0.0: cachedTokens/totalInputTokens, the normalized
234
- // total-including-cache denominator; runs.ts / http/server.ts just forward it), not derived here — this
235
- // description names the field, not a formula this file owns (E3: don't restate a formula whose owner
236
- // can change it without telling us; core 3.0.0's denominator switched from `promptTokens` to
237
- // `totalInputTokens`, which the old "(cachedTokens/promptTokens)" text would have silently mis-described).
238
204
  m.histogram("task_cache_hit_rate", "Prefix-cache hit rate per task (core-computed stats.cacheHitRate, 0..1)", [
239
205
  0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 1,
240
206
  ]);
241
207
  m.counter("prompt_cache_low_hit_total", "Tasks the Runner flagged with a low prefix-cache hit rate");
242
- // Cost & trace observability (core 1.37). Spend = the integer micro-USD `costMicroUsd` summed over
243
- // ALL brain calls (top-level + async + council sub-tasks) via the tracer — the authoritative billing
244
- // counter. firstTokenMs is the gateway-hang early signal; tool calls + budget cutoffs round it out.
245
208
  m.counter("model_cost_micro_usd_total", "Authoritative model spend (integer micro-USD) over all brain calls, by model");
246
209
  m.histogram("brain_first_token_ms", "Time to first content token per brain call (gateway-hang signal)", [
247
210
  50, 100, 250, 500, 1000, 2000, 5000, 10000, 30000,
@@ -253,106 +216,50 @@ export function createMetrics() {
253
216
  m.counter("budget_exceeded_total", "Tasks failed by a budget/limit ceiling, by errorCode");
254
217
  m.counter("cost_quota_rejected_total", "Tasks refused because the principal is over its cumulative cost quota");
255
218
  m.counter("degraded_total", "Tasks degraded to a cheaper model (1.40), by reason (budget/rate_limit/breaker_open)");
256
- // #233 / A-002.8:名单外的模型选择在 **resume 腿**被放行(fresh 提交是 400)。读法:非零 = 还有存量
257
- // 会话跑在 @-mention 名单之外 —— 那是刻意的(砖死 resume 比降级糟),但治理面必须看得见它有多少。
258
219
  m.counter("task_model_not_allowlisted_resume_total", "Resumed legs whose model/compactionModel is outside the @-mention allowlist (allowed to keep running by design — the allowlist gates fresh picks only)");
259
220
  m.counter("prompt_constitution_total", "Prompt assembly constitution mode per task (core 1.243 提示词主权): steady state = core; provider-assembled/replaced/legacy warrant eyes");
260
221
  m.counter("rewind_snapshot_failed_total", "Per-turn rewind file snapshots that failed, by code (too_large = big working tree, expected/benign; other codes warrant eyes)");
261
222
  m.counter("verifications_total", "Tasks run through the verification gate (1.44), by final verdict");
262
223
  m.counter("cascade_total", "Tasks run through the model cascade (1.45), by outcome (passed/exhausted)");
263
- // Degenerate-repetition salvage classifier (core 1.59): on errorCode=output.degenerate, where was the
264
- // useful answer? a=in the degenerate turn itself, b=in an earlier turn, unknown=nowhere. Frequencies
265
- // feed back to core's decision on whether the "last-substantive-turn" salvage (②) is worth building.
266
224
  m.counter("degenerate_total", "Degenerate-output failures (1.59), by salvage class (a/b/unknown)");
267
- // Plan-cache recurrence probe (INSTRUMENT-FIRST, core design/42): does a tenant re-pose near-duplicate
268
- // task objectives? Fleet-level totals; per-scope detail is at GET /metrics/plan-cache (not a label, to
269
- // avoid high tenant cardinality). Decides whether core builds the plan-caching producer hook.
270
225
  m.counter("plan_cache_recurrence_total", "Finished tasks whose objective recurs vs scope history, by hit (true/false)");
271
226
  m.histogram("plan_cache_qsig", "Significant-token count of task objectives (qSig length)", [1, 2, 3, 5, 10, 20]);
272
- // Memory consolidation (1.62 B-full): the reconcile-LLM store writes, by op. A spike in update/delete is
273
- // the prod signal for over-merging / a mis-calibrated band. Recorded by the store on each call, so it does
274
- // NOT need the fire-and-forget consolidation() to be awaited.
275
227
  m.counter("memory_consolidation_ops_total", "Memory consolidation store ops (1.62 B-full), by op (update/delete)");
276
228
  m.counter("mcp_server_unavailable_total", "MCP servers skipped fail-open at task start (1.68), unreachable/misconfigured");
277
- // DESIGN-269 车1:A2A peer 在任务起手不可用(卡取不到 / skill 铸不出)—— 与 mcp 同族的 fail-open 计数。
278
229
  m.counter("a2a_peer_unavailable_total", "A2A peers skipped at task start (DESIGN-269 client leg): card unreachable / spec unusable");
279
- // DESIGN-269 车2:server-as-peer 的**对外**面。两只计数器刻意分开,它们回答两个不同的问题:
280
- // · `a2a_serve_rpc_total{method,outcome}` = 每个方法各被调了多少次、结果是什么(ok / refused /
281
- // error_<code> / method_not_found / internal_error)。`method_not_found` 那一格是**路线图信号**:
282
- // 外部 peer 在真实拓扑里到底在要哪个还没做的方法(message/stream?tasks/cancel?),比我们猜准。
283
- // · `a2a_serve_rejected_total{reason}` = **没能进到方法层**的那些(凭据/形/版本以外的门),
284
- // 它是「有人在扫我们 / 有人接错了」的第一手材料。混进上面那只会让「协议用错」与「跑失败了」
285
- // 在同一条曲线上不可分。
286
230
  m.counter("a2a_serve_rpc_total", "A2A server-as-peer JSON-RPC calls by method and outcome (DESIGN-269 车2)");
287
231
  m.counter("a2a_serve_rejected_total", "A2A server-as-peer requests refused before reaching a method handler, by reason (DESIGN-269 车2)");
288
- // Remote execution env (design/48 E2B adapter): lifecycle outcomes by provider/result.
289
232
  m.counter("remote_env_connect_total", "E2B remote-env connect outcomes (provider/result)");
290
233
  m.counter("remote_env_destroy_total", "E2B remote-env destroy outcomes (provider/result)");
291
234
  m.counter("remote_env_suspend_total", "E2B remote-env suspendVM outcomes (v1.5, provider/result)");
292
235
  m.counter("remote_env_resume_total", "E2B remote-env resumeVM outcomes (v1.5, provider/result)");
293
236
  m.counter("remote_env_exec_stream_read_timeout_total", "E2B execStream idle read-timeouts (#1128, provider)");
294
- // E18 resume-at: a per-turn anchor capture (getLeafId→put) failed (e.g. an un-migrated DB missing resume_anchor) —
295
- // best-effort, never fails the run; this surfaces a SYSTEMATIC capture outage instead of a silent feature stoppage.
296
237
  m.counter("resume_anchor_capture_failed", "E18 resume-at per-turn anchor captures that failed (best-effort, run unaffected)");
297
- // Silent-fallback telemetry P0 batch (sema-internal server/docs/design/SILENT-FALLBACK-TELEMETRY.md, service half):
298
- // S4 — brain-layer liveness phases (rate_limited/retrying/reconnecting/circuit_open) were previously visible
299
- // only on the live SSE stream; count them so upstream throttling/outage is graphable per phase.
300
238
  m.counter("brain_retry_total", "Brain-layer status events (S4), by phase (rate_limited/retrying/reconnecting/circuit_open)");
301
- // S5 — the auto store-backend probe degraded this replica to in-memory at boot (configured DB unreachable).
302
- // 1 = degraded (this replica is NOT persisting); scrape-level alert target. /health carries the twin flag.
303
239
  m.gauge("store_backend_degraded", "1 when the auto DB probe failed at boot and this replica degraded to in-memory (S5)");
304
- // S6 — images submitted to a model whose declared input has no "image": core degrades them to text
305
- // placeholders (1.233.1). Counted at submit so the silent modality drop is visible per model.
306
240
  m.counter("images_omitted_total", "Tasks whose images will be degraded to text placeholders (no-vision model), by model (S6)");
307
- // S7 — background reapers: how many rows each sweep actually flipped (previously discarded), by kind.
308
- // A spike = an instance died and its orphans were batch-failed (distinguishable from organic failures).
309
241
  m.counter("runs_reaped_total", "Run rows flipped by the background reapers (S7), by kind (stale/suspended/expired_checkpoint)");
310
242
  m.counter("checkpoints_reaped_total", "Durable checkpoints expired by the backstop reaper (S7)");
311
243
  m.counter("bakes_reaped_total", "Image bakes failed by the stale-lease reaper (S7)");
312
- // S9 — cross-replica write-behind counters (rate limit / cost quota): flush/refresh failures previously
313
- // vanished in a bare catch → the fleet silently degrades to per-replica counting. Streak gauge = current
314
- // consecutive failures (0 = healthy); the total counts every failed attempt.
315
244
  m.counter("counter_flush_failed_total", "Write-behind counter flush/refresh failures (S9), by table and kind");
316
245
  m.gauge("counter_flush_fail_streak", "Current consecutive write-behind flush/refresh failures (S9), by table and kind");
317
- // S14 — near-budget degrade was configured but DROPPED for this task (e.g. image task + text-only degrade
318
- // target): the task keeps its main model and will hard-fail on cost instead. Previously undetectable.
319
246
  m.counter("degrade_dropped_total", "Tasks whose near-budget degrade was dropped at submit (S14), by reason");
320
- // Silent-fallback P1 batch (second sweep, same doc §P1):
321
- // S18 — every brain call by provider+model; provider="unknown" = the routing layer fell through to the
322
- // gateway default without a recognized provider (previously invisible).
323
247
  m.counter("brain_calls_total", "Brain calls by provider/model (S18); provider=unknown = unrecognized routing fallthrough");
324
- // S10 — DB wall-clock minus local wall-clock (ms, can be negative). The write-behind counters bucket on
325
- // LOCAL floor(now/windowMs); replica clock skew splits a fleet window into disjoint buckets (soft-limit leak).
326
248
  m.gauge("fleet_counter_bucket_skew_ms", "DB clock minus local clock in ms (S10) — skew splits cross-replica counter windows");
327
249
  m.gauge("fleet_clock_probe_ok", "1 = last DB clock probe succeeded, 0 = failing (skew gauge is stale while 0)");
328
- // #96 redesign⑤ (§DESIGN-V2 E): LSP sandbox-bridge lifecycle + session outcomes — previously zero metrics
329
- // existed for the whole subsystem (a dead bridge or a stuck restart budget had no fleet-level signal).
330
250
  m.counter("lsp_bridge_start_total", "LSP sandbox bridge (re)starts attempted, by provider and generation");
331
251
  m.counter("lsp_bridge_dead_total", "LSP sandbox bridge generations that died, by reason (the word IS the model-facing unavailableReason)");
332
252
  m.counter("lsp_session_open_total", "LSP session opens, by language and outcome (ok/failed)");
333
253
  m.counter("lsp_session_heal_total", "LSP session heals (dead-transport reopen), by outcome (ok/failed)");
334
- // S21 — which backend snapshot blobs actually land on (minio|sql). Partial MINIO_* config silently falls to sql.
335
254
  m.gauge("snapshot_blob_backend", "1 on the active snapshot-blob backend series (S21), by backend (minio|sql)");
336
- // S22 — memory embed/KNN runtime failures (boot said vector; runtime silently degrades to lexical).
337
- // HELP 文案按**唯一活发射点**写(#228 接线后的复扫):stores.ts 的 onFailure 腿打 `{ backend: dialect }`,
338
- // 从没有过 `where` 维度(那是退役的 MEMORY_BACKEND/EMBEDDING_* store 面的形)—— 运维照旧文案写
339
- // `sum by (where)` 得到的是空序列。USAGE 的 `memory_embed_failed_total{backend}` 才是对的那一份。
340
255
  m.counter("memory_embed_failed_total", "Memory embedding calls that failed at runtime (S22), by backend (the memory-engine dialect that owns the embedder)");
341
256
  m.counter("memory_knn_query_failed_total", "Memory KNN searchScored queries that failed at runtime (S22)");
342
- // LOW — dedup fold vs new insert (a fold UPDATEs the existing row; previously indistinguishable), and the
343
- // dedup probe failing over to a plain INSERT.
344
257
  m.counter("memory_dedup_folded_total", "Memory writes folded into an existing near-duplicate row (LOW)");
345
258
  m.counter("memory_dedup_probe_failed_total", "Memory dedup KNN probes that failed and fell through to INSERT (LOW)");
346
- // S24 — redaction rewrites by pattern (over/under-redaction previously had zero fingerprint).
347
259
  m.counter("redactions_applied_total", "redactSecrets rewrites (S24), by pattern label");
348
- // S25 — warm session-cache entries evicted by a cross-instance write (import/replace) while still cached.
349
260
  m.counter("stale_cache_conflict_total", "Warm session-cache entries evicted by a cross-instance write while cached (S25)");
350
- // LOW — cross-replica breaker write-through failures (advisory swallow previously silent).
351
261
  m.counter("breaker_writethrough_failed_total", "Circuit-breaker cross-replica write-through failures (LOW), by backend");
352
- // LOW — web-search provider returned a non-array payload (model silently gets zero results).
353
262
  m.counter("web_search_bad_payload_total", "Web-search provider responses whose results field was not an array (LOW), by provider");
354
- // 收账批(2026-07-08):这 15 个计数器一直只有 inc 没注册 — 在 auto-register 落地前
355
- // 整批从未上过 /metrics(inc 曾对未注册名静默 no-op)。补显式注册拿正经 HELP 文案。
356
263
  m.counter("permission_denied_total", "Tool-gate denials by source (core's PermissionDeniedSource, incl. org governance; 'other' = a word this build does not know; goal B4 always-on deny meter)");
357
264
  m.counter("permission_rule_events_total", "Persisted allow-rule seam events by event (persisted_rule_allowed = a rule authorized a call with nobody asked; rule_store_unreadable = the store failed to read so the call was adjudicated with ZERO rules)");
358
265
  m.counter("compaction_events_total", "Compaction lifecycle events by outcome (started/completed/failed/skipped) and trigger");
@@ -369,7 +276,6 @@ export function createMetrics() {
369
276
  m.counter("remote_env_rpc_timeout_total", "Remote-env RPC timeouts, by provider");
370
277
  m.counter("task_outcomes_total", "Task outcome-ledger rows recorded, by status and oracle green flag");
371
278
  m.counter("workflow_runs_total", "Self-orchestration workflow runs, by status");
372
- // 鉴权桥 verify 延迟:热路径(kid 命中)亚毫秒;冷路径(JWKS 拉取)带网络 RTT——首请求/轮换尾延迟可见。
373
279
  m.histogram("auth_bridge_verify_seconds", "Registry SSO JWT verify latency (hot path sub-ms; cold path includes a JWKS fetch)", [0.001, 0.005, 0.02, 0.1, 0.5, 2, 10]);
374
280
  return m;
375
281
  }
@@ -1,9 +1,7 @@
1
1
  import { recordFailOpen } from "./fail-open.js";
2
- const CUMULATIVE = 2; // OTel AggregationTemporality.CUMULATIVE
3
- /** uint64 nanos as a string (OTLP/JSON encodes 64-bit ints as strings; ms×1e6 overflows Number). */
2
+ const CUMULATIVE = 2;
4
3
  const nano = (ms) => (BigInt(ms) * 1000000n).toString();
5
4
  const attrs = (labels) => Object.entries(labels).map(([key, v]) => ({ key, value: { stringValue: String(v) } }));
6
- /** Build the OTLP/JSON metrics payload from a registry snapshot. Pure → unit-testable. */
7
5
  export function buildOtlpPayload(snap, opts) {
8
6
  const start = nano(opts.startMs);
9
7
  const now = nano(opts.nowMs);
@@ -38,15 +36,13 @@ export function buildOtlpPayload(snap, opts) {
38
36
  description: h.help,
39
37
  histogram: {
40
38
  dataPoints: h.series.map((s) => {
41
- // our buckets are CUMULATIVE (Prometheus-style: counts[i] = #obs ≤ bounds[i]); OTLP wants
42
- // PER-bucket counts with a trailing +Inf overflow bucket (length = bounds.length + 1).
43
39
  const bucketCounts = [];
44
40
  let prev = 0;
45
41
  for (let i = 0; i < h.buckets.length; i++) {
46
42
  bucketCounts.push(String(s.counts[i] - prev));
47
43
  prev = s.counts[i];
48
44
  }
49
- bucketCounts.push(String(s.count - prev)); // +Inf
45
+ bucketCounts.push(String(s.count - prev));
50
46
  return { attributes: attrs(s.labels), startTimeUnixNano: start, timeUnixNano: now, count: String(s.count), sum: s.sum, bucketCounts, explicitBounds: h.buckets };
51
47
  }),
52
48
  aggregationTemporality: CUMULATIVE,
@@ -62,14 +58,10 @@ export function buildOtlpPayload(snap, opts) {
62
58
  ],
63
59
  };
64
60
  }
65
- /** Start the periodic push loop (unref'd so it never holds the process open). Returns a stop handle. */
66
61
  export function startOtlpExporter(metrics, opts) {
67
62
  const startMs = Date.now();
68
63
  const url = `${opts.endpoint}/v1/metrics`;
69
64
  const fetchImpl = opts.fetchImpl ?? fetch;
70
- // A collector that accepts the connection but never responds would otherwise let one fetch per tick pile
71
- // up (each holding a full snapshot payload + socket) — bound each request and skip a tick while one is
72
- // still in flight, so a half-dead collector can never accumulate back-pressure inside the process.
73
65
  let inFlight = false;
74
66
  const exportOnce = async () => {
75
67
  if (inFlight)
@@ -83,8 +75,6 @@ export function startOtlpExporter(metrics, opts) {
83
75
  body: JSON.stringify(payload),
84
76
  signal: AbortSignal.timeout(Math.max(1_000, opts.intervalMs)),
85
77
  });
86
- // #193 车5(P1-3):两条失败臂都要过 fail-open 计数——onError 只到日志轴,而 Prometheus-only
87
- // 部署只看 metrics,此前 collector 持续宕机在 metrics 轴零信号(遥测腿自盲)。
88
78
  if (!res.ok) {
89
79
  recordFailOpen("server.otel.export-failed", `http_${res.status}`);
90
80
  opts.onError?.(new Error(`OTLP export HTTP ${res.status}`));
@@ -92,7 +82,7 @@ export function startOtlpExporter(metrics, opts) {
92
82
  }
93
83
  catch (err) {
94
84
  recordFailOpen("server.otel.export-failed", String(err));
95
- opts.onError?.(err); // export is best-effort — a collector hiccup must never affect serving
85
+ opts.onError?.(err);
96
86
  }
97
87
  finally {
98
88
  inFlight = false;
@@ -1,17 +1,8 @@
1
- /**
2
- * Per-principal execution context (AsyncLocalStorage). The brain-call tracer fires for EVERY model
3
- * call — including council/team sub-tasks that run on the shared `subRunner` (a per-task tracer can't
4
- * reach those). To attribute spend to the right principal we wrap a task's execution in this ALS:
5
- * `node:async_hooks` propagates the store through every `await`, so a sub-task spawned inside a tool
6
- * call still reads the originating principal. Absent store = unattributed (dev / no principal).
7
- */
8
1
  import { AsyncLocalStorage } from "node:async_hooks";
9
2
  export const principalContext = new AsyncLocalStorage();
10
- /** Run `fn` with `principal` as the ambient context (no-op wrapper when principal is undefined). */
11
3
  export function withPrincipal(principal, fn) {
12
4
  return principal ? principalContext.run(principal, fn) : fn();
13
5
  }
14
- /** The principal attributed to the current async execution, if any. */
15
6
  export function currentPrincipal() {
16
7
  return principalContext.getStore();
17
8
  }
@@ -1,9 +1,3 @@
1
- // design/158 N18: the prompt-manifest / config-assembled observability face moved VERBATIM out of budget.ts
2
- // (lens4 §4.1 — budget.ts's name says cost/budget, but this half is wire-observability projection of core's
3
- // `prompt.assembled`/`config.assembled` trace events, unrelated to spend). budget.ts re-exports all four for
4
- // compat (its own createTracer still consumes promptManifestRecordOf/configAssembledRecordOf/PromptManifestTracker).
5
- /** Whitelist one `prompt.assembled` trace event into a {@link PromptManifestRecord} (v1 face required,
6
- * v2 `sections`/`tools` additive — absent stays absent). */
7
1
  export function promptManifestRecordOf(e) {
8
2
  return {
9
3
  constitution: String(e.constitution),
@@ -14,8 +8,6 @@ export function promptManifestRecordOf(e) {
14
8
  ...(Array.isArray(e.tools)
15
9
  ? { tools: e.tools.map((t) => ({ wireName: String(t.wireName), aliases: (Array.isArray(t.aliases) ? t.aliases : []).map(String), contractId: String(t.contractId), implementationRevision: String(t.implementationRevision), cardId: String(t.cardId), shapeDigest: String(t.shapeDigest), wireSchemaDigest: String(t.wireSchemaDigest) })) }
16
10
  : {}),
17
- // toolDisclosure(core 5.15.0 #181,[2872] 提货②):三键逐挑——数值有限、两词字符串非空才铸;
18
- // 畸形按缺席(不铸假值,与 sections/tools 的 additive 同规)。
19
11
  ...(typeof e.toolDisclosure === "object" && e.toolDisclosure !== null &&
20
12
  typeof e.toolDisclosure.deferredTools === "number" && Number.isFinite(e.toolDisclosure.deferredTools) &&
21
13
  typeof e.toolDisclosure.strategy === "string" && e.toolDisclosure.strategy.length > 0 &&
@@ -25,23 +17,6 @@ export function promptManifestRecordOf(e) {
25
17
  totalChars: e.totalChars,
26
18
  };
27
19
  }
28
- /**
29
- * [998]② per-task prompt-manifest accumulator — the `ModelUsageTracker`(budget.ts)pattern: the
30
- * `prompt.assembled` tracer hook is SYNC/no-IO, so it cannot persist; it RECORDS here (registered
31
- * top-level runs only — the same leak fence: sub-task manifests are never registered, never leak), and
32
- * the durable run lifecycle DRAINS the pending records into `prompt_assembled` events. Two departures
33
- * from the ModelUsage shape (release-review findings):
34
- * - The pending value is an ordered QUEUE, not a single slot: a cascade/verify leg re-prepares under
35
- * ONE taskId (core's runCascade passes the same spec taskId to every rung), and each prepare's
36
- * manifest must persist — latest-wins would hide the prompt actually used by earlier billed rungs.
37
- * - {@link register} returns a per-leg LEASE and {@link clear} requires it: after setSuspended makes
38
- * the row claimable, a fast sibling resume can re-register the same taskId before the parked leg's
39
- * finally runs (the codebase's documented B-3 window) — an unconditional clear there would silently
40
- * delete the NEW leg's registration/pending manifest. Identity-guarded like the sibling
41
- * inflight/steerable teardowns.
42
- */
43
- /** [1301]③ — `config.assembled` 的白名单投影(core 1.346 trace:catalogVersion/fields[来源标注]/
44
- * overrideReasons)。fields 已是 core 精选的观测形(digest-free、有界);32KiB 罩防未来膨胀。 */
45
20
  export function configAssembledRecordOf(e) {
46
21
  const rec = {
47
22
  catalogVersion: e.catalogVersion,
@@ -51,7 +26,7 @@ export function configAssembledRecordOf(e) {
51
26
  };
52
27
  try {
53
28
  if (JSON.stringify(rec).length > 32_768)
54
- return undefined; // bounded frame — oversize dropped, never truncated半形
29
+ return undefined;
55
30
  }
56
31
  catch {
57
32
  return undefined;
@@ -60,14 +35,9 @@ export function configAssembledRecordOf(e) {
60
35
  }
61
36
  export class PromptManifestTracker {
62
37
  byTask = new Map();
63
- /** [1301]③:config.assembled 的同生命周期伴随记录(register 同门、drainConfig 同拍)。队列形
64
- * (codex F2):cascade 每 rung 独立解析配置,后到覆盖会把先前已计费 rung 的生效配置抹掉——与
65
- * prompt manifests 同姿势逐条保序。 */
66
38
  configByTask = new Map();
67
39
  active = new Map();
68
40
  leaseCounter = 0;
69
- /** Mark a TOP-LEVEL run active (same fence as `ModelUsageTracker.register`, budget.ts). Returns this leg's
70
- * lease — pass it to {@link clear} so only the CURRENT owner can tear the slot down. */
71
41
  register(taskId) {
72
42
  if (!taskId)
73
43
  return 0;
@@ -75,7 +45,6 @@ export class PromptManifestTracker {
75
45
  this.active.set(taskId, lease);
76
46
  return lease;
77
47
  }
78
- /** Queue this prepare's manifest — ONLY for a registered top-level run (else a no-op). Sync, never throws. */
79
48
  record(taskId, m) {
80
49
  if (!taskId || !this.active.has(taskId))
81
50
  return;
@@ -85,7 +54,6 @@ export class PromptManifestTracker {
85
54
  else
86
55
  this.byTask.set(taskId, [m]);
87
56
  }
88
- /** [1301]③:队一条 config.assembled 投影(同 register 门;逐条保序——cascade 每 rung 各留其形)。 */
89
57
  recordConfig(taskId, rec) {
90
58
  if (!taskId || !this.active.has(taskId))
91
59
  return;
@@ -95,7 +63,6 @@ export class PromptManifestTracker {
95
63
  else
96
64
  this.configByTask.set(taskId, [rec]);
97
65
  }
98
- /** Return-and-clear ALL pending config records in emission order; `undefined` when none. */
99
66
  drainConfig(taskId) {
100
67
  const q = this.configByTask.get(taskId);
101
68
  if (!q || q.length === 0)
@@ -103,7 +70,6 @@ export class PromptManifestTracker {
103
70
  this.configByTask.delete(taskId);
104
71
  return q;
105
72
  }
106
- /** Return-and-clear ALL pending manifests in emission order; `undefined` when none. */
107
73
  drain(taskId) {
108
74
  const q = this.byTask.get(taskId);
109
75
  if (!q || q.length === 0)
@@ -111,8 +77,6 @@ export class PromptManifestTracker {
111
77
  this.byTask.delete(taskId);
112
78
  return q;
113
79
  }
114
- /** Drop pending + unregister (run end / abandoned leg) — but ONLY while `lease` still owns the slot;
115
- * a stale leg's teardown after a fast re-register is a no-op (the new leg owns it now). */
116
80
  clear(taskId, lease) {
117
81
  if (this.active.get(taskId) !== lease)
118
82
  return;
@@ -1,15 +1,12 @@
1
1
  export class RateLimiter {
2
2
  windowMs;
3
3
  windows = new Map();
4
- /** 🔴 不再 `readonly`:限额是**比较参数**,不是窗表状态 —— 换代只动它,窗内已计的数一个不动(§5)。 */
5
4
  limit;
6
5
  constructor(limit, windowMs = 60_000) {
7
6
  this.windowMs = windowMs;
8
7
  this.limit = limit;
9
8
  }
10
9
  check(key) {
11
- // 关断哨兵:零比较、零窗表写入。off 期间不攒计数是刻意的——0→N 打开时应该是一个完整的新窗口,
12
- // 而不是「上来就超」(那段时间根本没有 ceiling,补记账等于追溯执法)。
13
10
  if (this.limit <= 0)
14
11
  return { allowed: true, retryAfterSec: 0, remaining: Number.POSITIVE_INFINITY };
15
12
  const now = Date.now();
@@ -27,7 +24,6 @@ export class RateLimiter {
27
24
  setLimits(next) {
28
25
  this.limit = next.rateLimitPerMin;
29
26
  }
30
- /** Drop expired windows (call periodically). */
31
27
  sweep(now = Date.now()) {
32
28
  for (const [k, w] of this.windows)
33
29
  if (w.resetAt <= now)
@@ -1,53 +1,18 @@
1
- /**
2
- * A-033.3(#256)—— **密钥形 env 剥除的留痕面**(core D1 `scrubSecretEnv` 的可观测半场)。
3
- *
4
- * ## 它解决的问题
5
- * `scrubSecretEnv` 是本仓两条 shell/子进程腿的**安全默认**:模型可驱动的 shell(`inheritEnv:"scrub"`)与
6
- * git 子进程(钩子/凭证助手)拿到的 env 里,`*_KEY`/`*_TOKEN`/`*_SECRET`/`*_PASSWORD`… 一律先丢掉。
7
- * 两处都以**单参形**调用,于是「这一次到底丢了哪些 key、按哪条规则(suffix-rule / exact-name)、置信度多少」
8
- * **零留痕** —— 部署方既证明不了保护生效过,也发现不了它悄悄不生效了(词表漂移 / 一个新的密钥命名法从
9
- * 规则的缝里穿过去 / 某条腿被改成 `inheritEnv:"all"`)。core 早就把收集口开好了(`findings?:
10
- * SecretEnvFinding[]`,d.ts 原话:the VALUE is never carried — this record is meant to be loggable),
11
- * 缺的只是本仓这半场。本模块就是那半场,**不是**一个新的执法面:它一个字节也不改变剥除行为。
12
- *
13
- * ## 形(照 `fail-open.ts` 的仓内先例)
14
- * 1. **计数(逐次)** —— `secret_env_scrubbed_total{site,kind,confidence}`;
15
- * 2. **结构化披露(每 site×key 一次)** —— 首次在某条腿上丢掉某个 key 时一条 `info`,之后同 key 只走计数。
16
- * 不是「每 site 一次」:一个**新**的密钥形 key 后来才出现(部署加了一个凭证)时,那才是最该看见的一行。
17
- * 值永不进任何一件(core 的 finding 本身就不携带值)。
18
- *
19
- * ## 为什么是进程级自由函数而不是注入
20
- * 与 `recordFailOpen` 同因:两个调用点都在**没有 logger/metrics 句柄**的深处
21
- * (`plugins/remote-env-host.ts` 的 `mergeEnv` 全文零观测通道;`project-memory.ts` 的 `git()` 是个自由函数)。
22
- * 装配层用 {@link installSecretEnvScrubRecorder} 把句柄接上,装配之前命中的次数不会丢(计数照常累加,
23
- * install 时一次性补进 metrics),披露行则在下一次命中时补发(未成功披露的 key 不进 `disclosed` 集)。
24
- */
25
1
  import { summarizeRedactions } from "@sema-agent/core";
26
- /** 登记过的剥除现场。闭集:未登记的 site 传不进 {@link recordSecretEnvScrub}(编译期拒,同 FAIL_OPEN_TAGS 纪律)。 */
27
2
  export const SECRET_ENV_SCRUB_SITES = {
28
3
  "server.remote-env-host.inherit-scrub": "模型可驱动的 host shell 继承 env(`inheritEnv:\"scrub\"` 默认腿)——这条腿上的一次泄漏等于把编排器的脑钥/仓库令牌交给模型选的命令",
29
4
  "server.project-memory.git-subprocess": "project-memory 读仓叙事时的 git 子进程 env —— 仓内的钩子/凭证助手会以本进程身份运行",
30
5
  };
31
- /** 计数器名。 */
32
6
  const SCRUB_COUNTER = "secret_env_scrubbed_total";
33
- /** 已披露集的上限。key 集在一个进程里实际上是常量(恒扫同一份 `process.env`),这个帽只是防病态输入
34
- * 把集合撑爆;越帽之后只停止**新增披露**,计数照常(缺席的是行,不是账)。 */
35
7
  const DISCLOSED_CAP = 512;
36
- /** 桶键与披露键的分隔符。用 `=` 是有据的:POSIX 环境变量名里唯二不可能出现的字节就是 `=` 与 NUL
37
- * (NUL 不写进源码 —— 裸控制字节会让 grep 对整个文件失明,本仓有机械门盯它)。用空格/冒号分隔则会让
38
- * `A B`+`C` 与 `A`+`B C` 撞成同一个键 —— 标签串台 = 遥测说谎。 */
39
8
  const SEP = "=";
40
9
  let sink = {};
41
- /** `site=kind=confidence` → 累计次数(逐次,进程级)。 */
42
10
  let counts = new Map();
43
- /** 已经推进 metrics 的部分 —— 差额即欠账,install 时一次性补。 */
44
11
  let pushed = new Map();
45
- /** `site=key` —— 已经出过披露行的现场×键。 */
46
12
  let disclosed = new Set();
47
13
  function bucketKey(site, f) {
48
14
  return `${site}${SEP}${f.kind}${SEP}${f.confidence}`;
49
15
  }
50
- /** 把 counts 与 pushed 的差额推进 metrics。metrics 缺席时什么也不做(差额留着,下次补)。 */
51
16
  function drainToMetrics() {
52
17
  const m = sink.metrics;
53
18
  if (m === undefined)
@@ -59,17 +24,12 @@ function drainToMetrics() {
59
24
  const [site, kind, confidence] = k.split(SEP);
60
25
  try {
61
26
  m.inc(SCRUB_COUNTER, { site: site, kind: kind, confidence: confidence }, n - already);
62
- pushed.set(k, n); // 真进了汇才销账(投递失败 ⇒ 差额留着重放)
27
+ pushed.set(k, n);
63
28
  }
64
29
  catch {
65
- /* 下一次命中/下一次装配再补 */
66
30
  }
67
31
  }
68
32
  }
69
- /**
70
- * 记一次剥除。**总不抛** —— 观测本身绝不能变成故障(同 `recordFailOpen` / reapers 的判据)。
71
- * `findings` 为空(这一次没丢任何 key)时什么也不做:那不是事件,是常态。
72
- */
73
33
  export function recordSecretEnvScrub(site, findings) {
74
34
  if (findings.length === 0)
75
35
  return;
@@ -81,13 +41,7 @@ export function recordSecretEnvScrub(site, findings) {
81
41
  drainToMetrics();
82
42
  const logger = sink.logger;
83
43
  if (logger === undefined)
84
- return; // 披露行留到有 logger 的下一次命中(未进 disclosed ⇒ 会补发)
85
- // 🔴 帽是**对这一次的批也生效**的(codex 复审 round2 medium 真修):先按剩余容量截断,再用截断后的
86
- // 那一批去发行与登记。两条都靠它:
87
- // ① 帽满 ⇒ 停止披露,而不是「喊了但记不下」—— 后者会把「每 site×key 一次」在 key 最多的时候
88
- // 反转成「每次调用一行」(mergeEnv 是逐命令调用的,那是刷屏);
89
- // ② 单批越界 ⇒ 也得截断 —— 只在批**之前**判帽,一次 513 条的病态 env 会一次性把 513 条全发出去
90
- // (一行超大日志)并全部留驻,帽等于没有。计数不受截断影响:账永远是全的,缺席的只是行。
44
+ return;
91
45
  const room = DISCLOSED_CAP - disclosed.size;
92
46
  if (room <= 0)
93
47
  return;
@@ -97,34 +51,26 @@ export function recordSecretEnvScrub(site, findings) {
97
51
  logger.info("secret_env_scrubbed", {
98
52
  site,
99
53
  note: SECRET_ENV_SCRUB_SITES[site],
100
- // 只报**本次新见**的 key(值永不携带 —— core 的 finding 按契约只带键名/规则/置信度/来源)。
101
54
  keys: fresh.map((f) => f.key),
102
- // 一行人读摘要走 core 的 `summarizeRedactions`(SecretEnvFinding 满足 SummarizableFinding),
103
- // 不在本仓另写一份分组渲染 —— 那正是本票要消灭的那种孪生。
104
55
  summary: summarizeRedactions(fresh),
105
56
  });
106
57
  for (const f of fresh)
107
58
  disclosed.add(`${site}${SEP}${f.key}`);
108
59
  }
109
60
  catch {
110
- /* 观测绝不抛 */
111
61
  }
112
62
  }
113
- /** 装配层把 logger/metrics 接上(main.ts,与 `installFailOpenRecorder` 同一处)。装配前累计的次数在此补进 metrics。 */
114
63
  export function installSecretEnvScrubRecorder(deps) {
115
64
  sink = { ...deps };
116
65
  deps.metrics?.counter(SCRUB_COUNTER, "secret-pattern env entries dropped before spawning a child process, by site/rule/confidence (core scrubSecretEnv findings)");
117
66
  drainToMetrics();
118
67
  }
119
- /** 已留驻的「现场×键」条数(测试/诊断读面)。恒 ≤ {@link DISCLOSED_CAP}。 */
120
68
  export function secretEnvScrubDisclosedSize() {
121
69
  return disclosed.size;
122
70
  }
123
- /** 本进程各桶的累计次数(测试/诊断读面)。 */
124
71
  export function secretEnvScrubCounts() {
125
72
  return new Map(counts);
126
73
  }
127
- /** 测试用重置(进程级状态在同一个 vitest worker 里跨用例存活)。 */
128
74
  export function resetSecretEnvScrubRecorderForTest(deps = {}) {
129
75
  counts = new Map();
130
76
  pushed = new Map();