@sema-agent/server 7.150.0 → 7.151.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 (341) hide show
  1. package/MIGRATION.md +185 -0
  2. package/USAGE.md +53 -25
  3. package/deploy/sema-up/README.md +7 -1
  4. package/deploy/sema-up/chart/Chart.yaml +1 -1
  5. package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
  6. package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
  7. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
  8. package/deploy/sema-up/chart/templates/server.yaml +5 -3
  9. package/deploy/sema-up/chart/values-full-single.yaml +3 -1
  10. package/deploy/sema-up/chart/values.yaml +8 -1
  11. package/deploy/sema-up/cluster-up.sh +1 -0
  12. package/deploy/sema-up/kube-up.sh +1 -1
  13. package/deploy/sema-up/lib/flagshape.sh +1 -1
  14. package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
  15. package/deploy/sema-up/lib/helm-values-kube.py +2 -0
  16. package/dist/adoption/import.js +3 -3
  17. package/dist/approval-ask-machine.d.ts +1 -1
  18. package/dist/approval-card.d.ts +2 -2
  19. package/dist/approval-gate-currency.d.ts +1 -1
  20. package/dist/auth-bridge.d.ts +20 -0
  21. package/dist/auth-bridge.js +9 -0
  22. package/dist/background-session-reap.d.ts +1 -1
  23. package/dist/bake-runner/main.d.ts +4 -2
  24. package/dist/bind-host.d.ts +2 -3
  25. package/dist/bind-host.js +2 -4
  26. package/dist/boot/budget-tracing.js +1 -1
  27. package/dist/boot/engine-lease.d.ts +5 -0
  28. package/dist/boot/engine-lease.js +1 -0
  29. package/dist/boot/execution-env.d.ts +8 -14
  30. package/dist/boot/execution-env.js +54 -17
  31. package/dist/boot/execution-lane.js +32 -5
  32. package/dist/boot/late-binds.d.ts +8 -4
  33. package/dist/boot/late-binds.js +7 -1
  34. package/dist/boot/memory-consolidation.d.ts +3 -2
  35. package/dist/boot/memory-consolidation.js +6 -6
  36. package/dist/boot/reapers.d.ts +5 -0
  37. package/dist/boot/reapers.js +12 -1
  38. package/dist/boot/resolve-spec.d.ts +3 -3
  39. package/dist/boot/resolve-spec.js +31 -22
  40. package/dist/boot/runner-deps.d.ts +9 -2
  41. package/dist/boot/runner-deps.js +3 -3
  42. package/dist/boot/session-age-out.d.ts +36 -0
  43. package/dist/boot/session-age-out.js +101 -0
  44. package/dist/boot/session-faces.d.ts +5 -14
  45. package/dist/boot/session-faces.js +9 -5
  46. package/dist/boot/stage-01-config.js +2 -0
  47. package/dist/boot/stage-05-execution-env.d.ts +5 -3
  48. package/dist/boot/stage-05-execution-env.js +2 -2
  49. package/dist/boot/stage-06-runners.d.ts +7 -4
  50. package/dist/boot/stage-06-runners.js +15 -6
  51. package/dist/boot/stage-07-capability-layer.d.ts +7 -4
  52. package/dist/boot/stage-07-capability-layer.js +2 -1
  53. package/dist/boot/stage-08-reapers.d.ts +7 -4
  54. package/dist/boot/stage-08-reapers.js +4 -2
  55. package/dist/boot/stage-09-leader.d.ts +11 -5
  56. package/dist/boot/stage-09-leader.js +12 -3
  57. package/dist/boot/stage-10-http-server.d.ts +11 -5
  58. package/dist/boot/stage-10-http-server.js +3 -6
  59. package/dist/boot/web-admin.js +3 -1
  60. package/dist/boot/workflow-orchestration.d.ts +7 -2
  61. package/dist/boot/workflow-orchestration.js +24 -7
  62. package/dist/bounded-rotation.d.ts +29 -0
  63. package/dist/bounded-rotation.js +24 -0
  64. package/dist/capability-facts.d.ts +22 -13
  65. package/dist/capability-facts.js +28 -18
  66. package/dist/center-credential.d.ts +25 -6
  67. package/dist/center-credential.js +25 -5
  68. package/dist/config-catalog.d.ts +26 -7
  69. package/dist/config-catalog.js +14 -11
  70. package/dist/config-invariants.d.ts +2 -1
  71. package/dist/config-invariants.js +4 -4
  72. package/dist/config-types.d.ts +10 -4
  73. package/dist/config.d.ts +51 -2
  74. package/dist/config.js +35 -24
  75. package/dist/deployment-shape.d.ts +9 -1
  76. package/dist/deployment-shape.js +2 -1
  77. package/dist/device-protocol.d.ts +5 -4
  78. package/dist/device-protocol.js +1 -1
  79. package/dist/device-ws-hub.js +3 -2
  80. package/dist/device-ws-protocol.d.ts +63 -10
  81. package/dist/device-ws-protocol.js +56 -25
  82. package/dist/engine-identity.d.ts +19 -7
  83. package/dist/engine-identity.js +10 -2
  84. package/dist/entry-mode-clamp-notice.d.ts +2 -1
  85. package/dist/execution-env-refusal.d.ts +23 -0
  86. package/dist/execution-env-refusal.js +5 -0
  87. package/dist/execution-lane-caps.d.ts +3 -1
  88. package/dist/field-conflict.d.ts +1 -1
  89. package/dist/file-history-capture.d.ts +11 -4
  90. package/dist/file-history-capture.js +9 -6
  91. package/dist/file-history-disclosure.d.ts +7 -5
  92. package/dist/file-history-disclosure.js +7 -5
  93. package/dist/fleet/drain-entry.d.ts +4 -0
  94. package/dist/hooks/hook-runner.js +2 -2
  95. package/dist/host-lsp-manager.d.ts +2 -2
  96. package/dist/http/admission.js +17 -2
  97. package/dist/http/approval-sweeps.js +5 -5
  98. package/dist/http/checkpoint-ctx.d.ts +2 -0
  99. package/dist/http/checkpoint-ctx.js +1 -0
  100. package/dist/http/dispatch.d.ts +2 -1
  101. package/dist/http/dispatch.js +34 -27
  102. package/dist/http/durable-run-leg.js +16 -16
  103. package/dist/http/health-self-report.d.ts +9 -1
  104. package/dist/http/health-self-report.js +2 -0
  105. package/dist/http/owner-access.d.ts +31 -11
  106. package/dist/http/owner-access.js +37 -6
  107. package/dist/http/resume-ctx-read.js +25 -3
  108. package/dist/http/resume-legs.d.ts +2 -1
  109. package/dist/http/resume-legs.js +259 -79
  110. package/dist/http/route-ctx.d.ts +18 -8
  111. package/dist/http/route-table.d.ts +1 -1
  112. package/dist/http/routes/admin-memory-optout.d.ts +1 -1
  113. package/dist/http/routes/agents-roster.js +2 -3
  114. package/dist/http/routes/approvals-assistant.js +4 -3
  115. package/dist/http/routes/background.d.ts +3 -3
  116. package/dist/http/routes/capabilities.js +8 -5
  117. package/dist/http/routes/fleet.js +10 -8
  118. package/dist/http/routes/projects.d.ts +3 -2
  119. package/dist/http/routes/projects.js +4 -5
  120. package/dist/http/routes/runs.js +102 -36
  121. package/dist/http/routes/session-sync.d.ts +4 -3
  122. package/dist/http/routes/session-sync.js +172 -77
  123. package/dist/http/routes/sessions-list.js +13 -7
  124. package/dist/http/routes/sessions.js +10 -6
  125. package/dist/http/routes/tasks.js +30 -16
  126. package/dist/http/routes/trace-usage.js +2 -2
  127. package/dist/http/server.d.ts +21 -27
  128. package/dist/http/server.js +2 -2
  129. package/dist/http/submit-ctx.js +5 -4
  130. package/dist/http/wire-types.d.ts +14 -4
  131. package/dist/identity-source.d.ts +20 -3
  132. package/dist/identity-source.js +14 -0
  133. package/dist/index.d.ts +4 -0
  134. package/dist/lane-home-probe.d.ts +3 -7
  135. package/dist/lane-home-probe.js +0 -1
  136. package/dist/leader/leader.d.ts +2 -1
  137. package/dist/leg-facts.d.ts +13 -5
  138. package/dist/leg-facts.js +2 -1
  139. package/dist/memory-scope.d.ts +5 -4
  140. package/dist/memory-scope.js +4 -4
  141. package/dist/memory-sync-client.d.ts +30 -2
  142. package/dist/memory-sync-client.js +16 -9
  143. package/dist/observability/err-text.d.ts +7 -0
  144. package/dist/observability/err-text.js +5 -0
  145. package/dist/observability/fail-open.d.ts +7 -3
  146. package/dist/observability/fail-open.js +7 -3
  147. package/dist/observability/metrics.js +1 -0
  148. package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
  149. package/dist/orchestration/workflow-notify-journal.js +12 -1
  150. package/dist/orchestration/workflow-park-decision.d.ts +70 -18
  151. package/dist/orchestration/workflow-park-decision.js +49 -10
  152. package/dist/orchestration/workflow-park-holder.d.ts +65 -0
  153. package/dist/orchestration/workflow-park-holder.js +98 -0
  154. package/dist/orchestration/workflow-record-fence.d.ts +36 -0
  155. package/dist/orchestration/workflow-record-fence.js +42 -0
  156. package/dist/owner-faces.d.ts +1 -1
  157. package/dist/parked-decide.d.ts +104 -53
  158. package/dist/parked-decide.js +153 -117
  159. package/dist/peer-directory.d.ts +2 -2
  160. package/dist/peer-inbound-face.d.ts +2 -6
  161. package/dist/peer-inbound-face.js +0 -3
  162. package/dist/permission-mode-admission.d.ts +41 -5
  163. package/dist/permission-mode-admission.js +29 -3
  164. package/dist/plugins/adoption-log-sql.d.ts +3 -1
  165. package/dist/plugins/adoption-log-sql.js +7 -1
  166. package/dist/plugins/approval-ask-store-file.d.ts +10 -2
  167. package/dist/plugins/approval-ask-store-file.js +8 -0
  168. package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
  169. package/dist/plugins/approval-ask-store-memory.js +28 -9
  170. package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
  171. package/dist/plugins/approval-ask-store-sql.js +34 -5
  172. package/dist/plugins/approval-exemption-store.js +3 -0
  173. package/dist/plugins/approval-nonce-store.js +2 -0
  174. package/dist/plugins/breaker-state-sql.js +2 -0
  175. package/dist/plugins/caching-session-store.d.ts +9 -15
  176. package/dist/plugins/caching-session-store.js +6 -0
  177. package/dist/plugins/device-store-sql.js +6 -0
  178. package/dist/plugins/error-origin.d.ts +18 -0
  179. package/dist/plugins/error-origin.js +10 -0
  180. package/dist/plugins/execution-placement-sql.js +6 -6
  181. package/dist/plugins/image-bake-store-sql.js +6 -0
  182. package/dist/plugins/image-index-sql.js +4 -0
  183. package/dist/plugins/landed-attendants.d.ts +212 -0
  184. package/dist/plugins/landed-attendants.js +161 -0
  185. package/dist/plugins/leader-run-store-sql.js +4 -0
  186. package/dist/plugins/local-session-live-reference.d.ts +40 -0
  187. package/dist/plugins/local-session-live-reference.js +39 -0
  188. package/dist/plugins/local-session-store.d.ts +71 -33
  189. package/dist/plugins/local-session-store.js +129 -27
  190. package/dist/plugins/memory-engine-pg.js +4 -0
  191. package/dist/plugins/memory-engine-sql-apply.js +5 -2
  192. package/dist/plugins/memory-engine-tidb.js +4 -0
  193. package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
  194. package/dist/plugins/memory-sync-store-pg.js +5 -0
  195. package/dist/plugins/memory-sync-store-tidb.js +5 -0
  196. package/dist/plugins/one-time-migrations.d.ts +57 -63
  197. package/dist/plugins/one-time-migrations.js +162 -90
  198. package/dist/plugins/org-delivery-quota-store.js +4 -1
  199. package/dist/plugins/outcome-ledger-sql.js +2 -0
  200. package/dist/plugins/permission-rule-store-sql.js +11 -4
  201. package/dist/plugins/pg-cost-quota.js +4 -0
  202. package/dist/plugins/pg-pool.js +24 -1
  203. package/dist/plugins/pg-rate-limiter.js +2 -0
  204. package/dist/plugins/posix-shell-fs.d.ts +6 -1
  205. package/dist/plugins/posix-shell-fs.js +4 -1
  206. package/dist/plugins/remote-env-adb.d.ts +19 -15
  207. package/dist/plugins/remote-env-adb.js +41 -15
  208. package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
  209. package/dist/plugins/remote-env-connect-seat.js +72 -0
  210. package/dist/plugins/remote-env-device.d.ts +22 -5
  211. package/dist/plugins/remote-env-device.js +106 -8
  212. package/dist/plugins/remote-env-e2b.d.ts +15 -6
  213. package/dist/plugins/remote-env-e2b.js +42 -32
  214. package/dist/plugins/remote-env-file-error.js +6 -2
  215. package/dist/plugins/remote-env-host.d.ts +17 -0
  216. package/dist/plugins/remote-env-host.js +30 -38
  217. package/dist/plugins/remote-env-k8s.d.ts +41 -39
  218. package/dist/plugins/remote-env-k8s.js +53 -30
  219. package/dist/plugins/remote-env-local-docker.d.ts +13 -8
  220. package/dist/plugins/remote-env-local-docker.js +34 -41
  221. package/dist/plugins/remote-env-ssh.d.ts +18 -12
  222. package/dist/plugins/remote-env-ssh.js +60 -59
  223. package/dist/plugins/remote-shell.d.ts +98 -1
  224. package/dist/plugins/remote-shell.js +218 -0
  225. package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
  226. package/dist/plugins/resume-anchor-store-sql.js +12 -5
  227. package/dist/plugins/retention-lane-store-sql.js +4 -0
  228. package/dist/plugins/retention-sql.d.ts +20 -2
  229. package/dist/plugins/retention-sql.js +11 -0
  230. package/dist/plugins/run-store-sql.d.ts +21 -2
  231. package/dist/plugins/run-store-sql.js +31 -7
  232. package/dist/plugins/scenario-prefix-switch-m42.d.ts +1 -1
  233. package/dist/plugins/scenario-prefix-switch-m42.js +2 -2
  234. package/dist/plugins/schema-consistency.d.ts +4 -2
  235. package/dist/plugins/schema-consistency.js +19 -2
  236. package/dist/plugins/send-file-ledger.js +3 -0
  237. package/dist/plugins/serving-requirement.d.ts +26 -7
  238. package/dist/plugins/serving-requirement.js +11 -4
  239. package/dist/plugins/session-birth.d.ts +6 -2
  240. package/dist/plugins/session-capture-record-store-sql.js +2 -0
  241. package/dist/plugins/session-ext-table.d.ts +14 -1
  242. package/dist/plugins/session-ext-table.js +5 -1
  243. package/dist/plugins/session-row-sql.d.ts +49 -2
  244. package/dist/plugins/session-row-sql.js +56 -13
  245. package/dist/plugins/session-store-sql.d.ts +34 -18
  246. package/dist/plugins/session-store-sql.js +98 -17
  247. package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
  248. package/dist/plugins/shared-memory-store-sql.js +6 -2
  249. package/dist/plugins/sql-driver.d.ts +2 -1
  250. package/dist/plugins/sql-driver.js +18 -3
  251. package/dist/plugins/sql-errors.d.ts +11 -6
  252. package/dist/plugins/sql-errors.js +5 -6
  253. package/dist/plugins/stop-then-start-sentence.d.ts +10 -4
  254. package/dist/plugins/stop-then-start-sentence.js +14 -10
  255. package/dist/plugins/stop-then-start.d.ts +109 -0
  256. package/dist/plugins/stop-then-start.js +104 -0
  257. package/dist/plugins/store-backend.d.ts +40 -13
  258. package/dist/plugins/store-backend.js +169 -27
  259. package/dist/plugins/store-boundary.d.ts +57 -0
  260. package/dist/plugins/store-boundary.js +139 -0
  261. package/dist/plugins/store-contracts.d.ts +17 -0
  262. package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
  263. package/dist/plugins/store-fault-sentence-m53.js +25 -0
  264. package/dist/plugins/store-fault.d.ts +26 -7
  265. package/dist/plugins/store-fault.js +18 -2
  266. package/dist/plugins/task-attachment-store.js +9 -0
  267. package/dist/plugins/task-list-store-sql.d.ts +2 -2
  268. package/dist/plugins/task-list-store-sql.js +10 -8
  269. package/dist/plugins/tidb-cost-quota.js +4 -0
  270. package/dist/plugins/tidb-pool.js +2 -1
  271. package/dist/plugins/tidb-rate-limiter.js +4 -0
  272. package/dist/plugins/user-input-ledger-sql.js +4 -0
  273. package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
  274. package/dist/plugins/workflow-run-store-sql.js +64 -4
  275. package/dist/plugins/workspace-fill.d.ts +4 -7
  276. package/dist/plugins/workspace-fill.js +2 -1
  277. package/dist/plugins/write-behind-counter.js +5 -0
  278. package/dist/project-context.d.ts +0 -2
  279. package/dist/project-context.js +4 -8
  280. package/dist/project-registry.d.ts +46 -0
  281. package/dist/project-registry.js +44 -0
  282. package/dist/request-key-closure.js +1 -1
  283. package/dist/request-value-closure.d.ts +4 -0
  284. package/dist/request-value-closure.js +10 -0
  285. package/dist/run-acceptance.d.ts +3 -2
  286. package/dist/run-acceptance.js +4 -6
  287. package/dist/run-local.js +1 -0
  288. package/dist/runs.d.ts +37 -8
  289. package/dist/runs.js +53 -12
  290. package/dist/runtime-governance.d.ts +5 -4
  291. package/dist/security.d.ts +30 -32
  292. package/dist/security.js +41 -7
  293. package/dist/server-secret-env.d.ts +12 -10
  294. package/dist/server-secret-env.js +24 -15
  295. package/dist/session-bindings.d.ts +82 -0
  296. package/dist/session-bindings.js +18 -0
  297. package/dist/session-children-retire.d.ts +2 -2
  298. package/dist/session-children-retire.js +2 -6
  299. package/dist/session-deletion.d.ts +16 -1
  300. package/dist/session-error-wire.d.ts +3 -3
  301. package/dist/session-error-wire.js +3 -3
  302. package/dist/session-incarnation.d.ts +10 -0
  303. package/dist/session-incarnation.js +10 -1
  304. package/dist/session-kind.d.ts +57 -0
  305. package/dist/session-kind.js +22 -0
  306. package/dist/session-row.d.ts +21 -1
  307. package/dist/session-row.js +7 -1
  308. package/dist/session-sync-kernel.d.ts +24 -14
  309. package/dist/session-sync-kernel.js +3 -6
  310. package/dist/session-sync.d.ts +4 -98
  311. package/dist/session-sync.js +1 -71
  312. package/dist/session-titler.d.ts +10 -3
  313. package/dist/session-titler.js +19 -6
  314. package/dist/ssh-host-key.d.ts +5 -0
  315. package/dist/ssh-host-key.js +3 -0
  316. package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
  317. package/dist/stores-sql/file-history-store-sql.js +108 -70
  318. package/dist/stores-sql/pg-session-storage.d.ts +34 -7
  319. package/dist/stores-sql/pg-session-storage.js +37 -13
  320. package/dist/stores-sql/retention-store-sql.d.ts +1 -9
  321. package/dist/stores-sql/retention-store-sql.js +2 -10
  322. package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
  323. package/dist/stores-sql/session-policy-store-sql.js +61 -44
  324. package/dist/stores-sql/session-write-hooks.d.ts +11 -0
  325. package/dist/stores-sql/tidb-session-store.d.ts +58 -7
  326. package/dist/stores-sql/tidb-session-store.js +37 -13
  327. package/dist/submission-exit.d.ts +3 -2
  328. package/dist/submission-exit.js +9 -1
  329. package/dist/task-cwd.d.ts +2 -2
  330. package/dist/task-settings.d.ts +6 -5
  331. package/dist/terminal.d.ts +33 -2
  332. package/dist/terminal.js +139 -9
  333. package/dist/tool-approval.d.ts +140 -6
  334. package/dist/tool-approval.js +165 -72
  335. package/dist/trace/self-minted-wire.d.ts +290 -27
  336. package/dist/trace/self-minted-wire.js +101 -13
  337. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +78 -27
  338. package/package.json +2 -2
  339. package/wire-changes/server@7.151.0.json +762 -0
  340. package/dist/http/session-inherit-env.d.ts +0 -36
  341. package/dist/http/session-inherit-env.js +0 -6
