@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
@@ -9,46 +9,15 @@ import { basename, resolve } from "node:path";
9
9
  import { stat as fsStat, readFile as fsReadFile } from "node:fs/promises";
10
10
  export function createLiveCoordinators(ctx) {
11
11
  const { config, logger, backend, sendUserFileTaskEnvs, ruleConsent, ruleScopeRootFor, getParkedRedeem } = ctx;
12
- // E23 (shell-host contract): inbound MCP elicitation coordinator (live-only HITL). Present ONLY when MCP_ELICITATION_ENABLED
13
- // — absent ⇒ onElicit is not wired ⇒ core advertises no elicitation capability to any server (fail-closed). Shared
14
- // by the runner (the onElicit seam) and the HTTP layer (the respond route + the per-run ALS context wraps).
15
12
  const elicitation = config.mcpElicitation.enabled ? new ElicitationCoordinator(config.mcpElicitation.throttle) : undefined;
16
- // §4④: AskUserQuestion LIVE-stream HITL coordinator — the sibling of `elicitation`. Present ⇒ wired onto
17
- // `RunnerDeps.onQuestion` (core mounts the AskUserQuestion tool + routes live asks here) AND consumed by the HTTP layer
18
- // (the `POST /v1/questions/:id/respond` route + the per-run ALS context wraps on the streaming legs). Absent ⇒ core
19
- // mounts the tool with the headless default (a run never hangs; the model just can't get a live answer).
20
- // #166 裁2 接线:三旋钮(QUESTION_TTL_MS / QUESTION_MAX_*_PER_RUN)从 config 通到协调器;缺席=构造器
21
- // 内建 DEFAULT_QUESTION_THROTTLE,默认值恒等钉在 test/question-unavailable.test.ts。
22
13
  const question = config.askQuestionEnabled ? new QuestionCoordinator(config.questionThrottle) : undefined;
23
- // [816]/[820]②: the live tool-approval HITL coordinator — the sibling of `question` on core's `RunnerDeps.onAsk`
24
- // seam (1.290 sync-ask leg). Present ⇒ a policy `ask` on a LIVE streaming leg becomes a `tool_approval` frame the
25
- // shell renders as the CC three-choice card, answered via POST /v1/tool-approvals/:id/respond.
26
- // [1535] 正名(旧文「background/workflow ⇒ headless auto-deny stands」已过时):宿主 sync 腿现同时装
27
- // `spec.onAsk = boundAsk(ctx)`(装配点在 `http/routes/tasks.ts`,design/158 A9 从 server.ts 拆出后
28
- // 的全树唯一装配点)——core 继承链把闭包冻给委派子代,**bg/嵌套子代的
29
- // ask 浮到宿主 live 流**(带 sourceTaskId);真正无宿主流的腿(durable-submit/headless resume)才留
30
- // 「unavailable → durable park;无 park 设施 core fail-closed deny」。durable 部署 G1 语义不变。
31
- // [1.294 G1] durable 姿势必须在 runnerDeps 字面量之前可判(onAsk 的「在场性」本身就是 core suspendAsk 的
32
- // 分路条件,不能再靠闭包惰性读)——判据与 checkpointStore 的构造条件同源(backend?.checkpoint 存在 ∧
33
- // DURABLE_APPROVAL;store 本体已上移至 subRunner 构造前,[1535] 断点② 双 Runner 挂载)。
34
- // 🔴 #151 车3 刀 3b:本行**上移**到协调器构造之前 —— 它同时是流内审批协议的「park 设施在场性」判据
35
- // (§8.4),而协调器要在构造那一刻就知道自己带不带 askStore。位置即契约的原约束(必须在 RunnerDeps
36
- // 字面量之前)不变,只是又提前了几行。
37
14
  const durableEnabled = backend?.checkpoint !== undefined && config.durableApproval === true;
38
- // #151 车3 刀 3b(设计稿 §2.3 + §8.4):协议上场判据走**单一谓词**(与 /v1/capabilities 的
39
- // `streamApproval` 格、与回决端点的 501 门同一个符号,见 resolveStreamApprovalGate 顶注)。
40
15
  const streamApprovalGate = resolveStreamApprovalGate({
41
16
  toolApprovalEnabled: config.toolApprovalEnabled === true,
42
17
  streamApprovalEnabled: config.streamApproval.enabled,
43
18
  backend,
44
19
  parkFacility: durableEnabled,
45
20
  });
46
- // §8.4:协议**开着**却上不了场 = 合法部署形(不是配置错误)⇒ 一行 **info**(不是 warn),把原因说明白。
47
- // 反过来「开关本来就没开」不打日志:显式关掉的部署自己知道为什么关,一行噪音没有信息量。
48
- // ⚠️ 读数已反转:总开关默认在树上已是 ON(版本坐标见 config.ts 的 `streamApprovalConfig()` 头注),
49
- // 所以这一行在**默认部署**上是常态 —— 任何没有 durable
50
- // backend / park 设施的默认部署每次启动都会打一行 `stream_approval_disabled`(每次启动一行,不是每
51
- // tick)。写这条判据时的前提「默认形绕开本路径」已不成立;要收紧,收紧点在本 if 而不是在开关处。
52
21
  if (config.streamApproval.enabled && !streamApprovalGate.active) {
53
22
  logger.info("stream_approval_disabled", {
54
23
  reason: streamApprovalGate.reason,
@@ -60,65 +29,31 @@ export function createLiveCoordinators(ctx) {
60
29
  }
61
30
  const toolApproval = config.toolApprovalEnabled
62
31
  ? new ToolApprovalCoordinator({
63
- // 协议上场 ⇒ 持久层 + 配置窗(§7:`STREAM_ASK_WINDOW_MS` **只在开关内**生效,
64
- // `DEFAULT_APPROVAL_TTL_MS` 一个字不动);不上场 ⇒ 两者都不传,窗保持既有 5min(D1 逐字不变)。
65
- // ⚠️ 两臂的窗**默认已同长**:窗默认自 7.5.0 起 = 300000 = `DEFAULT_APPROVAL_TTL_MS`(clay 同裁把
66
- // 「窗变短」从翻真里摘出去)。原注画的「上场 = 60s / 不上场 = 5min」这个对比在默认配置下不存在
67
- // 了,别据它得出「开了协议窗会缩短」的运维结论:上场与否改变的是**持久层与协议帧**,窗只有在
68
- // 部署显式设了 `STREAM_ASK_WINDOW_MS` 时才分叉。
69
32
  ...(streamApprovalGate.active ? { askStore: streamApprovalGate.askStore, ttlMs: config.streamApproval.windowMs } : {}),
70
- // 余量旋钮恒传(既有键,窗长三元公式的第二项;legDeadlineMonotonic 缺席时它不参与计算)。
71
33
  windowMarginMs: config.streamAskWindowMarginMs,
72
- // X-2 写侧准入门两帽(协议未上场时门本身不参与,见 ToolApprovalCoordinator.admit)。
73
34
  admitMaxPerTask: config.streamApproval.admitMaxPerTask,
74
35
  admitMaxPerOwner: config.streamApproval.admitMaxPerOwner,
75
- // #154 车二:同意车道(缺席 = 规则店没装配 ⇒ 帧上零候选、回决的 persistRule 如实拒)。
76
36
  ...(ruleConsent !== undefined ? { ruleConsent } : {}),
77
- // #295(F-1):卡批规则 project root 解析器(缺席 ⇒ 落 global,修前行为)。
78
37
  ...(ruleScopeRootFor !== undefined ? { ruleScopeRootFor } : {}),
79
- // #280 R-13 C:无人值守政策(闭集 park|deny)。**恒传**(不挂任何在场性推断):它是纯部署级的一位,
80
- // 语义/三问/射程边界一处成文见 `config-types.ts` 的 `unattendedApprovalPolicy` 域注。
81
- // ⚠️ 恒传是**刻意**的,不是顺手(codex R2-F2 点过这一行):`streamApprovalGate.active` 为假时只
82
- // 省略 askStore/窗,协调器照常在场,R-13 的 D1 窗到期臂照样按政策走 —— 那正是设计稿点名的主受益
83
- // 部署(park 的承载是 core checkpoint,不是 ask 行)。把政策挂到协议开关上会把它整个切掉。
84
- // 协调器本身缺席时(`TOOL_APPROVAL_ENABLED` 关)本旋钮无处施加 —— 成文事实,不是推断腿。
85
38
  unattendedPolicy: config.unattendedApprovalPolicy,
86
- // #329:PARKED 行迟到决议的赎回席(晚绑取值口,见 LiveCoordinatorsCtx.getParkedRedeem)。
87
39
  ...(getParkedRedeem !== undefined ? { parkedRedeem: getParkedRedeem } : {}),
88
40
  })
89
41
  : undefined;
90
- // SendUserFile(真 CC 契约,clay 2026-07-14)两种 lane 形态,其余缺席=诚实(工具不进 roster):
91
- // - host lane:本地盘读+服务端上传,【单用户 only】——host 多租户=任意路径读→公网 URL 的 exfil 面,
92
- // clay 拍永久关死,不留口子。路径=绝对或相对 process.cwd(用户目录语义,与 CC 一致)。
93
- // - e2b/k8s lane(v2,Plan B 直传):沙箱内 stat+`curl -T` 打 server 预签的单对象 PUT(字节不中转、
94
- // 凭据不进沙箱);沙箱自身文件系统=租户边界,故任意租户可用。
95
- // - ssh lane(后补 2026-07-14):同一直传链,但 peer=跨任务共享的真实主机(非租户边界)→
96
- // 【单用户 only】,租户门在 sandboxSendLaneEnabled(多租户下注册表不建,工具诚实缺席)。
97
- // adb/local-docker 仍后补。
98
- // 签发面缺 S3_PUBLIC_ENDPOINT 时 issue-time fail-loud(工具在但用即报修法);100MiB/文件帽在各 send 面。
99
42
  const sendUserFileEmitter = (() => {
100
43
  if (!config.sendUserFile)
101
44
  return undefined;
102
45
  if (sendUserFileTaskEnvs)
103
- return new SendUserFileEmitter(); // e2b/k8s/ssh(单用户):注册表已就位(lane+租户+签发面判定)
104
- // unset REMOTE_EXEC(in-process/TOC 本地形态)有意归入 host 语义:exec 与文件都在本机,单用户门
105
- // 同样把关(codex 复审提出收紧到显式 "host";判定=不收——unset 单机+配了 S3 是合法 dev/TOC 场景,
106
- // 且与 1.187.0 已发行为一致;真正的红线是 requirePrincipal 多租户,这里恒关)。
46
+ return new SendUserFileEmitter();
107
47
  const provider = config.remoteExec?.provider ?? "host";
108
48
  if (provider === "host" && config.requirePrincipal !== true)
109
49
  return new SendUserFileEmitter();
110
50
  return undefined;
111
51
  })();
112
- // SendUserFile 账本(多租户治理面,[768]②a 后半):key 里的 scope 段是哈希(URL 不泄 principal),
113
- // 按租户列/撤销的 scope↔对象映射记在 server 侧账本(四后端孪生,sendfile_link / JSONL)。有 backend 即建
114
- // (签发面也配了才有写点);无 backend(env-only worker)= 无账本 = 不记账不 501 治理面(诚实缺席,
115
- // capabilities.sendUserFileLedger=false)。
116
52
  const sendFileLedger = config.sendUserFile && backend ? backend.sendFileLedger() : undefined;
117
53
  const sendUserFileToolSpec = (() => {
118
54
  if (!sendUserFileEmitter || !config.sendUserFile)
119
55
  return undefined;
120
56
  const issuer = createSendUserFileIssuer(config.sendUserFile);
121
- // 两条 lane 的 send 都把 ctx.principal(core VERIFIED)传给签发面 → key 带哈希 scope 段 + link 携原文 scope。
122
57
  const rawSend = sendUserFileTaskEnvs
123
58
  ? createSandboxFileSend({
124
59
  registry: sendUserFileTaskEnvs,
@@ -133,13 +68,10 @@ export function createLiveCoordinators(ctx) {
133
68
  if (st.size > SEND_USER_FILE_MAX_BYTES)
134
69
  throw new Error(`file exceeds the ${Math.floor(SEND_USER_FILE_MAX_BYTES / (1024 * 1024))} MiB per-file cap (${st.size} bytes)`);
135
70
  const bytes = await fsReadFile(resolved);
136
- // 读后复验(codex 复审 MED):stat→read 间文件可增长——上传的是读到的字节,帽必须按真实字节数把关。
137
71
  if (bytes.byteLength > SEND_USER_FILE_MAX_BYTES)
138
72
  throw new Error(`file exceeds the ${Math.floor(SEND_USER_FILE_MAX_BYTES / (1024 * 1024))} MiB per-file cap (${bytes.byteLength} bytes)`);
139
73
  return issuer.issue(bytes, basename(resolved), undefined, sendCtx.principal);
140
74
  };
141
- // 账本写点(两 lane 同语义):上传+verify 成功后、file_link 帧前记账;记账失败=该文件 fail-loud +
142
- // best-effort 删已传对象(治理面必须有行;删失败 warn)。无账本(env-only)= 直通,行为与 1.189 一致。
143
75
  const send = sendFileLedger
144
76
  ? withLedgerRecording(rawSend, {
145
77
  ledger: sendFileLedger,
@@ -1,32 +1,18 @@
1
- /**
2
- * #320①([4728] clay 五点令)—— 崩溃遗言面 `crash-last.json`。
3
- *
4
- * 契约:**文件在场 ⇔ 上一次退出=崩溃**。铸点唯一=shutdown.ts 的 uncaught 双钩(写完仍 exit(1),
5
- * 不吞崩溃);受控退(hardShutdown 收尾链)`clearCrashLast` 删文件——两形可判别就靠这一删:不删的话
6
- * 文件语义退化成「历史上最后一次崩溃」,运维无从判「上次到底怎么退的」。
7
- *
8
- * 写路径全同步(`writeFileSync`):崩溃现场没有事件循环可信赖,async 写在 exit 前不保证落盘。
9
- * err 只取**首行**且过 `redactSecrets`——崩溃消息常含连接串/token(DSN 在 error.message 里是
10
- * 常见形,#305 同病),遗言是**判别位**不是取证档,stack 不落盘。
11
- */
12
1
  import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
13
2
  import { join } from "node:path";
14
3
  import { z } from "zod";
15
4
  import { redactSecrets } from "../trace/redact.js";
16
- /** 遗言文件的读回 schema(宪法 [2704] §2:边界读一律 safeParse,不裸铸)。写侧五键同形。 */
17
5
  const CrashLastSchema = z.object({
18
6
  at: z.string(),
19
7
  version: z.string(),
20
8
  pid: z.number(),
21
9
  uptimeMs: z.number(),
22
- /** 崩溃瞬间的在飞腿数(inflight getter 可用时);拿不到=键缺席,不编 0。 */
23
10
  inflight: z.number().optional(),
24
11
  err: z.string(),
25
12
  });
26
13
  export function crashLastPath(dataRoot) {
27
14
  return join(dataRoot, "crash-last.json");
28
15
  }
29
- /** 崩溃现场写遗言(同步;自身绝不抛——遗言写失败不能遮蔽原崩溃)。 */
30
16
  export function writeCrashLast(dataRoot, info) {
31
17
  try {
32
18
  const raw = info.err instanceof Error ? info.err.message : String(info.err);
@@ -42,19 +28,15 @@ export function writeCrashLast(dataRoot, info) {
42
28
  writeFileSync(crashLastPath(dataRoot), JSON.stringify(body) + "\n");
43
29
  }
44
30
  catch {
45
- // 遗言面自身故障(只读 FS 等)静默——崩溃路径上没有更好的去处,原错误仍会打到 stderr。
46
31
  }
47
32
  }
48
- /** 受控退收尾:删遗言(幂等,缺席零抛)。 */
49
33
  export function clearCrashLast(dataRoot) {
50
34
  try {
51
35
  unlinkSync(crashLastPath(dataRoot));
52
36
  }
53
37
  catch {
54
- // ENOENT(本就没有)与其余失败同置:清不掉只意味着下次 boot 多一行提示,绝不阻塞停机。
55
38
  }
56
39
  }
57
- /** boot 提示腿:在场读回、缺席/坏字节/坏形回 `undefined`(绝不抛——遗言坏了不能挡启动)。 */
58
40
  export function readCrashLast(dataRoot) {
59
41
  try {
60
42
  const p = crashLastPath(dataRoot);
@@ -1,87 +1,15 @@
1
- /**
2
- * #165 —— 沙箱 lane(e2b/k8s/ssh/adb/local-docker)写门的**真 env 裁决**接法。
3
- *
4
- * ## 缺口与它的形状
5
- *
6
- * core 的 `createFsWriteGatePolicy` / `createSensitivePathPolicy` 把每一个写目标交给
7
- * `canonicalizeTarget(env, …)` 去问 fs 要真身(`absolutePath` → `exists` → `canonicalPath` →
8
- * `fileInfo`/`readLink` 逐跳,dist 亲读)。host lane 上那个 env 就是 hand 工具真正写的那块盘。
9
- * 沙箱 lane 不是:per-task 的沙箱 env 由 core 的 `executionEnvFactory` 在 spec **之后**才铸,
10
- * 而写门 wiring 必须在 spec 期就交出去。
11
- *
12
- * 出路不是把裁决搬去 hook 层(那要在本仓复刻 `canonicalizeTarget` 的逐级 exists / symlink 解析 /
13
- * 段匹配 / 折叠序——复制 core 的裁决逻辑=同源谎),而是利用一个部署事实:**那个 per-task 沙箱 env
14
- * 本来就是本仓铸的**。于是本模块给写门一个 ExecutionEnv **代理**:它在**调用时刻**(runToolGate 裁决,
15
- * 必在 prepare 之后)去 slot 里取该 task 的真 env 并转发 fs 读原语。call-time 转发消除了时序问题,
16
- * 与「从调用上下文读 env」是同一性质。
17
- *
18
- * ## 三条不可动的裁定
19
- *
20
- * 1. **`absolutePath` 一面不转发**,沿用词法规则(POSIX 绝对形归一 / NUL 拒 / 非绝对形 err)。
21
- * 真 env 的 `absolutePath` 会按 **env 自己的 cwd** 解析相对路径,而工具真写处按 engine 跟踪的
22
- * cwd 解析——两个基准不同,转发它等于拿错基准铸 canonical key,而**错误的 allow 比没有门更糟**。
23
- * 相对形 / `~` / 盘符形因此照旧 err ⇒ `canonicalizeTarget` 失败 ⇒ 写门 `ask`(fail-closed)。
24
- * 「不猜沙箱工作目录」这条公理不随真 env 到场而松。
25
- * 2. **slot 空 = fail-closed 且响亮**:转发面返回错误 ⇒ core 把 `exists` 报错读成
26
- * `unresolvedSymlink` ⇒ `createSensitivePathPolicy` 判 deny、写门判 ask,折叠后 deny 恒赢。
27
- * 保护型缺席的失败方向必须与能力语义同向。留痕走结构化 `warn`(每实例一次)而**不**走
28
- * `observability/fail-open.ts`:那个词表按其模块头是 `F`(体验/缓存回退)与 `P-DEBT`(明知方向
29
- * 不对的保护型欠账)两类的准入面,本臂方向是**对的**(fail-closed),登记进去会把「已收口的
30
- * 保护臂」混进「待还的债」计数里。
31
- * 3. **slot 只认「当前活腿」**:同一 session 同时只有一条活腿(HTTP `conflict.session_active_run` +
32
- * core session acquire 双重串行化;委派子代另用一个 sessionId),所以同键改铸即顶替旧登记——
33
- * 那些旧登记来自 park-only 之类「不 destroy 也不 suspend」的终局,留着只会让 resume 腿失去真身裁决。
34
- *
35
- * ## slot 的键 = sessionId
36
- *
37
- * 工厂 ctx 只有 `{sessionId, taskId?, isolation?, parentCwd?}`(core `ExecutionEnvFactoryContext`),
38
- * 而 `/v1/runs` 会在 resolveSpec **之后**铸自己的持久 taskId 覆盖 `spec.taskId`,所以 taskId 在主路径
39
- * 上对不上——sessionId 是两侧都稳的那一个(per-task 镜像登记簿是同一个先例)。
40
- * 委派子任务(Agent / workflow 子代)拿的是**另一个** sessionId,所以父的 slot 不会被子代覆盖(「不许
41
- * 最后写赢」由此成立)。**残余面**:子代的写仍按**父**的 env 裁决——子代的 policy 就是父的那一个实例
42
- * (core `inheritedGateForChildren` 把它当 parentConstraint 继承),而 policy 收到的只有
43
- * `{toolName, args, toolCallId}`,读侧根本没有腿身份可键控。方向上它逼近真相而非远离(过渡形是「谁的盘
44
- * 都不看」),但有一条**新的松面**:父盘上一个名叫守卫段、真身良性的软链,会让子代在自己盘上指向真守卫段
45
- * 的同名路径拿到父盘的宽松答案。收口需要一条「按当前工具调用的 env 裁决」的引擎缝,属设计件;
46
- * 该面由 test/task-settings.test.ts 的「真身胜过名字」特征化钉机器可见。
47
- */
48
1
  import { posix } from "node:path";
49
2
  import { FileError, StubExecutionEnv, err, ok } from "@sema-agent/core";
50
- /** 哪些 lane 用本模块的代理裁决写门 —— `REMOTE_EXEC` 未设(进程内 host)与显式 `host` 之外的全部。
51
- * 与 resolve-spec 的 `hostSemanticsLane` 是同一判别式的两面,取值处**只此一个**(两处各写一份正是漂移的成因)。 */
52
3
  export function isSandboxPathAdjudicationLane(provider) {
53
4
  return provider !== undefined && provider !== "host";
54
5
  }
55
- /** 词法归一:纯字符串运算,折 `.` / `..` / 重复分隔符,不碰 fs。
56
- * `posix.normalize` 把 `..` 在根部截断(`/../x` → `/x`),与「沙箱根之上没有东西」的语义一致。
57
- * 尾部分隔符统一剥掉(根 `/` 除外),让同一目标只有一个 key ——前缀判域靠的就是 key 的唯一性。
58
- * (core 内部有同形的 `normalizeAbsPathLexically`,但未从包根导出;此处是 Node 标准库的等价运算,
59
- * 不是它的抄本——若日后 core 导出,这里应改为直接复用。) */
60
6
  function normalizeAbsolutePathLexically(path) {
61
7
  const collapsed = posix.normalize(path.replace(/^\/+/, "/"));
62
8
  return collapsed.length > 1 ? collapsed.replace(/\/+$/, "") : collapsed;
63
9
  }
64
- /** 同时可登记的 session 数上限。到顶后先清死条目,仍满则**拒收新键、绝不驱逐在场者**:被拒的 session
65
- * 只是拿不到真 env(⇒ fail-closed),而驱逐一个在场者会让一个正在跑的 task 的写门突然失去真身裁决。 */
66
10
  const MAX_BOUND_SESSIONS = 4096;
67
- /**
68
- * per-session 的真 env 登记簿 —— 工厂装饰器写,写门代理读。
69
- *
70
- * **同键改铸 = 顶替**(不排队、不判歧义)。依据是一条被两道门执法的不变量:一个 session 同时只有一条
71
- * 活腿(HTTP 层 `conflict.session_active_run` + core 的 session acquire),而委派子代用的是**另一个**
72
- * sessionId。所以工厂为某 session 铸出新 env 的那一刻,同键上还留着的登记按构造已经是死腿——最典型的
73
- * 产地是 **park-only 车道**(非 suspendable 的远程 env:durable park 既不 `suspendVM` 也不 `destroy`,
74
- * core 的 `teardownOwnedEnv` 在 checkpoint token 在场时整条跳过)。留着它只会让 resume 腿整轮拿不到
75
- * 真 env(写门恒 fail-closed);顶替按**实例身份**收口,被顶掉那一方迟到的注销全部落空,不误伤后继。
76
- */
77
11
  export class SandboxPathEnvSlots {
78
- /** **弱持有**:登记簿只在「别人还用着这个 env」期间指向它。跑着的任务由 core 强持有(`prepared.ownedEnv`),
79
- * 所以在场腿的裁决永远解得出;而一条 park 后再没人来 resume 的死腿,其 env 一旦无人引用即可被回收,
80
- * 条目在下一次清扫时消失。强持有会把它们连同各自的 adapter 连接一起钉死到进程结束,并在 4096 条之后
81
- * 让**每一个**新 session 拿不到真 env(守卫集下 = 每一次结构化写都 deny)—— 那是一道会自己关上的门。 */
82
12
  bySession = new Map();
83
- /** 登记一个刚铸出的 env(同键顶替),返回**幂等**的注销闭包;到达上限而拒收时返回 `undefined`。
84
- * 注销按**实例身份**摘除,所以一个迟到的 destroy 不会误伤同键的后继 env。 */
85
13
  bind(sessionId, env) {
86
14
  if (!this.bySession.has(sessionId) && this.bySession.size >= MAX_BOUND_SESSIONS) {
87
15
  this.sweepCollected();
@@ -102,46 +30,21 @@ export class SandboxPathEnvSlots {
102
30
  const env = ref?.deref();
103
31
  if (env === undefined) {
104
32
  if (ref !== undefined)
105
- this.bySession.delete(sessionId); // 顺手收掉已回收的条目
33
+ this.bySession.delete(sessionId);
106
34
  return { ok: false, reason: "unbound" };
107
35
  }
108
36
  return { ok: true, env };
109
37
  }
110
- /** 登记簿规模(上限行为的可观测面;测试与运维探针用)。含尚未清扫的死条目。 */
111
38
  get boundSessions() {
112
39
  return this.bySession.size;
113
40
  }
114
- /** 清掉 env 已被回收的条目。只在触到上限时跑一遍(O(n) 的代价换掉一次拒收)。 */
115
41
  sweepCollected() {
116
42
  for (const [sid, ref] of this.bySession)
117
43
  if (ref.deref() === undefined)
118
44
  this.bySession.delete(sid);
119
45
  }
120
46
  }
121
- /** 进程内唯一的登记簿:工厂装饰器(boot/execution-env.ts)与写门 wiring(boot/resolve-spec.ts)分处
122
- * 装配链两端,而中间的 core 只肯传 `ExecutionEnvFactoryContext`——两端共享同一个实例是它们唯一的会合点。
123
- * 测试要隔离时自建 {@link SandboxPathEnvSlots} 实例注入即可(两个消费点都收可选参)。 */
124
47
  export const sandboxPathEnvSlots = new SandboxPathEnvSlots();
125
- /**
126
- * 装饰工厂:每铸出一个 env 就按 `ctx.sessionId` 登记(同键顶替,见 {@link SandboxPathEnvSlots}),
127
- * 终态时注销。
128
- *
129
- * 挂**最外层**(装配链尾):内层装饰器可能**换掉**env 实例(worktree 隔离的 `rootEnvAt`)或改写它的
130
- * 方法(scratchpad 的 exec/canonicalPath 前置),写门要裁决的是 core 最终拿到手的那一个。
131
- *
132
- * 生命周期三面(顺序即语义):
133
- * · `destroy` ⇒ 注销。
134
- * · `suspendVM` 成功 ⇒ 注销。core 在挂起时**跳过** destroy(runtask 留着 env 做快照),所以这是可挂起
135
- * 车道唯一的及时腾位点:挂起期该 session 没有活腿,登记留着只是白占登记簿容量。
136
- * · `resumeVM` 成功 ⇒ **重新登记**。挂起并不必然终结这一轮:`commitSuspendSaga` 的 checkpoint 写失败
137
- * 臂会 `resumeVM` + `postResumeInit` 把**同一个实例**复活,然后作废本次挂起让 run 继续跑
138
- * (core dist 亲读)。只注销不复登记,一次瞬时 checkpoint 故障就会让这条 run 之后每一次写都失去真身
139
- * 裁决(方向 fail-closed,但整轮写面被毒死)。复登记幂等:仍在场时是 no-op,所以正常 resume 腿
140
- * (新 env 在工厂处已登记、随后被 core `resumeVM` 复原快照)照旧不动;后继腿已接管该 session 时也不抢回。
141
- *
142
- * 两个终态面都缺席的 env(五条沙箱 adapter 都不是这一形)照样登记——正确性优先于回收:不登记等于
143
- * 让那条 lane 的写门恒 fail-closed。回收兜底=登记簿自己的上限。
144
- */
145
48
  export function withSandboxPathEnvSlot(factory, slots, logger) {
146
49
  return async (ctx) => {
147
50
  const env = await factory(ctx);
@@ -150,7 +53,6 @@ export function withSandboxPathEnvSlot(factory, slots, logger) {
150
53
  const sessionId = ctx.sessionId;
151
54
  let release = slots.bind(sessionId, env);
152
55
  if (release === undefined) {
153
- // 上限拒收:该 task 的写门会落 fail-closed 而不是拿别人的盘裁决,但运维得知道为什么。
154
56
  logger?.warn?.("sandbox_path_env_slot_at_capacity", { sessionId, boundSessions: slots.boundSessions });
155
57
  return env;
156
58
  }
@@ -160,9 +62,9 @@ export function withSandboxPathEnvSlot(factory, slots, logger) {
160
62
  };
161
63
  const revive = () => {
162
64
  if (release !== undefined)
163
- return; // 仍在场:复活是 no-op,绝不重复登记
65
+ return;
164
66
  if (slots.resolve(sessionId).ok)
165
- return; // 后继腿已接管这个 session:一个迟到的复活绝不把它顶掉
67
+ return;
166
68
  release = slots.bind(sessionId, env);
167
69
  if (release === undefined)
168
70
  logger?.warn?.("sandbox_path_env_slot_at_capacity", { sessionId, boundSessions: slots.boundSessions });
@@ -197,18 +99,10 @@ export function withSandboxPathEnvSlot(factory, slots, logger) {
197
99
  return env;
198
100
  };
199
101
  }
200
- /**
201
- * 写门用的 ExecutionEnv 代理(见文件头)。fs **读**原语转发 slot 里的真 env,`absolutePath` 保留词法形,
202
- * 其余一切(写面 / shell 面 / listDir …)继承 `StubExecutionEnv` 的 `not_supported`。
203
- *
204
- * **继承而非逐一手写**是刻意的:core 日后给 `ExecutionEnv` 加必填面时,新面会随 `StubExecutionEnv`
205
- * 一起到位并保持同一个诚实答案,不会在这里留下一个悄悄编出来的假答案。
206
- */
207
102
  export class DeferredSandboxPathEnv extends StubExecutionEnv {
208
103
  sessionId;
209
104
  slots;
210
105
  logger;
211
- /** 每实例一次的留痕闸:裁决面每个写目标要走 3-4 次转发,逐次 warn 会把日志刷成噪声。 */
212
106
  warned = false;
213
107
  constructor(opts) {
214
108
  super();
@@ -216,7 +110,6 @@ export class DeferredSandboxPathEnv extends StubExecutionEnv {
216
110
  this.slots = opts.slots;
217
111
  this.logger = opts.logger;
218
112
  }
219
- /** 取真 env;取不到就地铸 fail-closed 错误(并留痕一次)。 */
220
113
  bound(path, face) {
221
114
  const found = this.slots.resolve(this.sessionId);
222
115
  if (found.ok)
@@ -235,16 +128,10 @@ export class DeferredSandboxPathEnv extends StubExecutionEnv {
235
128
  error: new FileError("not_supported", `no sandbox execution environment is bound for this task (${found.reason}) — the write gate cannot inspect the real filesystem`, path),
236
129
  };
237
130
  }
238
- /** 词法一面,**不**转发 —— 理由见文件头裁定 1。 */
239
131
  absolutePath(path) {
240
- // 含 NUL 的字符串不是合法 POSIX 路径(任何 fs 面都写不进去),但词法归一会照样给它铸出 canonical
241
- // key——`\0` 尾巴让守卫段 glob 失配,豁免会话下还能拿 allow。拒收让 canon 在豁免咨询**之前**就失败。
242
132
  if (path.includes("\u0000")) {
243
133
  return Promise.resolve(err(new FileError("not_supported", "this gate resolves paths lexically and rejects a path containing a NUL byte (not a representable POSIX path)", path)));
244
134
  }
245
- // POSIX 绝对形是沙箱 lane(全 Linux)上 hand 工具的书面契约形。相对路径 / `~` / `C:\…` 都要一个
246
- // 沙箱工作目录才能解析,而那个基准与工具真写处的基准不是同一个,不猜。
247
- // (反斜杠 UNC 形根本到不了这里:core 在 canonicalizeTarget 入口就短路了。)
248
135
  if (!path.startsWith("/")) {
249
136
  return Promise.resolve(err(new FileError("not_supported", "this gate cannot resolve a non-absolute path (the sandbox working directory is not the gate's to guess)", path)));
250
137
  }
@@ -262,9 +149,6 @@ export class DeferredSandboxPathEnv extends StubExecutionEnv {
262
149
  const b = this.bound(path, "fileInfo");
263
150
  return b.ok ? b.env.fileInfo(path, abortSignal) : b;
264
151
  }
265
- /** `readLink` 在 `ExecutionEnv` 上是**可选**面。本代理恒定义它,于是 core 永远走不到自己的
266
- * 「env 没有 readLink」腿——真 env 缺这一面时由此处答错误,落到 core 同一条 `unresolvedSymlink` 出口,
267
- * 裁决结果与那条腿完全一致。 */
268
152
  async readLink(path, abortSignal) {
269
153
  const b = this.bound(path, "readLink");
270
154
  if (!b.ok)