@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,24 +1,13 @@
1
- /**
2
- * 把 `onClose` 接到这条连接的两个关闭信号上,并保证**至多执行一次**。
3
- *
4
- * 连接在调用本函数**之前**就已经断了(接线排在慢查询之后的那种形),`onClose` 会在本函数内
5
- * **同步**跑一次 —— 调用方据此可以假定:本函数返回后,「已断连」这件事一定已经被通知过了。
6
- * 于是 `closed` 旗在泵进入循环前就已翻好,不需要每条腿再自己判一次 `res.destroyed`。
7
- *
8
- * @param onClose 本连接的全部清理动作(置 `closed` 旗、`clearInterval`、迭代器 `return()` 等)。
9
- */
10
1
  export function bindSseLifecycle(req, res, onClose) {
11
2
  let fired = false;
12
3
  const once = () => {
13
4
  if (fired)
14
5
  return;
15
- fired = true; // 先落闸再调用:onClose 抛出时不得从第二个事件重入(见顶注)
6
+ fired = true;
16
7
  onClose();
17
8
  };
18
- res.on("close", once); // 实测恒先到的那一个,也是读干形下唯一到的那一个
9
+ res.on("close", once);
19
10
  req.on("close", once);
20
- // 回看:接线之前就断掉的连接,两个事件都已烧完,只有状态位还留着证据(理由与判据选取见顶注)。
21
- // 先注册再回看,不是反过来 —— 反过来会在「回看与注册之间」新开一个同样的窗。
22
11
  if (res.destroyed || res.closed)
23
12
  once();
24
13
  }
@@ -1,22 +1,6 @@
1
1
  import { bindSseLifecycle } from "./sse-lifecycle.js";
2
2
  import { sendJson, sendError, sseHeaders, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_MS } from "./send.js";
3
- /** #151 车3 §5.3:preamble 的**有界**等待上限。挂起的 store 不许拖住开流——开流是壳的主路径,
4
- * 而 preamble 只是一个锦上添花的对账基准。超时即按「无卡集基准」继续(与今天等同)。 */
5
3
  export const SSE_PREAMBLE_TIMEOUT_MS = 2_000;
6
- /**
7
- * 跑一次 preamble,**有界 + fail-open**(#151 车3 §5.3)。
8
- *
9
- * 🔴 只 catch throw 是不够的:store **挂起**(不抛)会把整条开流一起拖住。所以这里是 `Promise.race`
10
- * 而不是 try/catch —— 两种失败形(抛 / 不返回)各自有一条出路,都落到「零帧 + 一次 warn + 继续开流」。
11
- *
12
- * 🔴 #151 车3 刀 3b(§14.2 遗留记账兑现):超时**同时 abort** 一个交给 `preamble` 的 `AbortSignal`。
13
- * 此前只是「丢结果」,原 promise 仍在飞 —— DB 挂死时的重连风暴会一轮轮往连接池里堆等待者。现在读口
14
- * 拿到 abort 会当场拒绝,调用链不再累积;库那侧的那条查询仍会跑完(驱动无取消 seam,残留边界见
15
- * `ApprovalAskStore.listPendingBySession` 的注)。丢结果这条语义不变:preamble 只读,丢弃零副作用。
16
- *
17
- * 导出给 `routes/tasks.ts` 的 sync 腿直接用 —— 那条腿不走 `streamSseLog`(它不是账本 tail),但
18
- * 「有界 + fail-open」这条判据必须是**同一份实现**,不能各腿各写一遍。
19
- */
20
4
  export async function collectSsePreamble(preamble, onFailure, timeoutMs = SSE_PREAMBLE_TIMEOUT_MS) {
21
5
  const timedOut = Symbol("preamble-timeout");
22
6
  let timer;
@@ -26,10 +10,10 @@ export async function collectSsePreamble(preamble, onFailure, timeoutMs = SSE_PR
26
10
  preamble(ac.signal),
27
11
  new Promise((resolve) => {
28
12
  timer = setTimeout(() => {
29
- ac.abort(); // 先掐读,再回空集——两件事的顺序无关紧要,但都要发生
13
+ ac.abort();
30
14
  resolve(timedOut);
31
15
  }, timeoutMs);
32
- timer.unref?.(); // 别让这只表把进程钉住(测试进程里尤其明显)
16
+ timer.unref?.();
33
17
  }),
34
18
  ]);
35
19
  if (raced === timedOut) {
@@ -47,14 +31,10 @@ export async function collectSsePreamble(preamble, onFailure, timeoutMs = SSE_PR
47
31
  clearTimeout(timer);
48
32
  }
49
33
  }
