@sema-agent/server 7.138.0 → 7.140.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 (332) hide show
  1. package/MIGRATION.md +457 -7
  2. package/README.md +7 -4
  3. package/README.zh-CN.md +6 -4
  4. package/USAGE.md +91 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/runner.js +1 -1
  8. package/dist/adoption/sql.js +1 -1
  9. package/dist/adoption/wire.d.ts +3 -3
  10. package/dist/approval-card.d.ts +26 -26
  11. package/dist/bench/s1/arms.d.ts +1 -1
  12. package/dist/bench/s1/arms.js +1 -3
  13. package/dist/bench/s1/live-deps.js +3 -2
  14. package/dist/bench/s1/row.d.ts +5 -2
  15. package/dist/bench/s1/row.js +2 -22
  16. package/dist/bench/s1/run-firm.js +2 -2
  17. package/dist/boot/adoption.js +1 -1
  18. package/dist/boot/device-lane.d.ts +8 -3
  19. package/dist/boot/device-lane.js +7 -2
  20. package/dist/boot/execution-env.d.ts +4 -2
  21. package/dist/boot/execution-env.js +28 -34
  22. package/dist/boot/execution-lane.d.ts +9 -1
  23. package/dist/boot/execution-lane.js +6 -1
  24. package/dist/boot/governance-seams.d.ts +3 -2
  25. package/dist/boot/governance-seams.js +9 -11
  26. package/dist/boot/memory-boundary.d.ts +14 -51
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +15 -11
  29. package/dist/boot/memory-consolidation.js +25 -14
  30. package/dist/boot/resolve-spec.d.ts +4 -6
  31. package/dist/boot/resolve-spec.js +10 -13
  32. package/dist/boot/retention-lane.d.ts +5 -5
  33. package/dist/boot/retention-lane.js +0 -6
  34. package/dist/boot/runner-deps.d.ts +6 -4
  35. package/dist/boot/runner-deps.js +3 -1
  36. package/dist/boot/session-faces.d.ts +0 -3
  37. package/dist/boot/session-faces.js +15 -8
  38. package/dist/boot/stage-02-web-admin-pull.js +2 -2
  39. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  40. package/dist/boot/stage-03-store-backend.js +2 -3
  41. package/dist/boot/stage-04-stores.d.ts +2 -6
  42. package/dist/boot/stage-04-stores.js +5 -5
  43. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  44. package/dist/boot/stage-05-execution-env.js +2 -2
  45. package/dist/boot/stage-06-runners.d.ts +2 -6
  46. package/dist/boot/stage-06-runners.js +2 -3
  47. package/dist/boot/stage-07-capability-layer.d.ts +2 -6
  48. package/dist/boot/stage-08-reapers.d.ts +2 -6
  49. package/dist/boot/stage-09-leader.d.ts +2 -6
  50. package/dist/boot/stage-09-leader.js +1 -2
  51. package/dist/boot/stage-10-http-server.d.ts +2 -6
  52. package/dist/boot/stage-10-http-server.js +7 -6
  53. package/dist/boot/stores.d.ts +3 -6
  54. package/dist/boot/stores.js +27 -40
  55. package/dist/boot/task-list-lane.d.ts +1 -1
  56. package/dist/boot/task-list-lane.js +1 -1
  57. package/dist/boot/web-admin.js +37 -11
  58. package/dist/capabilities/execution-lane.d.ts +2 -2
  59. package/dist/capabilities/mcp-probe.js +1 -1
  60. package/dist/capabilities/scenarios.d.ts +1 -1
  61. package/dist/capability-facts.d.ts +15 -7
  62. package/dist/capability-facts.js +8 -3
  63. package/dist/config-catalog.js +9 -8
  64. package/dist/config-invariants.d.ts +23 -3
  65. package/dist/config-invariants.js +42 -0
  66. package/dist/config-provider.d.ts +33 -37
  67. package/dist/config-provider.js +51 -30
  68. package/dist/config-types.d.ts +21 -7
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +46 -16
  71. package/dist/delegated-session-owner.d.ts +4 -3
  72. package/dist/delegated-session-owner.js +1 -1
  73. package/dist/deployment-shape.d.ts +10 -9
  74. package/dist/deployment-shape.js +4 -4
  75. package/dist/device-enrollment.d.ts +41 -2
  76. package/dist/device-enrollment.js +27 -12
  77. package/dist/device-store.d.ts +58 -85
  78. package/dist/device-store.js +19 -49
  79. package/dist/device-ws-hub.js +2 -1
  80. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  81. package/dist/exec-cut.d.ts +60 -0
  82. package/dist/exec-cut.js +36 -0
  83. package/dist/execution-lane-caps.d.ts +115 -105
  84. package/dist/execution-lane-caps.js +76 -82
  85. package/dist/execution-placement.d.ts +259 -0
  86. package/dist/execution-placement.js +199 -0
  87. package/dist/field-conflict.d.ts +3 -2
  88. package/dist/field-conflict.js +1 -0
  89. package/dist/hosted-posture.d.ts +2 -2
  90. package/dist/http/admission.js +8 -19
  91. package/dist/http/checkpoint-ctx.d.ts +21 -12
  92. package/dist/http/checkpoint-ctx.js +4 -2
  93. package/dist/http/claimed-run.d.ts +1 -1
  94. package/dist/http/durable-run-leg.js +9 -5
  95. package/dist/http/execution-context.d.ts +218 -0
  96. package/dist/http/execution-context.js +330 -0
  97. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  98. package/dist/http/memory-compliance-failure-reply.js +5 -0
  99. package/dist/http/memory-layer-reply.d.ts +30 -0
  100. package/dist/http/memory-layer-reply.js +24 -0
  101. package/dist/http/resume-legs.js +63 -36
  102. package/dist/http/retired-service-deps.d.ts +1 -1
  103. package/dist/http/retired-service-deps.js +1 -1
  104. package/dist/http/route-ctx.d.ts +14 -8
  105. package/dist/http/routes/adoption.js +1 -1
  106. package/dist/http/routes/background.d.ts +29 -1
  107. package/dist/http/routes/background.js +29 -2
  108. package/dist/http/routes/devices.d.ts +13 -19
  109. package/dist/http/routes/devices.js +21 -26
  110. package/dist/http/routes/memory-bundle.js +3 -0
  111. package/dist/http/routes/memory-compliance.js +8 -1
  112. package/dist/http/routes/memory-consolidation.js +12 -1
  113. package/dist/http/routes/memory-origin.js +14 -2
  114. package/dist/http/routes/memory-policy.js +5 -0
  115. package/dist/http/routes/session-sync.js +110 -15
  116. package/dist/http/routes/sessions.d.ts +6 -0
  117. package/dist/http/routes/sessions.js +143 -22
  118. package/dist/http/routes/side-query.d.ts +12 -4
  119. package/dist/http/routes/side-query.js +4 -5
  120. package/dist/http/routes/tasks.js +41 -33
  121. package/dist/http/server.d.ts +40 -44
  122. package/dist/http/server.js +13 -2
  123. package/dist/http/session-inherit-env.d.ts +3 -4
  124. package/dist/http/session-inherit-env.js +1 -1
  125. package/dist/http/wire-types.d.ts +8 -12
  126. package/dist/identity-source.d.ts +1 -1
  127. package/dist/identity-source.js +1 -1
  128. package/dist/index.d.ts +1 -0
  129. package/dist/lane-home-probe.d.ts +68 -42
  130. package/dist/lane-home-probe.js +53 -30
  131. package/dist/leader/wire.js +5 -4
  132. package/dist/memory-bundle-engine.d.ts +16 -9
  133. package/dist/memory-bundle-engine.js +29 -6
  134. package/dist/memory-operator-faces.d.ts +85 -30
  135. package/dist/memory-operator-faces.js +136 -30
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +29 -5
  141. package/dist/memory-scope.js +16 -4
  142. package/dist/memory-sync-client.d.ts +70 -18
  143. package/dist/memory-sync-client.js +215 -117
  144. package/dist/memory-sync.d.ts +1 -1
  145. package/dist/memory-sync.js +3 -2
  146. package/dist/observability/cost-taxonomy.d.ts +2 -2
  147. package/dist/observability/fail-open.d.ts +12 -0
  148. package/dist/observability/fail-open.js +12 -0
  149. package/dist/observability/run-terminal.js +0 -1
  150. package/dist/orchestration/workflow-notify-journal.js +4 -4
  151. package/dist/org-memory-admission.d.ts +4 -3
  152. package/dist/org-memory-admission.js +6 -6
  153. package/dist/parked-decide.d.ts +34 -9
  154. package/dist/parked-decide.js +12 -19
  155. package/dist/parked-revive-surface.d.ts +12 -15
  156. package/dist/parked-revive-surface.js +3 -6
  157. package/dist/peer-seat.d.ts +13 -7
  158. package/dist/peer-seat.js +37 -3
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  161. package/dist/plugins/approval-ask-store-file.js +11 -0
  162. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  163. package/dist/plugins/approval-ask-store-memory.js +12 -0
  164. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  165. package/dist/plugins/approval-ask-store-sql.js +44 -26
  166. package/dist/plugins/approval-nonce-store.js +4 -4
  167. package/dist/plugins/blob-backend.d.ts +4 -4
  168. package/dist/plugins/blob-backend.js +7 -7
  169. package/dist/plugins/breaker-state-sql.js +2 -2
  170. package/dist/plugins/caching-session-store.d.ts +6 -2
  171. package/dist/plugins/caching-session-store.js +9 -3
  172. package/dist/plugins/checkpoint-index-columns.js +2 -2
  173. package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
  174. package/dist/plugins/checkpoint-store-sql.js +21 -23
  175. package/dist/plugins/device-store-sql.d.ts +25 -51
  176. package/dist/plugins/device-store-sql.js +68 -122
  177. package/dist/plugins/ensure-index.d.ts +1 -2
  178. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  179. package/dist/plugins/execution-placement-sql.js +223 -0
  180. package/dist/plugins/file-history-store-sql.d.ts +9 -8
  181. package/dist/plugins/file-history-store-sql.js +9 -9
  182. package/dist/plugins/file-run-store.d.ts +6 -1
  183. package/dist/plugins/file-run-store.js +4 -0
  184. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  185. package/dist/plugins/fork-routing-session-store.js +16 -0
  186. package/dist/plugins/image-bake-store-sql.js +9 -9
  187. package/dist/plugins/image-index-sql.js +11 -11
  188. package/dist/plugins/leader-run-store-sql.js +4 -4
  189. package/dist/plugins/local-session-store.d.ts +149 -50
  190. package/dist/plugins/local-session-store.js +382 -107
  191. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  192. package/dist/plugins/mailbox-store-sql.js +4 -4
  193. package/dist/plugins/memory-engine-tidb.js +5 -5
  194. package/dist/plugins/memory-key-guards.d.ts +4 -0
  195. package/dist/plugins/memory-key-guards.js +1 -0
  196. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  197. package/dist/plugins/memory-run-store.d.ts +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +117 -8
  200. package/dist/plugins/one-time-migrations.js +408 -38
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  203. package/dist/plugins/outcome-ledger-sql.js +2 -2
  204. package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
  205. package/dist/plugins/permission-rule-store-sql.js +29 -32
  206. package/dist/plugins/pg-pool.d.ts +7 -3
  207. package/dist/plugins/pg-session-storage.d.ts +31 -20
  208. package/dist/plugins/pg-session-storage.js +120 -52
  209. package/dist/plugins/remote-env-adb.d.ts +18 -19
  210. package/dist/plugins/remote-env-adb.js +91 -66
  211. package/dist/plugins/remote-env-device.d.ts +23 -31
  212. package/dist/plugins/remote-env-device.js +19 -17
  213. package/dist/plugins/remote-env-e2b.d.ts +27 -21
  214. package/dist/plugins/remote-env-e2b.js +91 -45
  215. package/dist/plugins/remote-env-file-error.d.ts +3 -1
  216. package/dist/plugins/remote-env-file-error.js +9 -3
  217. package/dist/plugins/remote-env-host.d.ts +31 -34
  218. package/dist/plugins/remote-env-host.js +91 -72
  219. package/dist/plugins/remote-env-k8s.d.ts +13 -29
  220. package/dist/plugins/remote-env-k8s.js +63 -55
  221. package/dist/plugins/remote-env-local-docker.d.ts +24 -12
  222. package/dist/plugins/remote-env-local-docker.js +49 -61
  223. package/dist/plugins/remote-env-ssh.d.ts +11 -19
  224. package/dist/plugins/remote-env-ssh.js +74 -57
  225. package/dist/plugins/remote-shell.d.ts +100 -3
  226. package/dist/plugins/remote-shell.js +166 -2
  227. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  228. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  229. package/dist/plugins/retention-lane-store-sql.js +2 -2
  230. package/dist/plugins/retention-store-sql.d.ts +10 -7
  231. package/dist/plugins/retention-store-sql.js +13 -7
  232. package/dist/plugins/run-store-sql.d.ts +13 -3
  233. package/dist/plugins/run-store-sql.js +20 -15
  234. package/dist/plugins/schema-consistency.d.ts +21 -11
  235. package/dist/plugins/schema-consistency.js +15 -9
  236. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  237. package/dist/plugins/schema-drift-sentence.js +38 -0
  238. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  239. package/dist/plugins/schema-ensure-legs.js +2 -2
  240. package/dist/plugins/schema-live-columns.d.ts +1 -1
  241. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  242. package/dist/plugins/session-fork.d.ts +54 -0
  243. package/dist/plugins/session-fork.js +38 -1
  244. package/dist/plugins/session-row-sql.d.ts +22 -0
  245. package/dist/plugins/session-row-sql.js +81 -0
  246. package/dist/plugins/session-rules-column.d.ts +1 -1
  247. package/dist/plugins/session-rules-column.js +1 -1
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  250. package/dist/plugins/session-workspace-cell.js +1 -1
  251. package/dist/plugins/shared-memory-store-sql.js +2 -2
  252. package/dist/plugins/sql-driver.d.ts +10 -5
  253. package/dist/plugins/sql-driver.js +6 -6
  254. package/dist/plugins/sql-errors.js +1 -1
  255. package/dist/plugins/store-backend.d.ts +5 -7
  256. package/dist/plugins/store-backend.js +23 -11
  257. package/dist/plugins/store-contracts.d.ts +25 -5
  258. package/dist/plugins/task-list-store-sql.js +11 -11
  259. package/dist/plugins/tidb-pool.d.ts +12 -7
  260. package/dist/plugins/tidb-pool.js +13 -7
  261. package/dist/plugins/tidb-session-storage.js +4 -4
  262. package/dist/plugins/tidb-session-store.d.ts +57 -26
  263. package/dist/plugins/tidb-session-store.js +127 -59
  264. package/dist/plugins/tool-result-store-sql.d.ts +5 -4
  265. package/dist/plugins/tool-result-store-sql.js +21 -13
  266. package/dist/plugins/user-input-ledger-sql.js +2 -2
  267. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  268. package/dist/plugins/workflow-run-store-sql.js +19 -19
  269. package/dist/plugins/write-behind-counter.d.ts +2 -3
  270. package/dist/plugins/write-behind-counter.js +5 -5
  271. package/dist/project-memory.d.ts +9 -15
  272. package/dist/project-memory.js +5 -4
  273. package/dist/request-key-closure.js +1 -1
  274. package/dist/request-value-closure.d.ts +6 -7
  275. package/dist/request-value-closure.js +1 -3
  276. package/dist/resource-suspend.d.ts +14 -12
  277. package/dist/resource-suspend.js +1 -1
  278. package/dist/resource-window.d.ts +4 -3
  279. package/dist/run-local.d.ts +6 -0
  280. package/dist/run-local.js +34 -35
  281. package/dist/runs.d.ts +10 -5
  282. package/dist/runs.js +52 -66
  283. package/dist/security.d.ts +38 -12
  284. package/dist/security.js +8 -2
  285. package/dist/server-secret-env.d.ts +9 -5
  286. package/dist/session-children-retire.d.ts +27 -9
  287. package/dist/session-children-retire.js +9 -5
  288. package/dist/session-incarnation.d.ts +20 -10
  289. package/dist/session-incarnation.js +5 -3
  290. package/dist/session-policy-wire.d.ts +1 -1
  291. package/dist/session-row.d.ts +114 -5
  292. package/dist/session-row.js +55 -5
  293. package/dist/session-titler.d.ts +2 -2
  294. package/dist/sql-engine-posture.d.ts +1 -1
  295. package/dist/sql-engine-posture.js +1 -1
  296. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  297. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  298. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  299. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  300. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  301. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  302. package/dist/task-a2a.d.ts +1 -1
  303. package/dist/task-a2a.js +5 -6
  304. package/dist/task-cwd.d.ts +6 -1
  305. package/dist/task-mcp.d.ts +22 -9
  306. package/dist/task-mcp.js +17 -9
  307. package/dist/terminal.d.ts +27 -0
  308. package/dist/terminal.js +6 -0
  309. package/dist/tool-approval.d.ts +6 -0
  310. package/dist/tool-approval.js +8 -1
  311. package/dist/trace/project.d.ts +1 -0
  312. package/dist/trace/project.js +1 -0
  313. package/dist/trace/redact.d.ts +3 -1
  314. package/dist/trace/redact.js +80 -28
  315. package/dist/trace/self-minted-wire.d.ts +169 -2
  316. package/dist/trace/self-minted-wire.js +54 -1
  317. package/dist/url-refusal.d.ts +1 -1
  318. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  319. package/dist/web-admin-client/apply-effective.js +20 -10
  320. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  321. package/dist/web-admin-client/candidate-gate.js +1 -1
  322. package/dist/web-admin-client/http-client.d.ts +5 -4
  323. package/dist/web-admin-client/http-client.js +2 -2
  324. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  325. package/dist/web-admin-client/read-warnings.js +5 -9
  326. package/dist/web-admin-client/skills-mcp.js +3 -2
  327. package/dist/web-admin-client/types.d.ts +6 -0
  328. package/package.json +3 -3
  329. package/wire-changes/server@7.139.0.json +348 -0
  330. package/wire-changes/server@7.140.0.json +184 -0
  331. package/dist/http/device-placement.d.ts +0 -175
  332. package/dist/http/device-placement.js +0 -145