@@ -0,0 +1,46 @@
1
+ import type { ProjectRegistration } from "@sema-agent/settings-schema";
2
+ import type { ListFilter } from "./owner-faces.js";
3
+ /** 一张登记簿(键 = 小写规范 UUID;值至少带可见性的属主键)。缺席 = 本部署没有登记簿(`capability-facts.ts` `projectRegistry` 答 no)。 */
4
+ export type ProjectRegistryTable<R> = Readonly<Record<string, R>> | undefined;
5
+ /**
6
+ * 谓词读的观者事实(列表面视域 {@link ListFilter} 里与登记可见性有关的两格)。`gateOwner` 只拿来与登记的 `registeredBy`(settings-schema 的纯串)逐字节比,
7
+ * 所以这里收纯串:新提交腿交来的是读面推导出的主体(品牌串,天然是串),续跑腿交来的是冻结时落成字节的那一串(不重铸品牌 —— 它不进任何属主门,只比这一格)。
8
+ */
9
+ export interface ProjectViewer {
10
+ readonly fleetWide: ListFilter["fleetWide"];
11
+ readonly gateOwner: string | null;
12
+ }
13
+ /** 一只登记对这位观者可见吗(唯一判):全舰队视域看全部;否则只看 `registeredBy` 恰是自己的(无身份的观者 `gateOwner = null` 恒看不见)。 */
14
+ export declare function projectVisibleTo(viewer: ProjectViewer, registration: Pick<ProjectRegistration, "registeredBy">): boolean;
15
+ /** 这位观者看得见的那只登记(唯一取法):未登记 / 看不见 / 没有观者 / 本部署没有登记簿 ⇒ 同答 `undefined`。按 id 只认自有键。 */
16
+ export declare function visibleProjectOf<R extends Pick<ProjectRegistration, "registeredBy">>(registry: ProjectRegistryTable<R>, projectId: string | undefined, viewer: ProjectViewer | undefined): R | undefined;
17
+ /** 一条 run 的登记簿默认读层种子:种子项目(这次提交带的、提交者看得见的那只)+ 判出它的那一位观者。随 run 冻结(续跑重建输入 `CheckpointCtx.memorySeed`)。 */
18
+ export interface MemorySeed {
19
+ readonly projectId: string;
20
+ readonly viewer: ProjectViewer;
21
+ }
22
+ /**
23
+ * 新提交腿的种子判(一条 run 一次):这次**提交**没带项目 ⇒ `undefined`(没什么可判);带了而对提交者可见 ⇒ 种子;带了而判不出(未登记 / 看不见 / 没有观者 / 没有登记簿)
24
+ * ⇒ `null`(判过,无 —— 冻结下来,续跑腿据它与「停驻在种子判出现之前」分开)。
25
+ */
26
+ export declare function memorySeedOf(registry: ProjectRegistryTable<Pick<ProjectRegistration, "registeredBy">>, submittedProjectId: string | undefined, viewer: ProjectViewer | undefined): MemorySeed | null | undefined;
27
+ /** 种子的词(两腿同调):种子的那一位观者此刻仍看得见的那只登记声明的 `defaultScopes`;没有种子 / 判过无 / 登记已不在 / 不再对他可见 ⇒ 缺席。 */
28
+ export declare function memorySeedScopesOf(registry: ProjectRegistryTable<Pick<ProjectRegistration, "registeredBy" | "defaultScopes">>, seed: MemorySeed | null | undefined): string[] | undefined;
29
+ /** 续跑重建输入里冻结的种子的读数:缺席(提交没带项目 / 本格出现之前写下的行)· 判过无(`null`)· 读得出 · 形不对。 */
30
+ export type FrozenMemorySeedReading = {
31
+ readonly kind: "absent";
32
+ } | {
33
+ readonly kind: "none";
34
+ } | {
35
+ readonly kind: "ok";
36
+ readonly seed: MemorySeed;
37
+ } | {
38
+ readonly kind: "malformed";
39
+ };
40
+ /**
41
+ * 冻结的种子的**唯一读法**(读者 = `http/resume-ctx-read.ts` 的续跑上下文读口,开封之后、任何续跑消费者之前)。服务端写下的形是闭集({@link memorySeedOf} 铸、经部署事实落盘):
42
+ * `null`(判过,无)或 `{ projectId, viewer: { fleetWide, gateOwner } }` —— `projectId` = 规范小写 UUID;`fleetWide` 布尔;`gateOwner` = 非空串(冻结时落成字节的主体,原样)或 `null`;
43
+ * 观者至少看得见点什么(`fleetWide` 或 `gateOwner` 在场 —— 看不见的提交不冻结种子)。别的形 ⇒ `malformed`(读口答坏体:续跑不按它挑种子)。
44
+ */
45
+ export declare function frozenMemorySeedOf(stored: unknown): FrozenMemorySeedReading;
46
+ //# sourceMappingURL=project-registry.d.ts.map
@@ -0,0 +1,44 @@
1
+ import { PROJECT_ID_REGEX } from "@sema-agent/core";
2
+ export function projectVisibleTo(viewer, registration) {
3
+ return viewer.fleetWide || registration.registeredBy === viewer.gateOwner;
4
+ }
5
+ export function visibleProjectOf(registry, projectId, viewer) {
6
+ if (registry === undefined || projectId === undefined || viewer === undefined || !Object.hasOwn(registry, projectId))
7
+ return undefined;
8
+ const registration = registry[projectId];
9
+ return registration !== undefined && projectVisibleTo(viewer, registration) ? registration : undefined;
10
+ }
11
+ export function memorySeedOf(registry, submittedProjectId, viewer) {
12
+ if (submittedProjectId === undefined)
13
+ return undefined;
14
+ if (viewer === undefined || visibleProjectOf(registry, submittedProjectId, viewer) === undefined)
15
+ return null;
16
+ return { projectId: submittedProjectId, viewer: { fleetWide: viewer.fleetWide, gateOwner: viewer.gateOwner } };
17
+ }
18
+ export function memorySeedScopesOf(registry, seed) {
19
+ return visibleProjectOf(registry, seed?.projectId, seed?.viewer)?.defaultScopes;
20
+ }
21
+ export function frozenMemorySeedOf(stored) {
22
+ if (stored === undefined)
23
+ return { kind: "absent" };
24
+ if (stored === null)
25
+ return { kind: "none" };
26
+ const own = (o, k) => (Object.hasOwn(o, k) ? o[k] : undefined);
27
+ const closedObject = (v, keys) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.keys(v).every((k) => keys.includes(k));
28
+ if (!closedObject(stored, ["projectId", "viewer"]))
29
+ return { kind: "malformed" };
30
+ const projectId = own(stored, "projectId");
31
+ const viewer = own(stored, "viewer");
32
+ if (typeof projectId !== "string" || !PROJECT_ID_REGEX.test(projectId) || projectId !== projectId.toLowerCase())
33
+ return { kind: "malformed" };
34
+ if (!closedObject(viewer, ["fleetWide", "gateOwner"]))
35
+ return { kind: "malformed" };
36
+ const fleetWide = own(viewer, "fleetWide");
37
+ const gateOwner = own(viewer, "gateOwner");
38
+ if (typeof fleetWide !== "boolean" || (gateOwner !== null && (typeof gateOwner !== "string" || gateOwner.length === 0)))
39
+ return { kind: "malformed" };
40
+ if (!fleetWide && gateOwner === null)
41
+ return { kind: "malformed" };
42
+ return { kind: "ok", seed: { projectId, viewer: { fleetWide, gateOwner } } };
43
+ }
44
+ //# sourceMappingURL=project-registry.js.map
@@ -29,7 +29,7 @@ const TASK_BODY_DECLARED_KEYS = [
29
29
  "outputSchema", "permissionMode", "projectId", "projectMemory", "promptLean", "reasoningEffort", "requireExistingSession",
30
30
  "resilience", "restoreFiles", "resumeAt", "resumeAtMode", "retainBackgroundProcesses", "retainSubagentSessions",
31
31
  "rewindFilesTo", "sandboxImageProfile", "scenario", "scratchpadDir", "selfOrchestration",
32
- "sessionId", "settings", "skills", "suggestNextPrompts", "systemPrompt", "taskId", "toolMaterializeStrategy",
32
+ "sessionId", "sessionKind", "settings", "skills", "suggestNextPrompts", "systemPrompt", "taskId", "toolMaterializeStrategy",
33
33
  "userAttachments", "userInvokedSkills", "verify", "verifyRounds",
34
34
  ];