50
- /** The shared resumable SSE pump (P2.8) — provider supplies the log-specific differences; the liveness/
51
- * heartbeat/416/terminal-re-fetch/15-min-cap logic is identical for task_run + image_bake. */
52
34
  export async function streamSseLog(req, res, provider, id, staleMs) {
53
35
  const lastId = Number(req.headers["last-event-id"] ?? new URL(req.url ?? "", "http://x").searchParams.get("from") ?? 0);
54
36
  let from = Number.isFinite(lastId) ? lastId : 0;
55
37
  if (from > 0) {
56
- // Resume point evicted past retention → 416 so the client re-syncs, instead of a silent incomplete stream
57
- // (parity with streamTaskTrace; council). Inert until retention evicts (retainedFrom stays 1 with no TTL).
58
38
  const retainedFrom = await provider.retainedFrom(id);
59
39
  if (retainedFrom > from + 1) {
60
40
  sendError(res, 416, "limit.retention_evicted", "resume point evicted past retention", { retainedFrom });
@@ -62,26 +42,13 @@ export async function streamSseLog(req, res, provider, id, staleMs) {
62
42
  }
63
43
  }
64
44
  sseHeaders(res);
65
- // 🔴 断连接线必须**先于** preamble 的等待窗接上(codex 复审 2026-08-06 的 medium):Node 的 `'close'`
66
- // 只发一次,在等待窗里断连的客户端其 close 事件会在监听器注册之前烧掉 ⇒ `closed` 恒 false ⇒ 泵照常
67
- // 进轮询循环,对着已死的 res 反复 getEvents/statusOf 直到终态/stale/15 分钟帽。本行原本紧跟
68
- // sseHeaders,是 preamble 插进来才多出这个窗 —— 所以搬接线、不搬 preamble。
69
- //
70
- // design/245 件1:接线本身收到共享助手(req+res 双挂 + 一次性闸)。此前只挂 `req`,而请求流被读干
71
- // 之后 `req` 的 close 不再补发 —— 本泵是 `task_run` 与 `image_bake` 两条日志共用的 seam,调用方
72
- // 是谁、在它之前有没有人碰过请求流,不该由本泵去赌。理由全文在 `sse-lifecycle.ts` 顶注。
73
45
  let closed = false;
74
46
  bindSseLifecycle(req, res, () => {
75
47
  closed = true;
76
48
  });
77
- // #151 车3 §5.1:preamble 排在 416 判定与 sseHeaders **之后**、日志 tail 之前。有界 + fail-open,
78
- // 见 collectSsePreamble 顶注;`id:` 行故意不写(非账本行,不推进 Last-Event-ID 游标)。
79
- const preamble = provider.preamble?.bind(provider); // bind:接口按方法签名声明,实现方允许用 `this`
49
+ const preamble = provider.preamble?.bind(provider);
80
50
  if (preamble) {
81
51
  const frames = await collectSsePreamble(preamble, (reason, err) => provider.onPreambleFailure?.(reason, err));
82
- // 等待窗里断连了就别再写(`closed` 由上面的 close 监听器置;`writableEnded`/`destroyed` 是
83
- // 第二道 —— codex 复审第二轮 medium 当时指出 res 侧的 close 面本 helper 没听,那半条已由
84
- // design/245 件1 的 `bindSseLifecycle` 补上;这道判据保留为第二重,与 routes/tasks.ts 心跳处逐字同源)。
85
52
  if (!closed && !res.writableEnded && !res.destroyed) {
86
53
  for (const f of frames)
87
54
  res.write(`event: ${f.event}\ndata: ${JSON.stringify(f.data)}\n\n`);
@@ -97,20 +64,11 @@ export async function streamSseLog(req, res, provider, id, staleMs) {
97
64
  }
98
65
  };
99
66
  while (!closed) {
100
- // Independent reads — issue concurrently to shave the SSE poll latency. The terminal-event gap is
101
- // still covered by the extra getEvents below (a `done` landing between these two is re-fetched).
102
67
  const [events, row] = await Promise.all([provider.getEvents(id, from), provider.statusOf(id)]);
103
68
  write(events);
104
69
  if (!row)
105
70
  break;
106
71
  if (row.status !== "running") {
107
- // Terminal. The `done`/`failed` event is appended BEFORE the coarse status flips terminal (runs.ts +
108
- // the bake terminal-write), so it can land in the gap between the getEvents and statusOf above — fetch
109
- // ONCE MORE so the stream never ends before delivering the terminal event. (The ONE exception is E12's
110
- // post-completion `suggestions` event, appended AFTER setTerminal once the fire-and-forget pass settles —
111
- // it is intentionally NOT delivered on this live tail; a poller reads it from GET /v1/runs/:id, which is the
112
- // `await result() THEN await suggestions()` contract. Releasing the session lock at `done` beats blocking it
113
- // on the suggestion LLM pass just to stream a UI nicety.)
114
72
  write(await provider.getEvents(id, from));
115
73
  break;
116
74
  }
@@ -121,13 +79,10 @@ export async function streamSseLog(req, res, provider, id, staleMs) {
121
79
  break;
122
80
  }
123
81
  if (Date.now() - start > SSE_MAX_STREAM_MS) {
124
- // BL-16: cap reached but the run continues — emit a terminal reconnect signal (not a silent close, which
125
- // a client couldn't distinguish from a completed run). Distinct from the `failed`/`done` run events.
126
82
  res.write(`event: error\ndata: ${JSON.stringify({ type: "error", errorCode: "STREAM_MAX_DURATION", message: "stream reached its 15-minute cap — reconnect with Last-Event-ID to continue (the run is still active)" })}\n\n`);
127
83
  break;
128
84
  }
129
85
  if (events.length === 0) {
130
- // Heartbeat during a long idle tail (e.g. a debate phase) so the relay's idle timeout doesn't fire.
131
86
  if (Date.now() - lastBeat > SSE_HEARTBEAT_IDLE_MS) {
132
87
  res.write(`event: heartbeat\ndata: {}\n\n`);
133
88
  lastBeat = Date.now();
package/dist/http/tar.js CHANGED
@@ -1,23 +1,14 @@
1
- /**
2
- * 极小 ustar 写器(workspace 浏览面 #3 的 archive 端点用,[1882]/[1894])。零依赖(仓内 no-SDK 姿势,
3
- * 同 s3-presign/OTLP 先例)。只写 regular file 条目:512B ustar 头(prefix 155/name 100 拆分)+
4
- * 内容 512 对齐 + 两块零尾。**不做** PAX/GNU 长名扩展——快照 relPath 超过 prefix+name 可承载
5
- * (>255 或不可在 "/" 处拆)属病态路径,调用方按 fail-loud 处理(archive 面 422 点名)。
6
- */
7
1
  const BLOCK = 512;
8
- /** 八进制右对齐 NUL 结尾字段(ustar 数字字段形)。 */
9
2
  function octal(n, width) {
10
3
  const s = n.toString(8).padStart(width - 1, "0");
11
4
  return Buffer.from(s + "\0", "ascii");
12
5
  }
13
- /** 把 relPath 拆成 ustar 的 (prefix ≤155, name ≤100);不可拆 ⇒ null(调用方 fail-loud)。 */
14
6
  export function splitTarPath(relPath) {
15
7
  const bytes = Buffer.byteLength(relPath, "utf8");
16
8
  if (bytes <= 100)
17
9
  return { prefix: "", name: relPath };
18
10
  if (bytes > 255)
19
11
  return null;
20
- // 从右往左找一个 "/",使 name ≤100 且 prefix ≤155(按字节算——多字节字符路径保守处理)。
21
12
  for (let i = relPath.length - 1; i > 0; i--) {
22
13
  if (relPath[i] !== "/")
23
14
  continue;
@@ -28,11 +19,6 @@ export function splitTarPath(relPath) {
28
19
  }
29
20
  return null;
30
21
  }
31
- /** 一个 regular-file 条目的 512B ustar 头。`mtimeSec` 由调用方给(快照无 per-file mtime ⇒ 统一用
32
- * 快照时刻/0——确定性输出,同輸入同字节)。 */
33
- /** ustar 的 size 字段是 12 字节八进制(11 位数字 + NUL)⇒ 上限 8 GiB−1。超了写进去会溢出到相邻
34
- * 字段/丢末位并**静默**产出坏头(复审 F8/A-LOW 实测:2^36 解析回来是值/8)。fail-loud,与
35
- * splitTarPath 的病态路径同姿势。当前配置下不可达(快照总字节帽 256MiB),属防御纵深。 */
36
22
  const USTAR_MAX_SIZE = 8 * 1024 * 1024 * 1024 - 1;
37
23
  export function tarHeader(relPath, sizeBytes, mtimeSec) {
38
24
  if (!Number.isFinite(sizeBytes) || sizeBytes < 0 || sizeBytes > USTAR_MAX_SIZE) {
@@ -43,13 +29,13 @@ export function tarHeader(relPath, sizeBytes, mtimeSec) {
43
29
  throw new Error(`tar: path not representable in ustar (must be ≤255 bytes and splittable at "/"): ${relPath}`);
44
30
  const h = Buffer.alloc(BLOCK, 0);
45
31
  h.write(split.name, 0, 100, "utf8");
46
- octal(0o644, 8).copy(h, 100); // mode
47
- octal(0, 8).copy(h, 108); // uid
48
- octal(0, 8).copy(h, 116); // gid
32
+ octal(0o644, 8).copy(h, 100);
33
+ octal(0, 8).copy(h, 108);
34
+ octal(0, 8).copy(h, 116);
49
35
  octal(sizeBytes, 12).copy(h, 124);
50
36
  octal(Math.max(0, Math.floor(mtimeSec)), 12).copy(h, 136);
51
- h.fill(0x20, 148, 156); // chksum = spaces while summing
52
- h.write("0", 156, 1, "ascii"); // typeflag: regular file
37
+ h.fill(0x20, 148, 156);
38
+ h.write("0", 156, 1, "ascii");
53
39
  h.write("ustar\0", 257, 6, "ascii");
54
40
  h.write("00", 263, 2, "ascii");
55
41
  h.write(split.prefix, 345, 155, "utf8");
@@ -59,11 +45,9 @@ export function tarHeader(relPath, sizeBytes, mtimeSec) {
59
45
  Buffer.from(sum.toString(8).padStart(6, "0") + "\0 ", "ascii").copy(h, 148);
60
46
  return h;
61
47
  }
62
- /** 内容块的 512 对齐尾垫(0 长内容=0 垫)。 */
63
48
  export function tarPadding(sizeBytes) {
64
49
  const rem = sizeBytes % BLOCK;
65
50
  return Buffer.alloc(rem === 0 ? 0 : BLOCK - rem, 0);
66
51
  }
67
- /** 归档终止:两块 512 零。 */
68
52
  export const TAR_END = Buffer.alloc(BLOCK * 2, 0);
69
53
  //# sourceMappingURL=tar.js.map
@@ -1,12 +1,7 @@
1
- /** Verification rounds requested by a caller, clamped to a safe [1,5] (default 2). */
2
1
  export function clampVerifyRounds(v) {
3
2
  const n = typeof v === "number" && Number.isFinite(v) ? Math.floor(v) : 2;
4
3
  return Math.min(5, Math.max(1, n));
5
4
  }
6
- /** `undefined` when `body.verify` is not `true` (the caller didn't opt into the gate); otherwise the clamped
7
- * rounds + (if `maxCostUsd` is a positive number) the same USD→micro-USD cost ceiling the cascade lane uses.
8
- * Same expression that lived inline at all four verify/resume call sites in http/server.ts (fresh-submit,
9
- * resumeCheckpoint, resumePreempted, resumePlanReview) — collected here VERBATIM (design/97 R9). */
10
5
  export function verifyRoundsFromBody(body) {
11
6
  return body.verify === true
12
7
  ? {
@@ -1,12 +1,3 @@
1
- /**
2
- * D-0 (design/80 §1): the single source of truth for the `CheckpointGate.kind` values the service emits on the
3
- * wire. The suspend path surfaces the gate VERBATIM (`append("suspended", { gate })`, server.ts), and the
4
- * agent-client SDK renders these as typed arms (an unknown kind falls through the SDK's open `(string & {})`
5
- * arm and renders generically). This explicit mirror exists so the compile-time guards below + the contract
6
- * test (`test/wire-gate-contract.test.ts`) keep the wire/SDK in lockstep with core — the guarantee the SDK's
7
- * own `spec.test.ts` cannot give (it hardcodes the kinds without importing core, so it would stay GREEN the
8
- * moment core adds a 6th kind; design/80 §3 #10 was therefore unenforced until this).
9
- */
10
1
  export const WIRE_GATE_KINDS = ["human", "irreversible_ask", "resource_limit", "needs_review", "task_done", "plan_review"];
11
2
  const _wireCoversCore = true;
12
3
  const _noStaleWireKind = true;
@@ -1,13 +1,4 @@
1
- /**
2
- * design/158 A9 seam —— workspace 文件面的内容类型/二进制判别,从 `http/server.ts` 整段搬出的原文。
3
- * routes/sessions.ts 的 workspace 腿要用它,所以住在 http/ 叶子里(routes/* 不可值 import server.ts)。
4
- */
5
- /** workspace file 面的 content-type(#3,[1894]③):保守扩展名表——只收「浏览器渲染无歧义且
6
- * 无主动内容」的类型,其余一律 octet-stream(nosniff 恒带;html/svg 等可执行内容**故意**不进表,
7
- * agent 产物=不可信,按下载处理)。 */
8
1
  export function workspaceContentType(relPath) {
9
- // 🔴 复审 A-LOW:先确认**真有** `.` 再取扩展名——`lastIndexOf` 返回 -1 时 `slice(0)` 会把整个
10
- // basename 当扩展名,于是一个名为 `png`/`pdf` 的任意内容文件会命中保守表按 image/png 发出。
11
2
  const base = relPath.slice(relPath.lastIndexOf("/") + 1);
12
3
  const dot = base.lastIndexOf(".");
13
4
  const ext = dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
@@ -19,7 +10,6 @@ export function workspaceContentType(relPath) {
19
10
  };
20
11
  return TABLE[ext] ?? "application/octet-stream";
21
12
  }
22
- /** 二进制判别([1894]③):首 8KiB 含 NUL 即二进制(git 同判据——快、稳、无误伤 UTF-8)。 */
23
13
  export function looksBinary(bytes) {
24
14
  const n = Math.min(bytes.byteLength, 8192);
25
15
  for (let i = 0; i < n; i++)
@@ -1,25 +1,4 @@
1
- /**
2
- * Bake input WHITELIST + vetted-argv builder (IMAGE-API-DESIGN.md §P2.4 — the anti-RCE core).
3
- *
4
- * bands/profile/baseRef/bump compose a Dockerfile + a `FROM` + a `docker build` = arbitrary code on the build
5
- * host. So EVERY field is validated SERVER-SIDE against a CLOSED SET *before* an argv is built; off-list → an
6
- * error, never coerced. The runner only ever sees the VETTED argv this module emits (never operator free-text)
7
- * and only runs `build.sh`, which composes Dockerfiles from the in-repo `bands/` via `gen-dockerfile.sh` — there
8
- * is no path here to inject a raw Dockerfile or a shell command.
9
- *
10
- * Set membership is the gate (NOT regex): `^[a-z0-9-]+$` is belt-only — `python; rm -rf /` ∉ the frozen set →
11
- * rejected, and the argv is a RAW vetted VECTOR the runner array-spawns (each element in its own argv slot, no
12
- * shell), never concatenated into a command string.
13
- *
14
- * The frozen catalogs below MIRROR the recipe source of truth (`gen-dockerfile.sh` `profile_bands()` + the
15
- * `bands/` dir). A DRIFT test (test/image-bake-validate.test.ts) asserts they still match — a silently-drifted
16
- * list would either reject a valid profile or admit a removed band. Keep them in lockstep with the recipes.
17
- */
18
1
  import { SHA256_DIGEST_RE as DIGEST_RE } from "../digest-form.js";
19
- /**
20
- * FROZEN profile catalog — mirrored from `e2b-template/dev-sandbox/gen-dockerfile.sh` `profile_bands()`.
21
- * (db / browser-nodriver are opt-in overlays, NOT in `code-full` — `code-full-db` adds db; per design §4.)
22
- */
23
2
  export const FROZEN_PROFILES = [
24
3
  "code-python",
25
4
  "code-node",
@@ -41,7 +20,6 @@ export const FROZEN_PROFILES = [
41
20
  "code-full",
42
21
  "code-full-db",
43
22
  ];
44
- /** FROZEN band set — mirrored from the `e2b-template/dev-sandbox/bands/` directory (one dir per band). */
45
23
  export const FROZEN_BANDS = [
46
24
  "python",
47
25
  "node",
@@ -63,11 +41,6 @@ export const FROZEN_BANDS = [
63
41
  "browser-nodriver",
64
42
  "db",
65
43
  ];
66
- /**
67
- * FROZEN bump-tool set — the toolchain identifiers a `--bump <tool>=<ver>` may target. Conservative union of the
68
- * version-overridable toolchains the bands accept (`<TOOL>_VERSION` env, build.sh forwards `--bump tool=ver`).
69
- * Set-membership is the gate; the value is range-checked by VER_RE. Off-list tool → 400 (never coerced).
70
- */
71
44
  export const FROZEN_BUMP_TOOLS = [
72
45
  "node",
73
46
  "python",
@@ -86,28 +59,14 @@ export const FROZEN_BUMP_TOOLS = [
86
59
  const PROFILE_SET = new Set(FROZEN_PROFILES);
87
60
  const BAND_SET = new Set(FROZEN_BANDS);
88
61
  const BUMP_TOOL_SET = new Set(FROZEN_BUMP_TOOLS);
89
- /** Bump value range (P2.4d): printable version-ish tokens only, ≤40 chars — folds into the lockhash. */
90
62
  export const VER_RE = /^[A-Za-z0-9._-]{1,40}$/;
91
- /** Belt-only band shape (set-membership is the real gate); a defense-in-depth reject of structurally weird ids. */
92
63
  export const BAND_SHAPE_RE = /^[a-z0-9-]+$/;
93
64
  function reject(status, error, conflictBands) {
94
65
  return conflictBands ? { ok: false, status, error, conflictBands } : { ok: false, status, error };
95
66
  }
96
- /**
97
- * Validate + normalize a `POST /v1/images/bakes` body into a vetted argv, or a typed rejection.
98
- *
99
- * Order is deliberate: profile XOR bands → bands set-membership (dupes/empties/shape) → bump set+range →
100
- * baseRef shape (the index allow-list check is done by the caller via `baseRefAllowed`, async) → build the
101
- * raw vetted argv. The output repo is server-fixed (`sema-images/<profile>`); the caller CANNOT set
102
- * `--tag` (build.sh auto-computes the immutable tag — kills namespace-squat) nor an arbitrary `FROM`.
103
- *
104
- * `baseRefAllowed` is checked HERE for shape only; the digest-in-index allow-list is the caller's async step
105
- * (it owns the TiDB index). A present-but-malformed `baseRef` is rejected before any DB hit.
106
- */
107
67
  export function validateBake(body) {
108
68
  const hasProfile = body.profile !== undefined && body.profile !== null && body.profile !== "";
109
69
  const hasBands = body.bands !== undefined && body.bands !== null;
110
- // profile XOR bands — exactly one required.
111
70
  if (hasProfile && hasBands)
112
71
  return reject(400, "provide profile OR bands, not both");
113
72
  if (!hasProfile && !hasBands)
@@ -126,7 +85,6 @@ export function validateBake(body) {
126
85
  return reject(400, "bands must be an array of band ids");
127
86
  if (body.bands.length === 0)
128
87
  return reject(400, "bands must be a non-empty array");
129
- // Cap length at the all-bands count — a longer list can only be dupes/garbage.
130
88
  if (body.bands.length > FROZEN_BANDS.length)
131
89
  return reject(400, `at most ${FROZEN_BANDS.length} bands`);
132
90
  const seen = new Set();
@@ -139,21 +97,18 @@ export function validateBake(body) {
139
97
  if (seen.has(raw))
140
98
  return reject(400, `duplicate band '${raw}'`);
141
99
  seen.add(raw);
142
- // "custom"/tenant bands are a P4 affordance → 501 (not a 400) so the UI distinguishes "not yet" from "bad".
143
100
  if (!BAND_SET.has(raw))
144
101
  offList.push(raw);
145
102
  }
146
103
  if (offList.length > 0) {
147
- // An off-list band that is the reserved tenant marker routes to P4; any other off-list id is an unknown band.
148
104
  if (offList.includes("custom")) {
149
105
  return reject(501, "custom/tenant bake is P4");
150
106
  }
151
107
  return reject(400, `unknown band(s): ${offList.join(", ")}`, offList);
152
108
  }
153
109
  bands = body.bands;
154
- profile = "custom"; // build.sh records profile="custom" for --bands; the argv carries --bands, not --profile.
110
+ profile = "custom";
155
111
  }
156
- // bump — {tool:ver}: tool ∈ FROZEN_BUMP_TOOLS, ver ∈ VER_RE. Off-list/range → 400.
157
112
  let bump = null;
158
113
  if (body.bump !== undefined && body.bump !== null) {
159
114
  if (typeof body.bump !== "object" || Array.isArray(body.bump)) {
@@ -170,8 +125,6 @@ export function validateBake(body) {
170
125
  }
171
126
  bump = Object.keys(out).length > 0 ? out : null;
172
127
  }
173
- // baseRef — caller CANNOT pass an arbitrary FROM. Shape-check here (the index allow-list is the caller's async
174
- // step). Default = the pinned current base, injected by the caller when baseRef is absent.
175
128
  let baseRef = null;
176
129
  if (body.baseRef !== undefined && body.baseRef !== null && body.baseRef !== "") {
177
130
  if (typeof body.baseRef !== "string" || !DIGEST_RE.test(body.baseRef)) {
@@ -182,7 +135,6 @@ export function validateBake(body) {
182
135
  const push = body.push === true;
183
136
  const dryRun = body.dryRun === true;
184
137
  const logs = body.logs === true;
185
- // pkgSource — RFC B4 closed set (cn|global); off-list → 400, never coerced (same discipline as every field).
186
138
  let pkgSource = null;
187
139
  if (body.pkgSource !== undefined && body.pkgSource !== null && body.pkgSource !== "") {
188
140
  if (body.pkgSource !== "cn" && body.pkgSource !== "global") {
@@ -200,25 +152,9 @@ export function validateBake(body) {
200
152
  dryRun,
201
153
  logs,
202
154
  pkgSource,
203
- // argv is built by the caller AFTER it resolves the effective baseRef (the index allow-list + default base).
204
155
  argv: [],
205
156
  };
206
157
  }
207
- /**
208
- * Build the VETTED, NORMALIZED build.sh argv from an accepted validation + the caller-resolved effective fields
209
- * (the index-checked baseRef and the build-host CACHE_BASE — both runner-environment facts image-api injects,
210
- * never operator free-text). The elements are RAW (no shell-quoting): the runner array-spawns build.sh so each
211
- * element lands in its own argv slot (no shell can re-interpret a frozen-set value). The runner runs
212
- * `build.sh <argv...>` and ONLY this.
213
- *
214
- * Server-fixed invariants enforced here (NOT caller-settable, P2.4d):
215
- * - `--json` always (machine output the runner parses).
216
- * - selection is `--profile <p>` XOR `--bands a,b,c` (already vetted).
217
- * - NO `--tag` (build.sh auto-computes the immutable tag — kills namespace-squat / overwrite-by-naming).
218
- * - the output repo is server-fixed `sema-images/<profile>` (build.sh's IMAGE_PREFIX default; not forwarded).
219
- * - `--base-ref` is the index-checked effective base (caller resolves the default when absent).
220
- * - `--cache-base` is the build-host promoted cache (runner env).
221
- */
222
158
  export function buildBakeArgv(v, effective) {
223
159
  const args = [];
224
160
  if (v.bands && v.bands.length > 0) {
@@ -242,11 +178,6 @@ export function buildBakeArgv(v, effective) {
242
178
  if (v.dryRun)
243
179
  args.push("--dry-run");
244
180
  args.push("--json");
245
- // Return the RAW vetted vector. Set-membership against FROZEN_PROFILES/BANDS/BUMP_TOOLS is the RCE gate; the
246
- // runner array-spawns build.sh with each element in its own argv slot (no shell), so a frozen-set value can
247
- // never be re-interpreted by a shell. Shell-quoting here would embed literal single-quotes into each slot →
248
- // build.sh sees `'--profile'` (quotes included) and dies "unknown arg" on the first one (H2). Quoting is wrong
249
- // for an array-spawn boundary; the injection boundary is the array slot, not a joined command string.
250
181
  return args;
251
182
  }
252
183
  //# sourceMappingURL=bake-validate.js.map
@@ -4,18 +4,16 @@ const NESTED_BUILD_MODES = [
4
4
  "rootless-buildkit",
5
5
  "privileged-dind",
6
6
  ];
7
- /** Project the manifest's `nestedBuild` (string posture OR bool) → the enum (center contract). */
8
7
  export function nestedBuildModeOf(v) {
9
8
  if (typeof v === "string") {
10
9
  return NESTED_BUILD_MODES.includes(v)
11
10
  ? v
12
11
  : v
13
- ? "rootless-buildkit" // an unknown non-empty posture is treated as the safe default the pool uses
12
+ ? "rootless-buildkit"
14
13
  : "none";
15
14
  }
16
15
  return v ? "rootless-buildkit" : "none";
17
16
  }
18
- /** Parse a docker-style size ("64m" / "512M" / "1g") → MB. Undefined on an unparseable / absent value. */
19
17
  export function shmToMB(v) {
20
18
  if (!v)
21
19
  return undefined;
@@ -25,14 +23,12 @@ export function shmToMB(v) {
25
23
  const n = Number(m[1]);
26
24
  return (m[2] ?? "m").toLowerCase() === "g" ? n * 1024 : n;
27
25
  }
28
- /** --bands builds record profile="custom", so the tag's repo part ("code-flutter:tag" → "code-flutter") wins. */
29
26
  export function profileFromManifest(m) {
30
27
  if (m.profile && m.profile !== "custom")
31
28
  return m.profile;
32
29
  const repo = (m.tag ?? "").split(":")[0] ?? "";
33
30
  return repo || m.profile || "custom";
34
31
  }
35
- /** Pure projection: a build.sh manifest + the push-time facts → an index upsert. */
36
32
  export function manifestToIndexEntry(m, opts) {
37
33
  const profile = profileFromManifest(m);
38
34
  const tag = (m.tag ?? "").includes(":") ? (m.tag ?? "").split(":")[1] : (m.tag ?? "");
@@ -61,7 +57,6 @@ export function manifestToIndexEntry(m, opts) {
61
57
  sizeBytes: opts.sizeBytes ?? m.sizeBytes ?? null,
62
58
  status: opts.status ?? "published",
63
59
  visibility,
64
- // a tenant image carries its owner; the standard pool's "standard-pool" owner is NOT a tenant.
65
60
  tenantId: visibility === "tenant" && m.owner && m.owner !== "standard-pool" ? m.owner : null,
66
61
  manifestSha: opts.manifestSha ?? null,
67
62
  recipeGitSha: m.recipeGitSha && m.recipeGitSha !== "nogit" ? m.recipeGitSha : null,
package/dist/index.js CHANGED
@@ -1,14 +1,7 @@
1
- // Library surface (for embedding the service wiring in another Node app — e.g. the shell's seamless local mode
2
- // runs the sema-server HTTP wire in-process via createHttpServer + a local StoreBackend).
3
1
  export { loadConfig } from "./config.js";
4
2
  export { createBrain } from "./brain.js";
5
3
  export { createSessionStore } from "./plugins/session-store.js";
6
- // design/158 B5-①([1971] 声明后落):中立后端缝 —— 嵌入姿势(见顶注)= createHttpServer + StoreBackend。
7
4
  export { createStoreBackend } from "./plugins/store-backend.js";
8
- // ── B5-②(clay 两拍终裁 2026-07-30:cli/core 零消费,弃用窗撤销直接删)——6 个 TiDB 方言专属导出已摘 ──
9
- // 判据:①backend-可插铁律(公面即承诺,方言类在公面=邀请绕过中立缝直连方言);②公面瘸腿(PG 孪生
10
- // 从未导出);③全生态扫描零消费。替代:构造走 createStoreBackend(kind mysql|pg|local),DDL 审阅走
11
- // docs/schema/baseline-{mysql,pg}.sql(生成基线,机器门逐字节)。类/函数本体仍是内部实现(深径 import)。
12
5
  export { CachingSessionStore } from "./plugins/caching-session-store.js";
13
6
  export { runInBackground } from "./runs.js";
14
7
  export { createHardenedVmRunner, runInHardenedVm } from "./orchestration/hardened-vm-runner.js";
@@ -22,25 +15,11 @@ export { nowTool } from "./capabilities/builtin-tools.js";
22
15
  export { createCouncilTool } from "./capabilities/code-review-council.js";
23
16
  export { loadSkills, skillsForScenario } from "./capabilities/skills.js";
24
17
  export { buildScenarios, selectScenario, } from "./capabilities/scenarios.js";
25
- // #318:会话级任务清单车道(嵌入式装配自己组 ScenarioDeps 时接 `taskListStoreFor` 的属主工厂)。
26
- // 两条 `ensure*TaskListSchema` 一并导出:车道**不建表**(建表属主 = 调它的装配层,见车道顶注),
27
- // 所以走 SQL 臂的嵌入式装配必须自己先 await 对应方言的 ensure —— 不导出等于把那一步藏起来。
28
18
  export { createTaskListLane, taskListKeyFor, MAX_TASK_LIST_SESSIONS } from "./boot/task-list-lane.js";
29
19
  export { ensureTiDBTaskListSchema, ensurePgTaskListSchema } from "./plugins/task-list-store-sql.js";
30
20
  export { createAuthorizer, principalFrom, HttpError, } from "./security.js";
31
21
  export { memoryScopeFor, memoryEngineBackendFor } from "./memory-scope.js";
32
22
  export { createHttpServer, } from "./http/server.js";
33
- // ── A-057.8:`ServiceCoreDeps.sideQuery` 是**必填**席,而它的装配链此前整条不在公面上 ──
34
- // CHANGELOG 对自建 `ServiceDeps` 的嵌入方逐字指路「按 `createSideQueryLane` 装配即可」,但
35
- // `package.json` 的 `exports` 只开 `.` / `./main` / `./package.json`(无通配、无 typesVersions),
36
- // `files` 又只出 `dist` ⇒ 安装态里被指路的那个源文件读都读不到,深径 import 必
37
- // `ERR_PACKAGE_PATH_NOT_EXPORTED`。嵌入方于是只剩「手抄一份 per-model key 解析 + poison 处理」,
38
- // 而抄漏 poison 那半 = 拿共享网关账号去打别家上游,正是 #303 立案要修的凭据外泄病灶本身。
39
- // 🔴 poison 的判别式(`isSealedKeyPoison` / `SealedKeyPoisonedError`)与工厂**同批**出面是刻意的:
40
- // 只给工厂不给判据,嵌入方对「sealed key 解不开」就只能靠 message 猜,而猜错的方向恰好是静默回落
41
- // 网关 key(安全轴上没有静默降级 —— CLAUDE.md #157)。
42
- // 🔴 这五个值导出必须保持**值**形态(不许改成 `export type`):门 = `test/export-liveness-gate.test.ts`
43
- // 的 gate:embedder-assembly-exports(表内符号缺一即红;runtime 类被 type-only 化同样红)。
44
23
  export { createSideQueryLane, createPerModelKeyBrain } from "./boot/side-query-lane.js";
45
24
  export { createKeyResolver, resolveModelApiKey } from "./key-resolver.js";
46
25
  export { isSealedKeyPoison, SealedKeyPoisonedError } from "./sealed-key.js";
@@ -7,30 +7,15 @@ export function createKeyResolver(modelApiKeyEnv, env = process.env, modelApiKey
7
7
  return apiKey ? { apiKey } : undefined;
8
8
  };
9
9
  }
10
- /**
11
- * The ONE per-model key chain, synchronous: **sealed custody → env-NAME reference → (caller's) gateway key**,
12
- * with a poisoned sealed entry THROWING instead of returning undefined. `createKeyResolver` is the async
13
- * `getApiKeyAndHeaders` face of exactly this function — every other consumer must call it rather than
14
- * re-implement a subset of the chain.
15
- *
16
- * 复审 2026-07-29(同族缺口):`brain.ts` 的反应式降级 hop 曾只查 `modelApiKeyEnv` —— sealed-box 托管密钥的
17
- * 降级目标(按互斥契约**永不**落 modelApiKeyEnv)于是静默拿网关 key 打自己的网关(错 key 打上游),毒丸更
18
- * 被当成"没配 key"。链只有一条,重复实现就会长出这种半条链的偏差,故此处抽出同步内核共用。
19
- *
20
- * `undefined` = this model has no per-model key ⇒ the CALLER's gateway/default credential applies (the
21
- * additive contract). An empty env value counts as unset (unchanged from the original inline chain).
22
- */
23
10
  export function resolveModelApiKey(modelName, modelApiKeyEnv, env = process.env, modelApiKeys = {}) {
24
11
  const sealed = modelApiKeys[modelName];
25
- // Poison pill: the operator configured a sealed key for this model but it cannot be unsealed —
26
- // fail the brain call LOUD (never undefined = never the gateway fallback).
27
12
  if (isSealedKeyPoison(sealed))
28
13
  throw new SealedKeyPoisonedError(modelName, sealed);
29
14
  if (sealed)
30
- return sealed; // sealed-box custody wins (registry-core: sealed 压过 apiKeyEnv)
15
+ return sealed;
31
16
  const envName = modelApiKeyEnv[modelName];
32
17
  if (!envName)
33
- return undefined; // model has no per-model key → core uses the gateway key
18
+ return undefined;
34
19
  return env[envName] || undefined;
35
20
  }
36
21
  //# sourceMappingURL=key-resolver.js.map
@@ -1,33 +1,15 @@
1
- /**
2
- * 一次 diff 的字节帽(pull 腿与 push/上行腿**共用同一个数**)。
3
- *
4
- * 🔴 exported 是判据的一部分(2026-08-08,#176 二轮扫描 finding):`diffup.ts` 原先另抄了一份同值常量,
5
- * 而它**本就已经** `import { safeRev } from "./diffout.js"` —— 依赖边在,共享零成本。两份手抄的后果不是
6
- * 「多写一行」,是**单边改值后上行/pull 两腿对同一个 diff 的过/拒判定分家**:同一份补丁在一条腿上过、
7
- * 另一条腿上被截断,而两处都「按自己的常量」正确工作,没有任何一处会报错。
8
- * (同族先例:`checkpoint-store-sql.ts` 的 `export const TERMINAL_GRACE_MS`。)
9
- */
10
1
  export const DEFAULT_MAX_DIFF_BYTES = 5 * 1024 * 1024;
11
2
  const DEFAULT_READ_TIMEOUT_MS = 30_000;
12
- /** A safe git rev token: sha, branch/tag name, or `A..B`. No shell metacharacters, no leading dash.
13
- * 🔗 近亲判据:`leader/planner.ts` 的 branch 校验用同一套字符类 + 同一个 200 上限 + 同样拒前导 `-`,
14
- * 只在 `..` 上有意分家(这里要放行 `A..B` 区间形,那边全拒)。差异与候裁说明写在 planner 那处
15
- * (#176 二轮扫描 finding)——单边加字符类成员时,请两处一起看。 */
16
3
  const SAFE_REV = /^[A-Za-z0-9._/-]{1,200}$/;
17
4
  export function safeRev(rev) {
18
5
  return SAFE_REV.test(rev) && !rev.startsWith("-") && !rev.includes("..--");
19
6
  }
20
- /**
21
- * Pull `<baseSha>..<ref>` from a worker container as a `git format-patch` series. Never throws — failures
22
- * (bad rev, non-zero git, truncation, transport loss, empty diff) are returned as `{ ok: false }`.
23
- */
24
7
  export async function pullDiff(env, baseSha, opts = {}) {
25
8
  const ref = opts.ref ?? "HEAD";
26
9
  if (!safeRev(baseSha) || !safeRev(ref)) {
27
10
  return { ok: false, error: `unsafe git rev (baseSha=${JSON.stringify(baseSha)} ref=${JSON.stringify(ref)})` };
28
11
  }
29
12
  const maxBytes = opts.maxBytes ?? DEFAULT_MAX_DIFF_BYTES;
30
- // --stdout (council#14): emit the whole series to stdout, not .patch files. Newest-last (default order).
31
13
  const cmd = `git format-patch --stdout ${baseSha}..${ref}`;
32
14
  let patch = "";
33
15
  let bytes = 0;
@@ -36,7 +18,7 @@ export async function pullDiff(env, baseSha, opts = {}) {
36
18
  try {
37
19
  for await (const chunk of env.execStream(cmd, {
38
20
  ...(opts.cwd ? { cwd: opts.cwd } : {}),
39
- maxOutputBytes: maxBytes + 1, // +1 so we can DETECT the overflow (council#13 fail-on-truncation)
21
+ maxOutputBytes: maxBytes + 1,
40
22
  readTimeoutMs: opts.readTimeoutMs ?? DEFAULT_READ_TIMEOUT_MS,
41
23
  ...(opts.signal ? { signal: opts.signal } : {}),
42
24
  })) {
@@ -56,7 +38,6 @@ export async function pullDiff(env, baseSha, opts = {}) {
56
38
  }
57
39
  }
58
40
  catch (e) {
59
- // execStream throws on transport loss / suspendVM-aborted in-flight command (no exit chunk).
60
41
  return { ok: false, error: `execStream failed before exit: ${e instanceof Error ? e.message : String(e)}` };
61
42
  }
62
43
  if (exitCode === undefined) {