package/dist/runs.d.ts CHANGED
@@ -263,10 +263,14 @@ export declare function graftParkToolCallId<T>(result: T, cs: ParkCheckpointRead
263
263
  * Preemption (POST /v1/assistant/tasks/:id/preempt, [ref] seam #2): a SECOND, INDEPENDENT controller
264
264
  * (`preemptCtrl`, registered in `preemptable`) carries the scheduler's graceful "yield this task" — when raised,
265
265
  * core durably SUSPENDS the task at the next clean turn boundary (gate resource_limit, reason "preempt") so it is
266
- * RESUMABLE, the opposite of cancel's kill. It is wired ONLY on the plain-stream path: the verify/cascade
267
- * branches finalize with `setTerminal` unconditionally (no `setSuspended`), so a preempt-suspend there would write
268
- * a non-terminal "suspended" as terminal and release the session lock — and preempt's contract is graceful, not a
269
- * failure. Cross-replica via the `preempt_requested` flag (same heartbeat tick). Eligibility (resourceSuspend +
266
+ * RESUMABLE, the opposite of cancel's kill. It is wired ONLY on the plain-stream path. A cascade cannot honor it: a
267
+ * rung's pause lands on the rung's own engine-minted session, which the host's resume chain cannot reach (the
268
+ * host-boundary judge settles it terminal). A verify task carries the resource-suspend opt-in (a budget / turns /
269
+ * walltime limit parks its implementation leg or fix round), but its FIRST slice here is not wired to the preempt
270
+ * signal (`preemptEligible` below is false for verify, so the preempt endpoint answers a no-op for a verify run this
271
+ * replica owns); a RESUMED verify leg is wired (`resume-legs.ts` registers every resume leg in `preemptableRuns` and
272
+ * threads `preemptSignal` into `resumeWithVerification`). Cross-replica via the `preempt_requested` flag (same heartbeat
273
+ * tick). Eligibility (resourceSuspend +
270
274
  * durable store + durable tool-results + remote env) is core's call: an ineligible task's preemptSignal no-ops.
271
275
  *
272
276
  * Steering (POST /v1/runs/:id/steer, [ref]): the live core `TaskStream` is registered in `steerable` so the
@@ -332,7 +336,8 @@ approval?: {
332
336
  streamApprovalOn: boolean;
333
337
  windowMs: number | undefined;
334
338
  windowMarginMs: number;
335
- declaredFamilies: ReadonlySet<ApprovalFrameFamily>;
339
+ declaredFamilies: ReadonlySet<ApprovalFrameFamily>; /** 出处带车道:这条执行上下文的车道(装配点 `durable-run-leg.ts` 写 `ctx.lane`)。 */
340
+ executionLane: import("./execution-lane-caps.js").ExecutionLane;
336
341
  },
337
342
  /** [ref]:park 投影腿的 checkpoint 读口(`deps.checkpointStore`)。在场时,durable park 的
338
343
  * `suspended` 事件带 `toolCallId`(graft 见 {@link parkToolCallId});缺席=旧字节逐字不变。 */
package/dist/runs.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { runWithVerification, runCascade, defaultTaskRegistry, taskNotificationDedupKey, DURABLE_AGENT_HANDLE_RE } from "@sema-agent/core";
2
- import { failureCodeOf, sealResultForExit, outwardPlane } from "./terminal.js";
2
+ import { atHostBoundary, failureCodeOf, sealResultForExit, outwardPlane } from "./terminal.js";
3
3
  import { NOT_IN_ROSTER, entryProblemsFacts } from "./entry-problems.js";
4
4
  import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
5
5
  import { TOOL_DECLARATION_HTTP_STATUS } from "./tool-declaration-http-status.js";
@@ -19,6 +19,7 @@ import { createApprovalCardEmitter, resolveApprovalLeg } from "./tool-approval.j
19
19
  import { fleetRunResiduals, isFleetAgentTerminalNotification } from "./fleet/fleet-bus.js";
20
20
  import { defaultSubagentTailBus } from "./fleet/subagent-tail-bus.js";
21
21
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "./orchestration/workflow-completion-inbox.js";
22
+ import { isParkLedgerEventType } from "./trace/ledger-events.js";
22
23
  import { recordFailOpen } from "./observability/fail-open.js";
23
24
  import { errorText as errText } from "./observability/err-text.js";
24
25
  import { LIVENESS_HEARTBEAT_MS } from "./config-invariants.js";
@@ -88,6 +89,7 @@ const PREPARE_REFUSAL_HTTP_STATUS = {
88
89
  "config.project_identity_mismatch": 409,
89
90
  "config.memory_project_marker": 409,
90
91
  "config.memory_project_marker_nested": 409,
92
+ "config.read_deny_floor_unkept": 409,
91
93
  };
92
94
  export function resumeAtHttpStatus(result) {
93
95
  const code = failureCodeOf(result.terminal);
@@ -296,11 +298,12 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
296
298
  if (approval) {
297
299
  const coordinator = approval.coordinator;
298
300
  spec.onAsk = approvalLeg.unattended
299
- ? coordinator.boundUnattendedAsk({ owner: elicitOwner ?? owner, taskId, ...(spec.sessionId ? { sessionId: spec.sessionId } : {}) }, approvalLeg.unattended)
301
+ ? coordinator.boundUnattendedAsk({ owner: elicitOwner ?? owner, taskId, ...(spec.sessionId ? { sessionId: spec.sessionId } : {}), executionLane: approval.executionLane }, approvalLeg.unattended)
300
302
  : coordinator.boundAsk({
301
303
  owner: elicitOwner ?? owner,
302
304
  taskId,
303
305
  ...(spec.sessionId ? { sessionId: spec.sessionId } : {}),
306
+ executionLane: approval.executionLane,
304
307
  ...(approvalLeg.active ? { legKey: "", ...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}) } : {}),
305
308
  });
306
309
  }
@@ -325,14 +328,38 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
325
328
  let legLive = true;
326
329
  const notifiedKeys = new NotifiedKeys();
327
330
  const subagentHandleEvictions = [];
328
- const parkNeedsReview = async (res) => {
329
- await sink.appendParked("needs_review", res);
331
+ const parkRowWrite = {
332
+ suspended: (sealed) => runStore.setSuspended(taskId, sealed),
333
+ needs_review: (sealed) => runStore.setNeedsReview(taskId, sealed),
334
+ };
335
+ const parkLeg = async (word, res) => {
336
+ const grafted = await graftParkToolCallId(res, parkCheckpointReader);
337
+ await sink.appendParked(word, grafted);
330
338
  await flushModelUsage();
331
- const sealed = sealResultForExit(res);
332
- reached = { kind: "needs_review", result: sealed };
333
- await runStore.setNeedsReview(taskId, sealed);
339
+ const sealed = sealResultForExit(grafted);
340
+ reached = { kind: word, result: sealed };
341
+ await parkRowWrite[word](sealed);
342
+ acceptance?.settle(sealed);
334
343
  metrics?.addGauge("runs_active", -1);
335
- metrics?.inc("tasks_total", { status: "needs_review" });
344
+ metrics?.inc("tasks_total", { status: word });
345
+ };
346
+ const settleLegResult = async (engineResult) => {
347
+ const result = atHostBoundary(engineResult, spec.sessionId);
348
+ const parkWord = result.terminal.kind === "paused" ? outwardPlane(result.terminal).status : undefined;
349
+ if (parkWord !== undefined && isParkLedgerEventType(parkWord)) {
350
+ await parkLeg(parkWord, result);
351
+ return;
352
+ }
353
+ const settled = causeAtDone !== undefined ? cancelledResult(causeAtDone, result) : result;
354
+ settledAsCancel = failureCodeOf(settled.terminal) === CANCELLED_CODE;
355
+ const safe = await withModelUsage(sealResultForExit(settled));
356
+ await sink.appendDone(safe);
357
+ reached = { kind: "done", result: safe };
358
+ const plane = outwardPlane(safe.terminal);
359
+ await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
360
+ await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
361
+ acceptance?.settle(safe);
362
+ recordDone(safe, settledAsCancel ? CANCELLED_CODE : plane.status);
336
363
  };
337
364
  const instructionLeg = createInstructionSelectionLeg();
338
365
  const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), legSections: () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(spec) }) });