35
35
  const TASK_BODY_SCENARIO_KEYS = ["repo"];
@@ -148,5 +148,9 @@ export interface InheritEnvFieldIssue {
148
148
  * 句子对两种坏值形是同一句(点名两词与「名单不是请求值」);`reason` 区分它们。
149
149
  */
150
150
  export declare function inheritEnvFieldIssue(raw: unknown): InheritEnvFieldIssue | null;
151
+ /** 词表外的拒句(wire 可见、字节冻结在 `test/api-error-text-freeze.test.ts` 的常量锚格)。 */
152
+ export declare const SESSION_KIND_VALUE_MESSAGE: string;
153
+ /** `sessionKind` 在场且不在闭集里 ⇒ 拒句;缺席或合法 ⇒ `null`(只在 fresh 受理门调)。 */
154
+ export declare function sessionKindFieldIssue(raw: unknown): string | null;
151
155
  export {};
152
156
  //# sourceMappingURL=request-value-closure.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { SESSION_KINDS, isSessionKind } from "./session-kind.js";
1
2
  export const MEMORY_CAPTURE_SPELLING_MESSAGE = "memoryCapture must be the string 'off' when present (the session-level memory-capture opt-out declaration; absent = capture as usual — there is no 'on' spelling)";
2
3
  export const MEMORY_OFF_SPELLING_MESSAGE = "memory must be the string 'off' when present (the request-level declaration that this run mounts no memory face at all; absent = memory as usual — there is no 'on' spelling; to keep memory mounted but stop this session's content from entering long-term memory, send memoryCapture: 'off' instead)";
3
4
  export const FILE_HISTORY_OFF_SPELLING_MESSAGE = "fileHistory must be the string 'disabled' when present (the request-level declaration that this run records no file history — no backup of an edited file, and no file restore in the same request; absent = the deployment's setting — there is no 'enabled' spelling)";
@@ -49,4 +50,13 @@ export function inheritEnvFieldIssue(raw) {
49
50
  return null;
50
51
  return { message: INHERIT_ENV_VALUE_MESSAGE, facts: inheritEnvFacts(typeof value === "string" ? "unknown_word" : "not_a_string") };
51
52
  }
53
+ export const SESSION_KIND_VALUE_MESSAGE = `sessionKind must be one of ${SESSION_KINDS.map((w) => JSON.stringify(w)).join(" | ")} when present ` +
54
+ `("sidechain" = the session this submission creates is a client-placed sub-conversation: it is not listed by GET /v1/sessions unless asked for with ?sessionKind=sidechain, ` +
55
+ `and it is deleted after BG_AGENT_RETENTION_MS of inactivity; absent = a top-level conversation) — the kind is written once, when the session is created`;
56
+ export function sessionKindFieldIssue(raw) {
57
+ if (raw === null || raw === undefined || typeof raw !== "object" || Array.isArray(raw))
58
+ return null;
59
+ const value = raw.sessionKind;
60
+ return value === undefined || isSessionKind(value) ? null : SESSION_KIND_VALUE_MESSAGE;
61
+ }
52
62
  //# sourceMappingURL=request-value-closure.js.map
@@ -76,12 +76,13 @@ export interface RunAcceptance {
76
76
  observe(ev: {
77
77
  type: string;
78
78
  }): Promise<void>;
79
- /** 终局落行之后:还没在流上见过受理帧、而结果里跑过 turn(只回结果的腿)⇒ 受理。只读 `stats.turns`。不抛、不等。 */
79
+ /** 结果到手、**放掉这条 run 的认领之前**(收场写 / 停驻提交之前):还没在流上见过受理帧、而结果里跑过 turn(只回结果的腿)⇒ 受理。只读 `stats.turns`。不抛;
80
+ * 交回的那一拍等标题器钉住要起名的住客(有界一读;认领在手 ⇒ 会话删不掉 ⇒ 住客就是这条 run 的)与补写落定 —— 腿 await 它再放认领。 */
80
81
  settle(result: {
81
82
  readonly stats: {
82
83
  readonly turns: number;
83
84
  };
84
- }): void;
85
+ }): Promise<void>;
85
86
  /** 提交那一刻就知道的交付文本(腿上没有屏 ⇒ 请求原文);腿上有屏 ⇒ 缺席:各面提交时不写,交付时经 {@link onDelivered} 补。 */