@@ -418,32 +445,18 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
418
445
  void sink.appendStatus(e).catch((err) => completionDiagLog?.("status_event_append_failed", { route: "bg-run-composite", taskId, err: err instanceof Error ? err.message : String(err) }));
419
446
  },
420
447
  };
421
- if (verify !== undefined) {
422
- const result = await runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, { ...fwdInternals, ...statusSeat });
423
- await flush();
424
- await flushPromptManifest();
425
- const safe = await withModelUsage(sealResultForExit(result));
426
- await append("done", { result: safe });
427
- reached = { kind: "done", result: safe };
428
- const plane = outwardPlane(safe.terminal);
429
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
430
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
431
- acceptance?.settle(safe);
432
- recordDone(safe, plane.status);
433
- return;
434
- }
435
- if (cascade !== undefined) {
436
- const result = await runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, { ...fwdInternals, ...statusSeat });
448
+ const compositeLeg = verify !== undefined
449
+ ? () => runWithVerification(runner, { ...spec, taskId, signal: cancelCtrl.signal }, verify, { ...fwdInternals, ...statusSeat })
450
+ : cascade !== undefined
451
+ ? () => runCascade(runner, { ...spec, taskId, signal: cancelCtrl.signal }, cascade, { ...fwdInternals, ...statusSeat })
452
+ : undefined;
453
+ if (compositeLeg !== undefined) {
454
+ const result = await compositeLeg();
455
+ observedDone = result;
456
+ causeAtDone = cancelCtrl.cause();
437
457
  await flush();
438
458
  await flushPromptManifest();
439
- const safe = await withModelUsage(sealResultForExit(result));
440
- await append("done", { result: safe });
441
- reached = { kind: "done", result: safe };
442
- const plane = outwardPlane(safe.terminal);
443
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
444
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
445
- acceptance?.settle(safe);
446
- recordDone(safe, plane.status);
459
+ await settleLegResult(result);
447
460
  return;
448
461
  }
449
462
  const stream = runner.runTaskStream({ ...spec, taskId, signal: cancelCtrl.signal, preemptSignal: preemptCtrl.signal }, undefined, fwdInternals);
@@ -478,33 +491,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
478
491
  }
479
492
  if (ev.type === "done") {
480
493
  await flush();
481
- const parkStatus = ev.result.terminal.kind === "paused" ? outwardPlane(ev.result.terminal).status : undefined;
482
- if (parkStatus === "suspended") {
483
- const grafted = await graftParkToolCallId(ev.result, parkCheckpointReader);
484
- await sink.appendParked("suspended", grafted);
485
- await flushModelUsage();
486
- const sealed = sealResultForExit(grafted);
487
- reached = { kind: "suspended", result: sealed };
488
- await runStore.setSuspended(taskId, sealed);
489
- metrics?.addGauge("runs_active", -1);
490
- metrics?.inc("tasks_total", { status: "suspended" });
491
- continue;
492
- }
493
- if (parkStatus === "needs_review") {
494
- await parkNeedsReview(ev.result);
495
- continue;
496
- }
497
- {
498
- const settled = causeAtDone !== undefined ? cancelledResult(causeAtDone, ev.result) : ev.result;
499
- settledAsCancel = failureCodeOf(settled.terminal) === CANCELLED_CODE;
500
- const safe = await withModelUsage(sealResultForExit(settled));
501
- await sink.appendDone(safe);
502
- reached = { kind: "done", result: safe };
503
- const plane = outwardPlane(safe.terminal);
504
- await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
505
- await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
506
- recordDone(safe, failureCodeOf(safe.terminal) === CANCELLED_CODE ? CANCELLED_CODE : plane.status);
507
- }
494
+ await settleLegResult(ev.result);
508
495
  continue;
509
496
  }
510
497
  await sink.onEvent(ev);
@@ -531,6 +518,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
531
518
  owner: elicitOwner ?? owner,
532
519
  abortSignal: cancelCtrl.signal,
533
520
  ...(spec.sessionId ? { sessionId: spec.sessionId } : {}),
521
+ executionLane: approval.executionLane,
534
522
  emitCard: createApprovalCardEmitter({ appendDurable: (f) => approvalEmit(f) }),
535
523
  emitRevoke: (f) => approvalEmit(f),
536
524
  legKey: "",
@@ -579,11 +567,9 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
579
567
  await releaseKeyIfZeroWorkRefusal(runStore, taskId, reached.result);
580
568
  acceptance?.settle(reached.result);
581
569
  }