86
87
  readonly objectiveAtSubmit: string | undefined;
87
88
  /**
@@ -35,11 +35,10 @@ export function createRunAcceptance(deps, input) {
35
35
  delivered = objective;
36
36
  if (objective === undefined)
37
37
  return Promise.resolve();
38
- if (input.sessionId)
39
- deps.sessionTitler?.maybeTitle(input.sessionId, objective, input.model, deliver);
38
+ const titled = input.sessionId ? (deps.sessionTitler?.maybeTitle(input.sessionId, objective, input.model, deliver) ?? Promise.resolve()) : Promise.resolve();
40
39
  if (!input.screened)
41
- return Promise.resolve();
42
- return Promise.all(fills.map(({ face, fill }) => runFill(face, fill, objective))).then(() => undefined);
40
+ return titled;
41
+ return Promise.all([titled, ...fills.map(({ face, fill }) => runFill(face, fill, objective))]).then(() => undefined);
43
42
  };
44
43
  return {
45
44
  objectiveAtSubmit: input.screened ? undefined : input.objective,
@@ -51,8 +50,7 @@ export function createRunAcceptance(deps, input) {
51
50
  return Promise.resolve();
52
51
  },
53
52
  settle(result) {
54
- if (result.stats.turns > 0)
55
- void accept(input.screened ? undefined : input.objective);
53
+ return result.stats.turns > 0 ? accept(input.screened ? undefined : input.objective) : Promise.resolve();
56
54
  },
57
55
  onDelivered(face, fill) {
58
56
  if (!input.screened)
package/dist/run-local.js CHANGED
@@ -637,6 +637,7 @@ export async function runLocal(argv, deps = {}) {
637
637
  sharedMemoryStores: undefined,
638
638
  mcpRevocations: undefined,
639
639
  peerDesk,
640
+ runtimeCapsResolver: undefined,
640
641
  });
641
642
  const runnerDeps = {
642
643
  ...sharedRunnerDeps,
package/dist/runs.d.ts CHANGED
@@ -17,6 +17,9 @@ import type { RunAcceptance } from "./run-acceptance.js";
17
17
  import type { SessionWorkspaceRecorder } from "./session-workspace.js";
18
18
  import type { VerifyRoundsSpec } from "./http/verify-rounds.js";
19
19
  import { type ParkLedgerEventType } from "./trace/ledger-events.js";
20
+ import type { LandedAttendantSeat } from "./plugins/landed-attendants.js";
21
+ import type { DelegationRootWitness } from "./delegated-session-owner.js";
22
+ import type { SessionFace } from "./session-incarnation.js";
20
23
  import type { UserInputLedger } from "./user-input-ledger.js";
21
24
  /**
22
25
  * stoppedBy (core 1.252): a service cancel is a USER stop — mark the run's still-running background
@@ -27,8 +30,8 @@ import type { UserInputLedger } from "./user-input-ledger.js";
27
30
  * cross-replica flag poll). Advisory only — the cancel itself must never fail on attribution.
28
31
  *
29
32
  * Access = TASK-scoped children ONLY: owner = the parent taskId (on a RESUME leg: the sessionId — core's
30
- * canonical taskId there), scope = `principal ?? "default"` (core registers children under
31
- * `spec.principal ?? "default"`, so an anonymous run's children live in scope "default" and an access
33
+ * canonical taskId there), scope = the engine-identity cell `engineScopeOfOwner(subject)` (core registers
34
+ * children under its task scope, so an anonymous run's children live in scope "default" and an access
32
35
  * WITHOUT scope matched nothing). Session-scoped children are DELIBERATELY not marked: a cancelled parent
33
36
  * does NOT take them down (CC Backgrounded semantics — core's teardown reaps with skipSessionScoped), so
34
37
  * pre-marking them "user" would misattribute a stop that isn't happening.
@@ -201,10 +204,36 @@ export type ResumeAnchorLeg = "stream" | "sync" | "bg" | "resume";
201
204
  * 到期只放手不取消:那次写照常在飞,迟到落定的锚照样可用(迟到的失败被吞,不重复计数)。
202
205
  * 用户面的读数仍只有一处:会话视图用户消息行的 `taskId?`(present-iff 锚在)与 `resumeAt` 的 404 `resume_at.unknown_event`。
203
206
  */