582
- else if (reached?.kind === "suspended") {
583
- await runStore.setSuspended(taskId, reached.result);
584
- }
585
- else if (reached?.kind === "needs_review") {
586
- await runStore.setNeedsReview(taskId, reached.result);
570
+ else if (reached !== undefined) {
571
+ await parkRowWrite[reached.kind](reached.result);
572
+ acceptance?.settle(reached.result);
587
573
  }
588
574
  else if (thrownCancelCause !== undefined && observedDone === undefined) {
589
575
  const c = cancelledResult(thrownCancelCause);
@@ -612,9 +598,9 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
612
598
  }
613
599
  if (reached?.kind === "done")
614
600
  recordDone(reached.result, failureCodeOf(reached.result.terminal) === CANCELLED_CODE ? CANCELLED_CODE : outwardPlane(reached.result.terminal).status);
615
- else if (reached?.kind === "suspended") {
601
+ else if (reached !== undefined) {
616
602
  metrics?.addGauge("runs_active", -1);
617
- metrics?.inc("tasks_total", { status: "suspended" });
603
+ metrics?.inc("tasks_total", { status: reached.kind });
618
604
  }
619
605
  else if (settledAsCancel) {
620
606
  recordDone(null, CANCELLED_CODE);
@@ -1,11 +1,12 @@
1
1
  import type { IncomingMessage } from "node:http";
2
- import { type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
2
+ import { type SessionHead, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
3
3
  import type { ServiceConfig } from "./config-types.js";
4
4
  import { type DeploymentShapeInput } from "./deployment-shape.js";
5
5
  import type { TaskRequestBody } from "./http/wire-types.js";
6
6
  import type { StagingHandle } from "./session-sync-kernel.js";
7
- import { type SessionFace } from "./session-incarnation.js";
8
- import type { SessionRowPatch } from "./session-row.js";
7
+ import type { ClientSessionRowWrite, ReplaySessionRowWrite, ReplaySessionRowWriteOutcome, SessionRowPatch, SessionRowWriteOutcome } from "./session-row.js";
8
+ import type { ExecutionPlacementSeat } from "./execution-placement.js";
9
+ import type { ForkTarget, ForkToOutcome } from "./plugins/session-fork.js";
9
10
  import { type OwnerlessClaim } from "./delegated-session-owner.js";
10
11
  /** One row of the session-list projection (CC /resume picker) — the durable session abstraction's enumeration
11
12
  * output (§0.5). Mirrors {@link RunStore}'s shipped `SessionSummary` shape so the wire response is unchanged. */
@@ -32,6 +33,19 @@ export interface SessionListItem {
32
33
  /** Pinned by the user (`PATCH /v1/sessions/:id`). ALWAYS present — `false` by default (a session's state, not a capability);
33
34
  * a lister face with no session_meta answers `false` (nothing there can pin it). */
34
35
  pinned: boolean;
36
+ /** The row tag (`src/session-row.ts` `sessionRowTagOf`): an opaque one-way digest of this occupant's writable cells — the precondition a
37
+ * `PATCH /v1/sessions/:id` carries back as `expectedRowTag`. Present-iff the row can be written conditionally (the store has the row write
38
+ * primitive ∧ the row has an occupant token); additive key, passed through unprojected (`sessions-list.ts`). Same name and meaning as
39
+ * `SessionSummary.rowTag` in `plugins/store-contracts.ts`. */
40
+ rowTag?: string;
41
+ }
42
+ /**
43
+ * 会话店的行格写原语(规则与判序:`session-row.ts` 头注「写原语」)—— 按入口分两只重载:客户端写(PATCH)的结局闭集四员;行重放(2c 导入落定后的那一笔)
44
+ * 只有 `written` / `absent`(`changed` / `mirror` 对它不可达)。
45
+ */
46
+ export interface SessionRowCellsWriter {
47
+ (sessionId: string, cells: SessionRowPatch, how: ClientSessionRowWrite): Promise<SessionRowWriteOutcome<SessionListItem>>;
48
+ (sessionId: string, cells: SessionRowPatch, how: ReplaySessionRowWrite): Promise<ReplaySessionRowWriteOutcome<SessionListItem>>;
35
49
  }
36
50
  /** A SessionStore that may additionally expose tenant-ownership ops (the TiDB store does) AND the
37
51
  * `SessionRepo` enumeration/lifecycle seam (§0.5: list/fork/delete homed on the session abstraction's own
@@ -187,17 +201,24 @@ export type OwnerAwareSessionStore = SessionStore & {
187
201
  * row of the list face (no such session, or a placed / delegation-transcript session, which never lists). Reader: the 2c
188
202
  * manifest (`title` / `pinned`). */
189
203
  sessionSummaryOf?: (sessionId: string) => Promise<SessionListItem | undefined>;
190
- /** The session row's writable bits (`title` / `pinned`) — the ONLY writer of a user-set title or pin (`title: null` clears it back
191
- * to the automatic title; the auto-titler's `setTitleIfNull` fills a NULL title only; the law of the two writers is written in
192
- * `session-row.ts`). **Written on the occupant the caller verified, and only on it**: `face` is the live occupant (`sessionFaceOfHead` over the `headOf` read)
193
- * the caller's gate judged; the write and the read-back are both conditioned on that occupant's incarnation (the same law as
194
- * the parked decide leg's two reads, `session-incarnation.ts`). Answers that occupant's list row after the write
195
- * ({@link sessionSummaryOf}'s projection); `undefined` = no list row for that occupant (deleted, re-registered by anyone in the
196
- * meantime, or a placed session) — nothing was written. A failed write throws. */
197
- patchSessionMeta?: (sessionId: string, patch: SessionRowPatch, face: SessionFace) => Promise<SessionListItem | undefined>;
204
+ /** The session row's write primitive for its writable cells (`title` / `pinned`) — the ONLY writer of a user-set title or pin (`title: null`
205
+ * clears it back to the automatic title; the auto-titler's `setTitleIfNull` fills a NULL title only; the law of the two writers and of this
206
+ * primitive is written in `session-row.ts`). Judged under the row's lock by one judge (`judgeSessionRowWrite`): the row expectation
207
+ * (core `SessionRowExpectation` — the occupant the caller's gate read, owner + incarnation, judged by core `sessionRowMatches`) → the writer
208
+ * (a client write on a read-only mirror ⇒ `mirror`) → the row tag (a client's `rowTag` that the locked row no longer carries ⇒ `changed`) → write.
209
+ * `written` / `changed` carry the list row read after the commit / rollback ({@link sessionSummaryOf}'s projection, with its new `rowTag`);
210
+ * `absent` = no list row for that occupant (deleted, re-registered, owner restamped, or a placed session) — nothing was written. A failed write throws. */
211
+ writeSessionRowCells?: SessionRowCellsWriter;
198
212
  /** E17 — fork a session's whole history to a NEW session; null if the source is unknown. The fork row records `owner` when it is a
199
- * string, else (`null`) the SOURCE row's owner — a copy of an owned history is never anonymous (core `SessionStore.fork`, 7.55.0 S2). */
213
+ * string, else (`null`) the SOURCE row's owner — a copy of an owned history is never anonymous (core `SessionStore.fork`, 7.55.0 S2).
214
+ * On the three stores it is the projection `forkTo(…, { id: uuidv7() })` of {@link forkTo} (one private body). */
200
215
  fork?: (sourceId: string, owner: string | null) => Promise<string | null>;
216
+ /** fork 续写的店契约(三店同一只私有体;规则与结果闭集在 `plugins/session-fork.ts`):把源会话整份历史拷进调用方给的目标 id;`target.key` 存进新行那一格 fork 键。
217
+ * `null` = 源不在;`created` / `replayed`(同属主同键)/ `occupied{sameOwner}`(撞上别的行)。目标行原子新建(SQL 主键排他、meta 先;local 两把写锁 + 格先)。 */
218
+ forkTo?: (sourceId: string, owner: string | null, target: ForkTarget) => Promise<ForkToOutcome | null>;
219
+ /** 执行席(规则在 `execution-placement.ts`):根会话行上这一位住客的那一格的唯一读口(`read`)与 place / markMirror 两只写口。三店都有;两只包装原样转发。
220
+ * 缺席 = 会话店没有执行放置(只有不带它的测试走廊 / 嵌入形),调用方各按自己的缺席臂处置。 */
221
+ executionPlacement?: ExecutionPlacementSeat;
201
222
  /** E21 — purge a session's conversation history (session_meta/session_event); idempotent (false if absent).
202
223
  * `owner` is the single-DB-fleet null-safe SQL owner guard on `session_meta.owner` (defense-in-depth — the
203
224
  * route already owner-gates; this pins the DELETE to the authenticated tenant so even a buggy gate can't purge
@@ -285,6 +306,11 @@ export interface AuthContext {
285
306
  principal?: string;
286
307
  /** The session to run against (resolved + ownership-checked here, then returned to the caller). */
287
308
  sessionId: string;
309
+ /**
310
+ * 属主门判的那一读(会话店 `headOf` 的整只头;现铸 / 未知 id 臂是登记后那一读)—— 执行上下文解析器的 `judged`:解析器读到的行头住客与它不同 ⇒ 404(门与解析之间会话被删并同 id 重登记)。
311
+ * 缺 = 店没有 `headOf`(读不出头的店;只一元车道表、无执行席的形可达)。
312
+ */
313
+ head?: SessionHead;
288
314
  /** Long-term memory scope, derived from the principal (not the body). Undefined disables memory. */
289
315
  memoryScope?: string;
290
316
  /** 142-S4:the request's SHAPE-VALIDATED **且已归一(小写)** 的 projectId(body.projectId 过 core
package/dist/security.js CHANGED
@@ -84,7 +84,11 @@ export function createAuthorizer(config, sessionStore) {
84
84
  }
85
85
  const requireExisting = body.requireExistingSession === true;
86
86
  const headOf = headReadOf(sessionStore);
87
- const readOwner = headOf === undefined ? undefined : async (id) => (await headOf(id))?.owner;
87
+ let head;
88
+ const readOwner = headOf === undefined ? undefined : async (id) => {
89
+ head = await headOf(id);
90
+ return head?.owner;
91
+ };
88
92
  const register = sessionStore.register?.bind(sessionStore);
89
93
  const requested = typeof body.sessionId === "string" && body.sessionId ? body.sessionId : undefined;
90
94
  let sessionId;
@@ -126,8 +130,10 @@ export function createAuthorizer(config, sessionStore) {
126
130
  sessionId = uuidv7();
127
131
  if (register)
128
132
  await register(sessionId, principal ?? null);
133
+ if (readOwner)
134
+ await readOwner(sessionId);
129
135
  }
130
- return { principal, sessionId, memoryScope: memoryScopeFor(config, principal, projectId), resolvedProjectId: projectId };
136
+ return { principal, sessionId, memoryScope: memoryScopeFor(config, principal, projectId), resolvedProjectId: projectId, ...(head === undefined ? {} : { head }) };
131
137
  };
132
138
  }
133
139
  export const PRINCIPAL_MAX_LENGTH = 190;
@@ -23,7 +23,7 @@
23
23
  * **机器派生**、不手抄、不按「归谁消费」切({@link serverWithheldEnvNames} 一处)。声明两个时机、同一只 {@link declareServerWithheldEnv}:起服第一步(三个进程入口:
24
24
  * `boot/stage-01-config.ts` / `run-local.ts` / `bake-runner/main.ts`,在任何执行环境构造与 spawn 之前 —— 声明先于构造才有构造时拒;此时没有生效配置 = 空表),
25
25
  * 与每次生效配置换代(`web-admin-client/apply-effective.ts` 本体:CAS 之后、Runner 换装与 commit 之前 —— 声明先于席位,旧世代不声明)。名单只随**落地**的世代
26
- * 增长(本进程 env 是 boot 期输入:一个名字一旦指过本进程的凭据,条目改名 / 删掉之后它的值仍在 env 里 ⇒ 落地过的名不退出;没落地的候选不留痕 —— 换装拒 ⇒
26
+ * 增长(本进程 env 是 boot 期输入:一个名字一旦指过本进程的凭据,条目改名 / 删掉之后它的值仍在 env 里 ⇒ 落地过的名不退出;没落地的候选不留痕 —— 名单重判命中 / 换装拒 ⇒
27
27
  * {@link withdrawServerWithheldEnv} 撤回到换代前的集合)。声明失败(坏形名 / 运行时回填名)⇒ 引擎抛且声明保持原状:起服那次 = 拒启,换代那次 = 候选整拒(与 stage
28
28
  * 抛同路)。能力词 `inherit_env.own_credentials_withheld` 在本进程的事实 =
29
29
  * {@link ownCredentialsWithheld}:最近一次成功声明的集合 ⊇ 当前生效配置应声明的集合(活配置经 {@link bindOwnCredentialsConfig} 在 `loadConfig()` 之后绑一次;
@@ -38,10 +38,13 @@
38
38
  * {@link allowlistedEnv} 是本仓「一张名单点到哪些 env 键,就把它们从源 env **原样**交给沙箱」的唯一实现。调用:沙箱带外 env 的 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` /
39
39
  * `K8S_SANDBOX_ENV`(`config.ts`)与名单解析器的「在不在」判。宿主子进程的剥后放回(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)不经它 —— 那是引擎的严格名单臂(上一段)。
40
40
  * host shell 的 `inheritEnv:[names]`(hermetic 形)7.129.0 起由引擎读(与 core `NodeExecutionEnv` 同名选项同义,点到自有凭据名即拒),不经本谓词。
41
- * 名单型旋钮的**名单**另由 `config.ts` 的同一只解析器 `parseEnvNameAllowlist` 判形(形错拒启 / 源里没有的名按序号 warn),本函数只管取值。
41
+ * 名单型旋钮的**名单**另由 `config.ts` 的同一只解析器 `parseEnvNameAllowlist` 判形(形错 / 点名名单型旋钮 / 点名本服务自有凭据名 ⇒ 拒启;源里没有的名按序号 warn),本函数只管取值。
42
42
  * 「点名」在各处同义:点到的键原样带值(空串值照带),密钥形也照过(点名 = 部署方显式承担);源里没有的名不产生条目。
43
43
  * 剥后放回形(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)的放回本身也经引擎:{@link composeHostSubprocessEnv} 把名单交引擎的严格名单臂(`inheritedHostEnv([names])`)叠在
44
- * 继承结果之上 ⇒ 点到本服务自有凭据名 = 引擎抛(spawn 时;起服时解析器 `parseEnvNameAllowlist` 按表列 `channel` 已用同一只判 `inheritEnvPolicyProblem` 拒过),
44
+ * 继承结果之上 ⇒ 点到本服务自有凭据名 = 引擎在**组合这一刻**抛(语言服务器在构造时组合一次、之后 spawn 用那份静态记录;bake-runner 每次 spawn 组合;
45
+ * 起服时解析器 `parseEnvNameAllowlist` 已用同一只判 `inheritEnvPolicyProblem` 拒过 —— 解析器对五只名单一律拒,点到**解析时已声明**的自有凭据名的沙箱名单因此
46
+ * 到不了 {@link allowlistedEnv} 取值这一步;声明之后增长(中心 / config.d 模型条目的 `apiKeyEnv` 换代并进)时,换代声明对活配置里每只名单此刻交出去的名用同一只判
47
+ * 重判一次 —— 命中 ⇒ 撤回这次增长、候选整拒,已装好的沙箱 env / 语言服务器 env 不变成本服务的凭据,`config.ts` `ownCredentialHitsInForce`),
45
48
  * 点到的变量值是本服务凭据的副本 = 引擎扣下。本仓不另判。
46
49
  */
47
50
  import { type InheritEnvPolicy, type SecretEnvFinding } from "@sema-agent/core";
@@ -64,7 +67,7 @@ export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKe
64
67
  /**
65
68
  * 🔴 **唯一声明点**:把本进程自有凭据向引擎声明(core 7.52.0 `declareWithheldEnv({ names, values })`;重声明即整表替换,从下一次 spawn 起生效)。`modelApiKeyEnv` = 这一次要并进的
66
69
  * 模型条目表(起服第一步 = 空表;换代 = 候选表)。两臂都只随**落地**的世代增长(落地过的名 / 值来源不退出:本进程 env 是 boot 期输入;没落地的候选不留痕 ——
67
- * 换装拒 ⇒ {@link withdrawServerWithheldEnv})。
70
+ * 名单重判命中 / 换装拒 ⇒ {@link withdrawServerWithheldEnv})。
68
71
  * · **名臂**:此前声明过的 ∪ 这张表的 {@link serverWithheldEnvNames}。引擎对坏形名 / 运行时回填名(`PATH` / `HOME` …)响亮抛且声明保持原状 —— 本函数不接住:
69
72
  * 起服那次抛 = 拒启;换代那次抛 = 候选整拒(`applyEffective` 抛,与 stage 抛同路)。第一次交表带着名臂与**此前已收下的值**(引擎收过、env 不变 ⇒ 同值照收),
70
73
  * 所以这一次抛只可能是名的问题。
@@ -76,7 +79,8 @@ export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKe
76
79
  */
77
80
  export declare function declareServerWithheldEnv(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
78
81
  /**
79
- * 席位拒 ⇒ 撤回最近一次声明(两臂一起回到它之前的那一份):`applyEffective` 在 Runner 换装抛时调,紧跟着那一次 {@link declareServerWithheldEnv} —— 没落地的候选不留痕。
82
+ * 候选没落地 ⇒ 撤回最近一次声明(两臂一起回到它之前的那一份),两个调用点都紧跟着那一次 {@link declareServerWithheldEnv}、同步无 await:换代声明 helper 在名单重判命中时调,
83
+ * `applyEffective` 在 Runner 换装抛时调 —— 没落地的候选不留痕。
80
84
  * 退回去的那一份是引擎之前接受过的(名确定、值来源的值随 env 不变)⇒ 不抛;连撤两次是同一个结果(幂等)。
81
85
  */
82
86
  export declare function withdrawServerWithheldEnv(): void;
@@ -27,6 +27,13 @@
27
27
  * (修复验证轮两条 opus 腿同命中:修前预检看的是内存句柄的**状态**(不是 running / pending ⇒ 够不着),本副本刚停、写在飞的子代因此被答成
28
28
  * 「在别的副本上」的 409 —— 同一个「这只子代在不在本进程」的判断有两个写者。)
29
29
  *
30
+ * ## 一只预检、一只协调器、一个 scope(树活性的子代半)
31
+ * 能在树活着时改根放置 / 住客的三门 —— 删除(`boot/session-faces.ts`)、换绑(`routes/devices.ts`)、导入(`routes/session-sync.ts`)—— 与显式结清口
32
+ * `POST /v1/sessions/:id/children/retire`(`routes/background.ts`)都只经这里:**观察**只有 {@link probeSessionChildren}(同一次枚举交出活子代句柄
33
+ * `liveChildren` 与不可达的那几只),**结清**只有 {@link retireSessionChildren}(结果交首轮观察到的句柄 `observed`);scope 只有一处取
34
+ * {@link sessionChildrenScope}(会话属主 `owner ?? "default"`)。wire 上的 `children` / `remaining` 一律经名册的 id 投影出门。
35
+ * 子代半是**写前一次观察**:子代登记在 core 的后台子代店,写者的事务装不进这一读(认领半才是写事务内那一读,`execution-placement-sql.ts` 头注)。
36
+ *
30
37
  * ## 循环(有上界)
31
38
  * 作废 / 停止与一次并发的 `/decide` 可能交错:expire 的 CAS 输给了决定腿的 resolve ⇒ 那只子代被唤醒成 running ⇒ 下一轮枚举看见
32
39
  * 它、停掉它。循环到「枚举不出活的子代」为止,轮数有上界({@link RETIRE_MAX_ROUNDS});本进程 kill 的耐久写是 core 的异步写链,
@@ -51,26 +58,37 @@ export interface RetireSessionChildrenDeps extends ChildrenSource {
51
58
  registry: Pick<TaskRegistry, "stopTask" | "reconcileParkedAgents" | "activeDelegationHandles">;
52
59
  sleep?: (ms: number) => Promise<void>;
53
60
  }
54
- /** 可达性预检的结果(零写)。`unreachableHandles` 只含本进程结束不了的那几只;可达的子代不在这里。 */
61
+ /**
62
+ * 可达性预检的结果(零写)。`liveChildren` = 这一次枚举观察到的全部活子代句柄(running / parked;换绑 / 导入两门的 409 体 `children` 就是它);
63
+ * `unreachableHandles` 只含其中本进程结束不了的那几只(删除门与结清口的 409 `remaining`);可达的子代不在那里。
64
+ */
55
65
  export type SessionChildrenReachability = {
56
66
  readonly reachable: true;
57
- readonly live: number;
67
+ readonly liveChildren: readonly string[];
58
68
  } | {
59
69
  readonly reachable: false;
60
- readonly live: number;
70
+ readonly liveChildren: readonly string[];
61
71
  readonly unreachableHandles: readonly string[];
62
72
  };
63
- /** 结束子代的结果。`incomplete` = 时序失败(本进程的停止还没落盘 / 预检之后冒出来的子代),不是可达性。 */
73
+ /**
74
+ * 结束子代的结果。`observed` = 首轮枚举观察到的活子代句柄(成功形 `all` 下它们已全部结清 —— 结清口应答的 `children`)。
75
+ * `incomplete` = 时序失败(本进程的停止还没落盘 / 预检之后冒出来的子代),不是可达性。
76
+ */
64
77
  export type RetireSessionChildrenOutcome = {
65
78
  readonly stopped: "all";
66
- readonly found: number;
79
+ readonly observed: readonly string[];
67
80
  readonly rounds: number;
68
81
  } | {
69
82
  readonly stopped: "incomplete";
70
- readonly found: number;
83
+ readonly observed: readonly string[];
71
84
  readonly rounds: number;
72
85
  readonly remainingHandles: readonly string[];
73
86
  };
87
+ /**
88
+ * 以一只根会话为根的后台子代在哪个 scope 分区里枚举 —— **唯一一处取**:会话属主 `owner ?? "default"`(删除门、换绑 / 导入两门、结清口同读;
89
+ * 子代按 run 的 principal 登记、与会话属主不同源的那一形是既有缺陷,修在这里一处)。
90
+ */
91
+ export declare function sessionChildrenScope(owner: string | null): string;
74
92
  /**
75
93
  * **可达性预检**(零写;协调器在任何破坏性步骤之前调)。parked 行在有 checkpoint 店时可达(expire + reconcile 在任何副本上都成);
76
94
  * running 行除非 {@link runsOnAnotherReplica} 都可达(`getAccessibleTask` / `agentStore.get` 都是纯读,不动任何东西)。
@@ -79,9 +97,9 @@ export declare function probeSessionChildren(deps: ProbeSessionChildrenDeps, roo
79
97
  /** 以 `rootSessionId` 为根、在 `scope` 分区里的后台子代:作废 parked、停掉 running,重新枚举到没有活的为止(有上界)。 */
80
98
  export declare function retireSessionChildren(deps: RetireSessionChildrenDeps, rootSessionId: string, scope: string): Promise<RetireSessionChildrenOutcome>;
81
99
  /**
82
- * 结束子代这一步的**时序**失败(可达性已在预检过了):本进程发出的停止在上界内没有全部落地。协调器抛它 ⇒ 路由通用 500;
83
- * session_meta 不删(E21「子腿失败 ⇒ 不删 meta ⇒ 幂等重试收敛」的既有契约),账本那一腿此时已经删了 —— 与任何一条子腿中途失败同形。
84
- * 文案不带句柄(句柄进运维 warn `session_delete_children_stop_incomplete`)。
100
+ * 结束子代这一步的**时序**失败(可达性已在预检过了):本进程发出的停止在上界内没有全部落地。两只调用方(删除协调器、结清口)抛它 ⇒ 路由通用 500;
101
+ * 删除:session_meta 不删(E21「子腿失败 ⇒ 不删 meta ⇒ 幂等重试收敛」的既有契约),账本那一腿此时已经删了 —— 与任何一条子腿中途失败同形;
102
+ * 结清口:重试同一请求收敛。文案不带句柄(句柄进运维 warn)。
85
103
  */
86
104
  export declare class SessionChildrenStopIncompleteError extends Error {
87
105
  readonly sessionId: string;
@@ -1,5 +1,8 @@
1
1
  export const RETIRE_MAX_ROUNDS = 6;
2
2
  const RETIRE_BACKOFF_MS = [0, 25, 50, 100, 200, 400];
3
+ export function sessionChildrenScope(owner) {
4
+ return owner ?? "default";
5
+ }
3
6
  async function liveChildren(deps, rootSessionId, scope) {
4
7
  const out = new Map();
5
8
  if (deps.agentStore !== undefined) {
@@ -22,7 +25,8 @@ export async function probeSessionChildren(deps, rootSessionId, scope) {
22
25
  if (unreachable)
23
26
  unreachableHandles.push(child.handle);
24
27
  }
25
- return unreachableHandles.length === 0 ? { reachable: true, live: live.length } : { reachable: false, live: live.length, unreachableHandles };
28
+ const handles = live.map((c) => c.handle);
29
+ return unreachableHandles.length === 0 ? { reachable: true, liveChildren: handles } : { reachable: false, liveChildren: handles, unreachableHandles };
26
30
  }
27
31
  async function runsOnAnotherReplica(deps, handle, rootSessionId, scope) {
28
32
  if (deps.registry.getAccessibleTask(handle, { scope, sessionId: rootSessionId }) !== undefined)
@@ -35,7 +39,7 @@ async function runsOnAnotherReplica(deps, handle, rootSessionId, scope) {
35
39
  export async function retireSessionChildren(deps, rootSessionId, scope) {
36
40
  const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
37
41
  let live = await liveChildren(deps, rootSessionId, scope);
38
- const found = live.length;
42
+ const observed = live.map((c) => c.handle);
39
43
  let rounds = 0;
40
44
  while (live.length > 0 && rounds < RETIRE_MAX_ROUNDS) {
41
45
  rounds++;
@@ -56,8 +60,8 @@ export async function retireSessionChildren(deps, rootSessionId, scope) {
56
60
  live = await liveChildren(deps, rootSessionId, scope);
57
61
  }
58
62
  return live.length === 0
59
- ? { stopped: "all", found, rounds }
60
- : { stopped: "incomplete", found, rounds, remainingHandles: live.map((c) => c.handle) };
63
+ ? { stopped: "all", observed, rounds }
64
+ : { stopped: "incomplete", observed, rounds, remainingHandles: live.map((c) => c.handle) };
61
65
  }
62
66
  async function expireParkedCard(deps, handle, scope) {
63
67
  if (deps.agentStore === undefined || deps.checkpointStore === undefined)
@@ -76,7 +80,7 @@ export class SessionChildrenStopIncompleteError extends Error {
76
80
  remaining;
77
81
  constructor(sessionId, remaining) {
78
82
  super(`session ${sessionId}: ${remaining} background agent(s) this replica stopped had not settled within ${RETIRE_MAX_ROUNDS} rounds — ` +
79
- `the session record was kept so the delete can be retried (it converges once they settle)`);
83
+ `the operation stopped before its next step (a delete keeps the session record) so it can be retried (it converges once they settle)`);
80
84
  this.sessionId = sessionId;
81
85
  this.remaining = remaining;
82
86
  this.name = "SessionChildrenStopIncompleteError";
@@ -29,11 +29,14 @@
29
29
  * 要拿令牌去**写**(带化身条件的 UPDATE、认领、空间分区键)或要响亮区分「行坏了」的读者经它;只**比较**两头的读者用 `compareOccupants`。
30
30
  * 空值只可能来自没铸化身的写腿、人为改库,或跳过了 7.104.0–7.135.x(那几版的起服迁移给存量行铸令牌,窗关后已删码)直接升上来的库 ——
31
31
  * 把它当「通配」就是让任何人都能和这一行比成同一位住客。
32
- * · **不上 wire、不进模型上下文、不进日志正文**:日志里要点名时只打 {@link incarnationLogPrefix}(前 8 位)。
32
+ * · **不上 wire、不进模型上下文、不进日志正文**:日志里要点名时只打 {@link incarnationLogPrefix}(前 8 位)。值本身不出进程;唯一出进程的形是
33
+ * **单向摘要** —— 它折进会话列表行的行标签(`session-row.ts` 的 `sessionRowTagOf`,与会话 id、属主、可写格一起 SHA-256),摘要不可逆
34
+ * (令牌是 v4 随机串),只让「住客换了」在标签上看得见。
33
35
  *
34
36
  * ## 谁读它(全部派生自那一只头读)
35
- * · `/decide` 的 parked 赎回腿(`parked-decide.ts`):claim 前读一次、claim 后再读一次,会话不在或不是同一位住客 ⇒ 回滚 claim、
36
- * 答既有的 `409 decide.not_parked`;赎回面(`parked-revive-surface.ts`)只在属主相等**且**两次读是同一位住客时交继承座。
37
+ * · `/decide` 的 parked 赎回腿(`parked-decide.ts`):对根会话一读一判 —— 认领之前执行上下文解析器那一次执行席行读(与头读同一行、同一列)是唯一一读,
38
+ * 认领之后唯一写口按那一读的住客定址判一次(化身 ∧ 属主;不在或不是那一位 ⇒ 回滚认领、答既有的 `409 decide.not_parked`);
39
+ * 赎回面(`parked-revive-surface.ts`)只收写口放行的那一位,属主 = 卡的 principal 时交继承座。
37
40
  * · core 的记忆引擎:Runner 读**它取到的那只会话**的 `getMetadata().incarnation`(SQL 两店的 wake 带出这一列;local 店交出的会话
38
41
  * 在元数据上答边档那一格),不再有宿主座。
39
42
  * · 没取会话的那一侧(k8s 工作区空间的分区,`boot/execution-env.ts`):会话店的 `headOf`,经 {@link sessionFaceOfHead} 拿令牌;缓存包装层原样转发。
@@ -75,7 +78,7 @@ export declare class SessionIncarnationMissingError extends Error {
75
78
  */
76
79
  export type EmptyIncarnationReading = "refuse" | "absent";
77
80
  /**
78
- * 行上化身那一格的**唯一解码**(店读出的头、活住客的投影、SQL 两店 wake 带出的会话元数据、留存删除的空间键、整段换装的沿用判据都经它 ——
81
+ * 行上化身那一格的**唯一解码**(店读出的头、活住客的投影、SQL 两店 wake 带出的会话元数据、留存删除的空间键、整段换装的沿用判据({@link carriedRowCells})都经它 ——
79
82
  * 「是不是一只令牌」只判这一处)。非空字符串 ⇒ 那只令牌;否则按 `empty`({@link EmptyIncarnationReading}):缺省 `refuse` ⇒
80
83
  * {@link SessionIncarnationMissingError},`absent` ⇒ `undefined`。
81
84
  */
@@ -116,14 +119,21 @@ export declare function sessionFaceOfHead(sessionId: string, head: SessionHead |
116
119
  * 委派继承律读的都是这一只返回值。缺席 = 店没有实装 `headOf`(读不出属主 / 住客 / 叶子,调用方各按自己的 fail-closed 臂处置)。
117
120
  */
118
121
  export declare function headReadOf(store: object | undefined): HeadRead | undefined;
122
+ /** 换装沿用的行格(化身 + fork 键):{@link carriedRowCells} 的答案。`forkKey: null` = 没有可沿用的键(新建行 / 行上本来没有)。 */
123
+ export interface CarriedRowCells {
124
+ readonly incarnation: string;
125
+ readonly forkKey: string | null;
126
+ }
119
127
  /**
120
- * session-sync **换装**(整段换装 / 暂存提交 / local 落在既有行上的导入)时化身的去留 —— 三只店共用的一句判据:换装只换内容,换的是**同一行**
121
- * ⇒ 行在且那一格里有一只令牌(唯一解码 {@link incarnationFromCell} 的 `absent` 读法)⇒ 沿用(属主重戳与否不改这一判:同一行 = 同一位住客);
122
- * 行不在(这是一次新建)或还是空的存量形 ⇒ 铸一只新的。属主不在这里判:每只店在自己的属主门上判过被换的那一行,读不中的换装一个字节不写。
128
+ * session-sync **换装**(整段换装 / 暂存提交)时行格的去留 —— 三只店共用的一句判据:换装只换内容,换的是**同一行**
129
+ * ⇒ 行在且那一格里有一只令牌(唯一解码 {@link incarnationFromCell} 的 `absent` 读法)⇒ 化身与 fork 键(`fork_key` / local `forkKey`:建这一行的那次 fork
130
+ * 请求的键,只由 fork 创建点写、此后每个写携带)**一起沿用**(属主重戳与否不改这一判:同一行 = 同一位住客);行不在(这是一次新建)或还是空的存量形 ⇒
131
+ * 铸一只新化身、没有 fork 键。属主不在这里判:每只店在自己的属主门上判过被换的那一行(SQL 孪生的 replace 只交「属主门删得到的那一行」),读不中的换装一个字节不写。
123
132
  */
124
- export declare function carriedIncarnation(sessionId: string, prior: {
133
+ export declare function carriedRowCells(sessionId: string, prior: {
125
134
  incarnation?: unknown;
126
- } | undefined): string;
135
+ forkKey?: unknown;
136
+ } | undefined): CarriedRowCells;
127
137
  /**
128
138
  * `session_meta.incarnation` 的列定义 —— 两方言各一句,**唯一属主**:中央 DDL(`tidb-pool.ts` / `pg-session-storage.ts` 的
129
139
  * `CREATE TABLE session_meta`)内插这同一串;存量库缺这一列时,起服列册探针(`schema-consistency.ts`)拒启,拒句里的 `ALTER TABLE … ADD COLUMN …`
@@ -136,7 +146,7 @@ export declare function carriedIncarnation(sessionId: string, prior: {
136
146
  */
137
147
  export declare const SESSION_META_INCARNATION_COLUMN: {
138
148
  readonly name: "incarnation";
139
- readonly tidb: "incarnation VARCHAR(64) NOT NULL DEFAULT ''";
149
+ readonly mysql: "incarnation VARCHAR(64) NOT NULL DEFAULT ''";
140
150
  readonly pg: "incarnation VARCHAR(64) COLLATE \"C\" NOT NULL DEFAULT ''";
141
151
  };
142
152
  //# sourceMappingURL=session-incarnation.d.ts.map
@@ -67,13 +67,15 @@ export function headReadOf(store) {
67
67
  const read = store?.headOf;
68
68
  return typeof read === "function" ? read.bind(store) : undefined;
69
69
  }
70
- export function carriedIncarnation(sessionId, prior) {
70
+ export function carriedRowCells(sessionId, prior) {
71
71
  const held = prior === undefined ? undefined : incarnationFromCell(sessionId, prior.incarnation, "absent");
72
- return held ?? mintSessionIncarnation();
72
+ if (held === undefined)
73
+ return { incarnation: mintSessionIncarnation(), forkKey: null };
74
+ return { incarnation: held, forkKey: typeof prior?.forkKey === "string" && prior.forkKey !== "" ? prior.forkKey : null };
73
75
  }
74
76
  export const SESSION_META_INCARNATION_COLUMN = {
75
77
  name: "incarnation",
76
- tidb: "incarnation VARCHAR(64) NOT NULL DEFAULT ''",
78
+ mysql: "incarnation VARCHAR(64) NOT NULL DEFAULT ''",
77
79
  pg: `incarnation VARCHAR(64) COLLATE "C" NOT NULL DEFAULT ''`,
78
80
  };
79
81
  //# sourceMappingURL=session-incarnation.js.map
@@ -139,7 +139,7 @@ export declare function sessionPolicyErrorFacts(e: SessionPolicyError): Readonly
139
139
  * · `conflict`(409)—— CAS 输了,重读 `rev` 再来。**码位与状态逐字不变**。
140
140
  * · `loosen_forbidden`(403)—— 非 operator 想放松。**码位与状态逐字不变**。⚠️ 这个词还有**第二个铸点**([ref] B5,
141
141
  * 如实登记、未改):PG 后端的 SQL 双生对含 NUL / 孤代理字节的规则拒收时借用了它
142
- * (`plugins/session-policy-store-sql.ts` 的 `pgHasUnstorable` 臂)—— 那一形**既非放宽也非越权**,operator 写同样撞上;
142
+ * (`stores-sql/session-policy-store-sql.ts` 的 `pgHasUnstorable` 臂)—— 那一形**既非放宽也非越权**,operator 写同样撞上;
143
143
  * core 的闭集里没有「输入不可存」这个词,改码位要先裁定新码与 PUT / 2c 导入两条入口同判(DEBTS [ref] B5)。
144
144
  * · `corrupt`(**500**)—— 店读不出它自己该读的那份字节(记号/行被列外篡改或撕裂,或存着引擎今天的形读法读不出的规则体)。
145
145
  * 不是 4xx:调用方没做错任何事,它也改不了这件事;不是 503:重试改不了判(修复路径是**人**去改回或删掉那一行,