204
- export declare function settleResumeAnchor(write: () => Promise<void>, where: {
207
+ export declare function settleResumeAnchor(write: (held: () => boolean) => Promise<void>, where: {
205
208
  readonly leg: ResumeAnchorLeg;
206
209
  readonly kind: "prompt" | "turn";
207
210
  }): Promise<void>;
211
+ /** 一条运行腿的两只回退锚写口(缺席 = 这条腿没有那一种锚:没有落定席位 / 没有提示手柄 / 没有叶子读)。 */
212
+ export interface RunAnchorWriter {
213
+ /** 用户提示锚:键 = 这条 run 的 id(客户端手里的回退手柄),值 = 那条用户提交的条目 id。 */
214
+ readonly prompt?: (entryId: string, held: () => boolean) => Promise<void>;
215
+ /** 助手文本锚:键 = 这一轮第一个 text 帧的 eventId,值 = 此刻会话的叶子。 */
216
+ readonly turn?: (eventId: string, held: () => boolean) => Promise<void>;
217
+ }
218
+ /**
219
+ * **运行腿回退锚的唯一写法**(后台腿 / 同步腿 / 流式腿 / 续跑腿同一只):锚只经落定席位的 `putAnchors` 落(`plugins/landed-attendants.ts`:段首锁会话根行判住客),
220
+ * 住客 = **这条 run 自己的那一位** —— 受理时见证到的(`LegFacts.sessionWitness`,带化身);见证里还没有化身(这条腿新建了会话)⇒ 第一笔锚之前读一次头钉住。
221
+ * 钉住发生在腿还在等这一笔的期限之内(`held()`:腿被这一笔挡着 ⇒ run 在跑、认领在手 ⇒ 会话删不掉),到期之后才完成的钉住不发写;已发出的写到期只放手不取消
222
+ * (`settleResumeAnchor`),但它绑着钉住的那一位 —— run 收场、会话被删、同 id 被别人重建之后才落的那一笔落不进新住客,也落不进空闲 id(段判「不是那一位」⇒ 零写,
223
+ * 记一次同一只 fail-open,`reason=error`)。没有席位 ⇒ 两只都缺席(这台部署不存锚)。
224
+ */
225
+ export declare function createRunAnchorWriter(args: {
226
+ readonly seat: Pick<LandedAttendantSeat, "putAnchors"> | undefined;
227
+ readonly sessionId: string;
228
+ readonly owner: string | null;
229
+ /** 用户提示锚的键(这条 run 的 id);缺 ⇒ 不写提示锚(没有手柄 / 续跑腿接着别的腿的 run)。 */
230
+ readonly promptKey: string | undefined;
231
+ readonly witness: DelegationRootWitness | undefined;
232
+ /** 头读投成的住客(属主存取器的 `sessionOccupantReadOf`);缺 ⇒ 见证里没有化身时钉不住 ⇒ 不写。 */
233
+ readonly occupantOf: ((sessionId: string) => Promise<SessionFace | undefined>) | undefined;
234
+ /** 会话此刻的叶子(属主存取器的 `sessionFactsReadOf` 投影);缺 ⇒ 不写助手文本锚。 */
235
+ readonly leafOf: ((sessionId: string) => Promise<string | null | undefined>) | undefined;
236
+ }): RunAnchorWriter;
208
237
  export declare class TurnAnchorCapture {
209
238
  private readonly capture;
210
239
  /** 这只捕获器在哪条腿上(写失败的 fail-open 探针行带它)。 */
@@ -227,7 +256,7 @@ export declare class TurnAnchorCapture {
227
256
  private turnHadTool;
228
257
  /** 用户提交是不是开场提示的唯一读法({@link createPromptCommitReader};开场窗 + 闩 —— 开场提示之后打断标记 / steer 不改锚;附件帧不是提问)。 */
229
258
  private readonly promptCommit;
230
- constructor(capture: ((eventId: string) => Promise<void>) | undefined,
259
+ constructor(capture: ((eventId: string, held: () => boolean) => Promise<void>) | undefined,
231
260
  /** 这只捕获器在哪条腿上(写失败的 fail-open 探针行带它)。 */
232
261
  leg: Exclude<ResumeAnchorLeg, "stream">,
233
262
  /** R8 (CC-parity rewind): capture a USER-message anchor straight from the
@@ -242,7 +271,7 @@ export declare class TurnAnchorCapture {
242
271
  * user-role commit in the same run — the interruption marker, a steer — never moves it. The capture instance is per
243
272
  * run: a leg that CONTINUES an existing run (same taskId) wires no `captureUser` (its opening prompt was anchored
244
273
  * by the first leg). */
245
- captureUser?: ((entryId: string) => Promise<void>) | undefined);
274
+ captureUser?: ((entryId: string, held: () => boolean) => Promise<void>) | undefined);
246
275
  /** A `human_input` arrived — the objective's declared attachments open the frame window of {@link createPromptCommitReader} (another applied input inside the window voids it). */
247
276
  onHumanInput(ev: {
248
277
  readonly type: string;
@@ -359,7 +388,7 @@ cascade?: CascadeConfig, instrumentDegenerate?: (result: TaskResult) => void, pl
359
388
  elicitOwner?: string | null,
360
389
  /** E18 resume-at: capture the turn's (firstTextEventId → leaf entryId) anchor at each turn_end. Built by the caller
361
390
  * closing over {sessionId, owner, the session store's head read (`headOf(id)?.leafId`), resumeAnchorStore}; undefined ⇒ no anchor store wired. */
362
- captureTurnAnchor?: (eventId: string) => Promise<void>,
391
+ captureTurnAnchor?: (eventId: string, held: () => boolean) => Promise<void>,
363
392
  /** MF-Fleet (shell-host contract): the run-scoped fleet-row publisher. `onStart` adds the live row; `onEvent` accrues
364
393
  * tokens (turn_end) + subagent child rows (task_progress); `onTerminal` flips/removes it. Fired for ALL legs
365
394
  * (verify/cascade/plain-stream) so a background run TRANSITIONS its `GET /v1/fleet/stream` row exactly like the
@@ -367,7 +396,7 @@ captureTurnAnchor?: (eventId: string) => Promise<void>,
367
396
  fleetPublisher?: FleetRunPublisher,
368
397
  /** R8 (CC-parity rewind): capture a USER-message anchor (keyed by this run's taskId) from `message_committed{role:"user"}`
369
398
  * — the CC "rewind to the prompt" target. undefined ⇒ no anchor store wired. */
370
- captureUserMessageAnchor?: (entryId: string) => Promise<void>,
399
+ captureUserMessageAnchor?: (entryId: string, held: () => boolean) => Promise<void>,
371
400
  /** §4④ : AskUserQuestion LIVE coordinator — establishes a per-run onQuestion ALS context (like `elicitation`)
372
401
  * so an AskUserQuestion tool call routes to this run's stream + owner-gated respond. undefined ⇒ headless default. */
373
402
  question?: QuestionCoordinator,
@@ -457,7 +486,7 @@ workspaceRecorder?: SessionWorkspaceRecorder,
457
486
  */
458
487
  runSkills?: readonly RunSkillEntry[],
459
488
  /**
460
- * 这次提交登记的工作目录(部署事实 `LegFacts.registeredCwd`)。只在两条组合腿(verify / cascade)上经 `compositionLegRoot` 交给引擎 ——
489
+ * 这条腿跑在的工作目录(部署事实 `LegFacts.sessionBindings.cwd`)。只在两条组合腿(verify / cascade)上经 `compositionLegRoot` 交给引擎 ——
461
490
  * 梯子的级 / 验证器腿跑在引擎现铸的会话里,工厂按会话读不到它;普通流形跑在根会话上,工厂自己读得到。缺席 = 没登记 ⇒ 每一条腿照旧落临时目录。
462
491
  */
463
492
  registeredCwd?: string): Promise<void>;
package/dist/runs.js CHANGED
@@ -9,9 +9,8 @@ import { TOOL_DECLARATION_HTTP_STATUS } from "./tool-declaration-http-status.js"
9
9
  import { isSessionErrorCode, sessionErrorHttp } from "./session-error-wire.js";
10
10
  import { redactErrorMessage } from "./observability/run-terminal.js";
11
11
  import { withSubject } from "./observability/principal-context.js";
12
- import { partsOf } from "./owner-brands.js";
13
12
  import { recoveredSubject } from "./owner-faces.js";
14
- import { taskSpecIdentityOf } from "./engine-identity.js";
13
+ import { engineScopeOfOwner } from "./engine-identity.js";
15
14
  import { withModelReachScope } from "./model-reach-scope.js";
16
15
  import { createInstructionSelectionLeg } from "./instruction-selection.js";
17
16
  import { redactSecrets } from "./trace/redact.js";
@@ -62,11 +61,10 @@ export async function taskHandleStop(registry, handle, access, agentStore) {
62
61
  return registry.stopTask(handle, access);
63
62
  }
64
63
  export function markChildrenStoppedByUserOnAbort(signal, taskId, subject) {
65
- const recovered = recoveredSubject(subject);
66
- const principal = taskSpecIdentityOf(recovered === undefined ? undefined : partsOf(recovered)).principal;
64
+ const scope = engineScopeOfOwner(subject);
67
65
  const mark = () => {
68
66
  try {
69
- defaultTaskRegistry.markStopSourceForOwner({ owner: taskId, scope: principal ?? "default" }, "user", { skipSessionScoped: true });
67
+ defaultTaskRegistry.markStopSourceForOwner({ owner: taskId, scope }, "user", { skipSessionScoped: true });
70
68
  }
71
69
  catch {
72
70
  }
@@ -79,6 +77,10 @@ export function markChildrenStoppedByUserOnAbort(signal, taskId, subject) {
79
77
  }
80
78
  const EXECUTION_ENV_REFUSAL_HTTP_STATUS = {
81
79
  "env.home_unconfirmed": 409,
80
+ "env.snapshot_bucket_missing": 409,
81
+ "env.occupant_missing": 409,
82
+ "env.sandbox_refused": 409,
83
+ "env.host_key_rejected": 409,
82
84
  };
83
85
  const PREPARE_REFUSAL_HTTP_STATUS = {
84
86
  "resume.session_not_found": 404,
@@ -234,13 +236,52 @@ export function createPromptCommitReader() {
234
236
  };
235
237
  }
236
238
  export async function settleResumeAnchor(write, where) {
239
+ let open = true;
237
240
  try {
238
- await withinIoDeadline(write);
241
+ await withinIoDeadline(() => write(() => open));
239
242
  }
240
243
  catch (err) {
241
244
  const why = err instanceof IoDeadlineExceeded ? `reason=timeout boundMs=${BASELINE_IO_DEADLINE_MS}` : `reason=error error=${err instanceof Error ? err.message : String(err)}`;
242
245
  recordFailOpen("server.resume-anchor.capture-failed", `leg=${where.leg} kind=${where.kind} ${why}`);
243
246
  }
247
+ finally {
248
+ open = false;
249
+ }
250
+ }
251
+ export function createRunAnchorWriter(args) {
252
+ const { seat, sessionId, owner, promptKey, witness, occupantOf, leafOf } = args;
253
+ if (seat === undefined)
254
+ return {};
255
+ let pinned = witness !== undefined && witness.incarnation !== null ? { owner: witness.owner, incarnation: witness.incarnation } : undefined;
256
+ const pin = async () => (pinned ??= await occupantOf?.(sessionId));
257
+ const put = async (landed, eventId, entryId) => {
258
+ const out = await seat.putAnchors(sessionId, landed, [{ eventId, entryId }], owner);
259
+ if (!out.landed)
260
+ throw new Error("the session's occupant is no longer this run's — the anchor was not written");
261
+ };
262
+ return {
263
+ ...(promptKey !== undefined
264
+ ? {
265
+ prompt: async (entryId, held) => {
266
+ const landed = await pin();
267
+ if (landed !== undefined && held())
268
+ await put(landed, promptKey, entryId);
269
+ },
270
+ }
271
+ : {}),
272
+ ...(leafOf !== undefined
273
+ ? {
274
+ turn: async (eventId, held) => {
275
+ const landed = await pin();
276
+ if (landed === undefined || !held())
277
+ return;
278
+ const leaf = await leafOf(sessionId);
279
+ if (leaf)
280
+ await put(landed, eventId, leaf);
281
+ },
282
+ }
283
+ : {}),
284
+ };
244
285
  }
245
286
  export class TurnAnchorCapture {
246
287
  capture;
@@ -262,7 +303,7 @@ export class TurnAnchorCapture {
262
303
  const prompt = this.promptCommit(committed);
263
304
  const captureUser = this.captureUser;
264
305
  if (prompt !== undefined && captureUser)
265
- await settleResumeAnchor(() => captureUser(prompt), { leg: this.leg, kind: "prompt" });
306
+ await settleResumeAnchor((held) => captureUser(prompt, held), { leg: this.leg, kind: "prompt" });
266
307
  }
267
308
  onText(eventId) {
268
309
  if (this.firstTextEventId === undefined) {
@@ -278,7 +319,7 @@ export class TurnAnchorCapture {
278
319
  const capture = this.capture;
279
320
  const handle = this.firstTextEventId;
280
321
  if (handle && !this.turnHadTool && capture)
281
- await settleResumeAnchor(() => capture(handle), { leg: this.leg, kind: "turn" });
322
+ await settleResumeAnchor((held) => capture(handle, held), { leg: this.leg, kind: "turn" });
282
323
  this.firstTextEventId = undefined;
283
324
  this.turnHadTool = false;
284
325
  }
@@ -388,6 +429,7 @@ export async function runInBackground(runner, spec, runStore, taskId, parkCommit
388
429
  await sink.appendParked(word, grafted);
389
430
  const sealed = sealResultForExit(grafted);
390
431
  reached = { kind: word, result: sealed };
432
+ await acceptance?.settle(sealed);
391
433
  const committed = await commitParkRow({
392
434
  runStore, taskId, word, result: sealed, card: { token, sessionId: spec.sessionId }, cards: checkpointCards, leg: "bg",
393
435
  legId: parkCommit.legId, staleWindowMs: parkCommit.runStaleSec === undefined ? undefined : parkCommit.runStaleSec * 1000,
@@ -396,7 +438,6 @@ export async function runInBackground(runner, spec, runStore, taskId, parkCommit
396
438
  reached = undefined;
397
439
  settledAsCancel = committed.row?.errorCode === CANCELLED_CODE;
398
440
  }
399
- acceptance?.settle(sealed);
400
441
  };
401
442
  const settleLegResult = async (engineResult) => {
402
443
  const result = atHostBoundary(engineResult);
@@ -410,10 +451,10 @@ export async function runInBackground(runner, spec, runStore, taskId, parkCommit
410
451
  const safe = await withModelUsage(sealResultForExit(settled));
411
452
  await sink.appendDone(safe);
412
453
  reached = { kind: "done", result: safe };
454
+ await acceptance?.settle(safe);
413
455
  const plane = outwardPlane(safe.terminal);
414
456
  await runStore.setTerminal(taskId, plane.status, safe, plane.errorMessage ?? null);
415
457
  await releaseKeyIfZeroWorkRefusal(runStore, taskId, safe);
416
- acceptance?.settle(safe);
417
458
  };
418
459
  const instructionLeg = createInstructionSelectionLeg();
419
460
  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: runSkills }), usage: { modelUsage, taskId } });
@@ -609,12 +650,12 @@ export async function runInBackground(runner, spec, runStore, taskId, parkCommit
609
650
  if (reached?.kind === "done") {
610
651
  const plane = outwardPlane(reached.result.terminal);
611
652
  settledAsCancel = failureCodeOf(reached.result.terminal) === CANCELLED_CODE;
653
+ await acceptance?.settle(reached.result);
612
654
  await runStore.setTerminal(taskId, plane.status, reached.result, plane.errorMessage ?? null);
613
655
  await releaseKeyIfZeroWorkRefusal(runStore, taskId, reached.result);
614
- acceptance?.settle(reached.result);
615
656
  }
616
657
  else if (reached !== undefined) {
617
- acceptance?.settle(reached.result);
658
+ await acceptance?.settle(reached.result);
618
659
  }
619
660
  else if (thrownCancelCause === undefined && observedDone !== undefined && heldParkOf(atHostBoundary(observedDone)) !== undefined) {
620
661
  recordFailOpen("server.runs.terminal-ledger-write-failed", `arm=park-held ${message}`);
@@ -92,8 +92,9 @@ export declare function compileCommandPolicyLayers(rules: CommandRule[] | undefi
92
92
  * `plan` 是有出口的**生命周期词**(`plan_review` 把计划带出 plan),不是围栏:它是**提交入口的模式词地板**({@link autonomyModeFloor}),
93
93
  * 盖在每一个 spec 出生点一次(词 = {@link entryPermissionMode} 的 `effective`):受理门盖进存储的提交体(`task-settings.ts` `stampEntryPermissionMode`)、run-local 盖在它自建的 spec 上、
94
94
  * leader run 盖在它出生时的入口体上(`leader/run-entry.ts`;入口词是 `plan` 的 leader run 进不了那条车道 —— worker 会话没有计划审阅通道 ⇒ 受理口响亮拒)。此后一条 run 的模式词
95
- * 只有存储体一个源,写者是每一个出生点(新的持久化写都经 `http/checkpoint-ctx.ts` `buildCheckpointCtx`)与 `plan_review` 写回。按段折一个模式词 = 第二个源:被批准的计划会在下一次挂起→续跑
96
- * 被拉回 plan。收紧入参的型上因此没有 `permissionMode`(`governance-overrides.ts`)。(上一个发布版本里本档不碰模式词,按段折的是只读手 +
95
+ * 只有存储体一个源,写者是每一个出生点(新的持久化写都经 `http/checkpoint-ctx.ts` `buildCheckpointCtx`)、`plan_review` 写回与续跑口(停驻 run 的存储体被重放之前过一次**现**地板、
96
+ * 钳过的随 CAS 赢点写回;`permission-mode-admission.ts` `stampReplayEntryMode`,地板的出口词 —— `plan_review` 批准写下的词 —— 原样读)。按段折一个模式词 = 第二个源:
97
+ * 被批准的计划会在下一次挂起→续跑被拉回 plan;续跑口不是按段折 —— 它与提交口同一只入口一步,写的是同一个源,且不碰出口词。收紧入参的型上因此没有 `permissionMode`(`governance-overrides.ts`)。(上一个发布版本里本档不碰模式词,按段折的是只读手 +
97
98
  * 写面 `roots`、与 `read-only` 同形 —— 运维 plan 那时根本不进 plan 模式,见下一段。)
98
99
  *
99
100
  * 🔴 **为什么 `plan` 不再与 `read-only` 同形**:修前两档同映「只读手 + roots」,外加 `resolve-spec.ts` 见只读手就剥委派工具 —— 于是 `plan` 有三套
@@ -145,8 +146,8 @@ export interface EntryPermissionMode<W extends PermissionMode | undefined = Perm
145
146
  * 无人值守腿(`unattended-permission-mode.ts` `withUnattendedLeg`:`default` 钳在 spec 的词之下);
146
147
  * · 「这个词会变成什么」的读者 —— 能力面写面开不开({@link autonomyKeepsOpenWriteFace}:bypass 声明过地板)、能力面 auto 读面
147
148
  * (`auto-mode-face.ts` `judgeAutoModeArming`:意图过地板;`clamped` 且不是 auto ⇒ 未武装原因 `operator_floor`)、
148
- * 存量停驻体能否按此刻的地板重放(`http/stored-body-replay.ts`:体的词经钳会变 ⇒ 拒)。
149
- * `test/entry-permission-mode.test.ts` 按语法树钉:core 钳在 `src/` 只此一处调用,上面八处各调本函数一次。
149
+ * 受理判官的入口一步(`permission-mode-admission.ts` `entryStep`:提交口盖章、在飞切档口、续跑口 —— 停驻 run 的存储体被重放之前过**现**地板 —— 三口同经这一处)。
150
+ * `test/entry-permission-mode.test.ts` 按语法树钉:core 钳在 `src/` 只此一处调用,本函数七处调用点各一次。
150
151
  */
151
152
  export declare function entryPermissionMode<W extends PermissionMode | undefined>(word: W, floor: PermissionMode | undefined): EntryPermissionMode<W>;
152
153
  /**
@@ -1,4 +1,5 @@
1
1
  import type { IncomingMessage } from "node:http";
2
+ import type { EntriesStreamOptions } from "./session-sync-kernel.js";
2
3
  import { type SessionHead, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
3
4
  import type { ServiceConfig } from "./config-types.js";
4
5
  import type { Logger } from "./observability/logger.js";
@@ -9,6 +10,8 @@ import type { TaskRequestBody } from "./http/wire-types.js";
9
10
  import type { StagingHandle } from "./session-sync-kernel.js";
10
11
  import type { SessionDeletionFace } from "./session-deletion.js";
11
12
  import { type SessionBirth } from "./session-incarnation.js";
13
+ import { type IdleSession, type IdleSessionCursor, type SessionKind, type SessionKindReading } from "./session-kind.js";
14
+ import type { SessionListQuery } from "./plugins/store-contracts.js";
12
15
  import type { ClientSessionRowWrite, ReplaySessionRowWrite, ReplaySessionRowWriteOutcome, SessionRowAnswer, SessionRowPatch, SessionRowWriteOutcome } from "./session-row.js";
13
16
  import type { ExecutionPlacementSeat } from "./execution-placement.js";
14
17
  import type { SessionUnreadable } from "./session-error-wire.js";
@@ -56,6 +59,10 @@ export interface SessionListItem {
56
59
  * by a fork after lineage was recorded (an older fork is not backfilled: its source is unknown). OMIT-when-absent, never `null`. Additive key, passed
57
60
  * through unprojected (`sessions-list.ts`). Same name and meaning as `SessionSummary.forkedFrom` in `plugins/store-contracts.ts`. */
58
61
  forkedFrom?: string;
62
+ /** The session's kind (`session-kind.ts`): present-iff the session was created by a submission that declared `sessionKind`; a top-level session
63
+ * never carries it (OMIT-when-absent, never `null`). The default list view lists only top-level sessions; `?sessionKind=<word>` lists that kind.
64
+ * Additive key, passed through unprojected (`sessions-list.ts`). Same name and meaning as `SessionSummary.sessionKind` in `plugins/store-contracts.ts`. */
65
+ sessionKind?: SessionKind;
59
66
  }
60
67
  /**
61
68
  * 会话店的行格写原语(规则与判序:`session-row.ts` 头注「写原语」)—— 按入口分两只重载:客户端写(PATCH)的结局闭集四员;行重放(2c 导入落定后的那一笔)
@@ -74,7 +81,7 @@ export interface SessionRowCellsWriter {
74
81
  * 🔴 2026-08-01 复审逮到:守卫原本是**逐条路由白名单**(`PUT …/policy`、`POST …/sync/import`),
75
82
  * 而 `DELETE /v1/sessions/:id`(删整条会话)不在其中 —— 同一风险形、爆炸半径**更大**,却没门。
76
83
  *
77
- * 风险形逐字同源(守卫自己的注释):「无 service token + 未开 `ALLOW_UNAUTHED_WRITES`」这一形下
84
+ * 风险形逐字同源(守卫自己的注释):「请求没带凭据门认的凭据 + 未开 `ALLOW_UNAUTHED_WRITES`」这一形下
78
85
  * fleet 写档(面表 fleet 列的 `dev_open` 格,`owner-faces.ts` 的 `listFilterOf`)判 fleet-wide ⇒ 集群内**任意**不报主体的调用方可对**任意** session 动手(报了主体的调用方只动得到那位主体的,`principal-gate.ts` 的 `actsForDeployment`)。
79
86
  * policy 腿担心的是「forged principal 能 tighten/DoS 一个会话的工具」;DELETE 直接把会话删掉。
80
87
  *
@@ -104,6 +111,10 @@ export type SessionOwnerCell = {
104
111
  export declare function claimCallerOf(subject: Subject | undefined, config: DeploymentShapeInput<"principal">): Caller;
105
112
  /** 提交挂接「没有这只会话」那一答的句子(requireExisting 缺行与认领面看不见同一句;投影 `attach_absent`)。 */
106
113
  export declare const ATTACH_ABSENT_MESSAGE = "requireExistingSession: session does not exist (resume.session_not_found)";
114
+ /** 带种类的提交撞上一只种类不同的既有会话的 409(律 `session-kind.ts`):体上机读位 `sessionKind` = 行上的词(顶层行缺席)。句子不回显请求的词以外的任何行事实。 */
115
+ export declare const SESSION_KIND_MISMATCH_MESSAGE = "this session already exists with a different session kind \u2014 a session's kind is written once, when the session is created, and never changes; send the same sessionKind the session was created with, omit sessionKind to run the session as it is, or start a new session";
116
+ /** 部署记不了种类(会话店没有出生回执面 / 种类读)⇒ 400 `request.precondition_unmet`(受理门读同一只能力事实 `capability-facts.ts` 的 `sessionKinds`,先拒在那里;这里是纵深)。 */
117
+ export declare const SESSION_KIND_UNRECORDABLE_MESSAGE = "sessionKind requires a session store that records a session's kind and ages such sessions out (DB_BACKEND=mysql|pg|local) \u2014 this deployment cannot, so the session would be created as a top-level conversation; capabilities.sessionKinds lists the kinds a deployment accepts";
107
118
  /** 读一只会话行的 {@link SessionOwnerCell}:有主行 = 属主那一格(`owner-faces.ts` 的 `ownerCellOf`,属主列读数的唯一铸法);无主时问一次店的 `placementOf`
108
119
  * (既有读口;店不交它 = 不实现 placement = 没有 placed 行)。`owner` = 调用点那一读。 */
109
120
  export declare function sessionOwnerCellOf(store: Pick<OwnerAwareSessionStore, "placementOf"> | undefined, sessionId: string, owner: string | null): Promise<SessionOwnerCell>;
@@ -141,29 +152,19 @@ export type OwnerAwareSessionStore = SessionStore & {
141
152
  * `includeUnowned` = [ref] 窄互认第二臂的**第三条件**(`owner IS NULL`)的 SQL 承载位:置真时
142
153
  * 谓词放宽为 `owner = ? OR owner IS NULL`。只在 `owner` 在场时有意义(路由已判①③);`owner` 缺席
143
154
  * (fleet-wide)时本位无效——那一面本来就不钉 owner。 */
144
- listSessions?: (opts: {
145
- owner?: string;
146
- includeUnowned?: boolean;
147
- cursor?: {
148
- lastActivityAt: string;
149
- sessionId: string;
150
- };
151
- limit: number;
152
- q?: string;
153
- }) => Promise<SessionListItem[]>;
155
+ listSessions?: (opts: SessionListQuery) => Promise<SessionListItem[]>;
154
156
  /** The SAME list face as {@link listSessions} (same SELECT, same mapper, same predicate and keyset) with each row's occupant — the row's owner and
155
157
  * incarnation as read in that one read. The incarnation never enters the row (it is not a wire fact: `session-incarnation.ts`); this pair is for the
156
158
  * in-process reader that must address a row by occupant — the peer directory's `PeerSessionRecord.occupant`. `listSessions` is this face's rows. */
157
- listSessionRows?: (opts: {
158
- owner?: string;
159
- includeUnowned?: boolean;
160
- cursor?: {
161
- lastActivityAt: string;
162
- sessionId: string;
163
- };
164
- limit: number;
165
- q?: string;
166
- }) => Promise<Array<SessionRowAnswer<SessionListItem>>>;
159
+ listSessionRows?: (opts: SessionListQuery) => Promise<Array<SessionRowAnswer<SessionListItem>>>;
160
+ /** 按 id 的种类读(律 `session-kind.ts`;授权口的种类判与会话视图的 `sessionKind` 读它):读到它时行上的住客 + 种类。三只持久店都交,包装原样转发。 */
161
+ sessionKindOf?: (sessionId: string) => Promise<SessionKindReading | undefined>;
162
+ /** 老化候选面(律 `session-kind.ts`;唯一读者 = 维护拍的老化腿 `boot/session-age-out.ts`):种类为 `kind`、最后活动早于 `idleBefore`、当下无活引用的会话(在删的照交),
163
+ * 最老先出、`(lastActivityAt, sessionId)` 升序 keyset 续页(`after` = 上一页最后一条)。与 `listPlaced`、删除面 `pending()` 同族的用途专一枚举 —— 不出列表投影。 */
164
+ listIdleOfKind?: (kind: SessionKind, idleBefore: Date, page: {
165
+ readonly after?: IdleSessionCursor;
166
+ readonly limit: number;
167
+ }) => Promise<IdleSession[]>;
167
168
  /** ONE session's list row — the SAME projection {@link listSessions} emits for it (same columns, same mapper). `undefined` = not a
168
169
  * row of the list face (no such session, or a placed / delegation-transcript session, which never lists). Reader: the 2c
169
170
  * manifest (`title` / `pinned`). */
@@ -215,10 +216,7 @@ export type OwnerAwareSessionStore = SessionStore & {
215
216
  * `opts.batchSize` is the page size (default 500). Resolves to `null` (NOT an empty iterable) when the session does
216
217
  * not exist (the existence probe is async — `session_meta` first, like {@link exportEntries} — so the method returns
217
218
  * a Promise of the iterable-or-null; the iterable itself then pages lazily). The read half of exportEntries, paged. */
218
- exportEntriesStream?: (sessionId: string, opts?: {
219
- afterSeq?: number;
220
- batchSize?: number;
221
- }) => Promise<AsyncIterable<SessionTreeEntry> | null>;
219
+ exportEntriesStream?: (sessionId: string, opts: EntriesStreamOptions) => Promise<AsyncIterable<SessionTreeEntry> | null>;
222
220
  /** 2c session-sync — IMPORT a verbatim entry log into `sessionId`, re-stamping `owner` to the
223
221
  * authenticated importing principal (never the bundle's). The log is passed through core's
224
222
  * `validateEntriesForImport` (the single fail-closed invariant gate) before any write. */
@@ -303,7 +301,7 @@ export interface AuthContext {
303
301
  * `PROJECT_ID_REGEX` 门后折小写回传;[ref]/[ref] 宽读严写 —— 词法带 /i 收下大写形,但派生 scope 键
304
302
  * 与 config.projects 查表都只认这一个归一形,否则同一个项目会按大小写分成两只记忆盘)。
305
303
  * 身份半场(scope 的 tenant 段)永远来自 verified principal — projectId 只选「哪个项目盘/哪条登记」
306
- * (config.projects 查表键 + proj: 键的 projectId 段),不是 capability。main.ts 拿它查 defaultScopes。 */
304
+ * (config.projects 查表键 + proj: 键的 projectId 段),不是 capability。装配器拿它判 defaultScopes 种子项目(对提交者可见才算,`project-registry.ts`)。 */
307
305
  resolvedProjectId?: string;
308
306
  }
309
307
  /** 授权口的入参:请求、体,与提交出口收候选的口(`submission-exit.ts`;只有登记了新行才调,传了也可能一次不调)。 */
@@ -385,9 +383,9 @@ export declare const CHECKPOINT_PUBLIC_SCOPE: "_";
385
383
  *
386
384
  * 写侧不是本仓能单方面改的:core 的 `treeScope = reviveClaim?.row.scope ?? ctx.principal ??
387
385
  * opts.background?.scope` 同时喂**进程内** `defaultTaskRegistry` 与 **durable** 行,而本仓/`core` 全域的
388
- * 读侧是一整族 `principal ?? "default"`(`runs.ts` markStopSourceForOwner、会话的引擎租户分区 `session-children-retire.ts`
389
- * `sessionTenantScope`(删除协调器的子代 / 信箱 / 后台 shell 三腿)、`http/routes/runs.ts` 的 subagent output/stream 三口、`agents-roster.ts`、
390
- * `fleet.ts`)。⇒ **把铸点改成 `"_"` 会把这一整族读者一起打瞎**(本车实测:`subagent-tail-content-supply`
386
+ * 读侧经唯一换算 `engine-identity.ts` 的 `engineScopeOf` / `engineScopeOfOwner`(匿名 ⇒ `"default"`;读者:`runs.ts` markStopSourceForOwner、
387
+ * 会话的引擎租户分区 `session-children-retire.ts` `sessionTenantScope`(删除协调器的子代 / 信箱 / 后台 shell 三腿)、`http/routes/runs.ts` 的 subagent
388
+ * output/stream 与句柄三口、`agents-roster.ts`、`fleet.ts` 的工作流行)。⇒ **把铸点改成 `"_"` 会把这一整族读者一起打瞎**(本车实测:`subagent-tail-content-supply`
391
389
  * 的 tail 建连当场 404)。所以归一只能发生在**读侧**:checkpoint 侧拿着 `"_"` 去查 bg 分区时,
392
390
  * 显式把这两个键当同一格。
393
391
  */
@@ -396,9 +394,9 @@ export declare const BACKGROUND_REGISTRY_ANON_SCOPE: "default";
396
394
  * checkpoint 的 scope → 该在哪些 **bg 注册簿分区**里找它的 parked 子代([ref])。
397
395
  *
398
396
  * 匿名(`"_"`)⇒ 两个都查:`"_"`(将来若统一了写侧)与 `"default"`(今天 core 真写下去的那个)。
399
- * 其余 ⇒ 原样单查:有 principal 时两套约定折出**同一个**串(`encodeCheckpointScope` 只在 null/undefined
400
- * 时才替换,core 那侧也只在 `ctx.principal` 缺席时才落兜底),不存在第二个候选;而多租户部署里一个
401
- * **真名叫 `default`** 的租户必须只匹配自己那格 —— 所以别把别名做成双向。
397
+ * 其余 ⇒ 引擎那一格单查:停驻行的 scope 列是**主体**,bg 行是引擎按它的任务 scope 写的(落点今天只写人)⇒ 经唯一换算
398
+ * `engine-identity.ts` 的 `engineScopeOfOwner`:没带场景的主体逐字节同(两套约定折出同一个串),带场景的主体 ⇒ 那个人;
399
+ * 不存在第二个候选。多租户部署里一个**真名叫 `default`** 的租户只匹配自己那格 —— 所以别把别名做成双向。
402
400
  */
403
401
  export declare function backgroundScopesForCheckpointScope(scope: string): readonly string[];
404
402
  /** Encode a subject (the partition key — `owner-brands.ts`; or none) as the checkpoint `scope` column value. A row owner read back from a store is the