@sema-agent/server 7.137.0 → 7.139.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 (328) hide show
  1. package/MIGRATION.md +600 -142
  2. package/README.md +7 -3
  3. package/README.zh-CN.md +6 -3
  4. package/USAGE.md +132 -41
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/lib/lanes.json +3 -2
  7. package/dist/adoption/wire.d.ts +3 -3
  8. package/dist/approval-card.d.ts +23 -16
  9. package/dist/approval-card.js +1 -0
  10. package/dist/audit.js +1 -0
  11. package/dist/background-agent-name.d.ts +5 -0
  12. package/dist/background-agent-name.js +5 -0
  13. package/dist/bench/s1/arms.d.ts +1 -1
  14. package/dist/bench/s1/arms.js +1 -3
  15. package/dist/bench/s1/run-firm.js +2 -2
  16. package/dist/boot/budget-tracing.d.ts +1 -12
  17. package/dist/boot/deployment-mods.d.ts +46 -16
  18. package/dist/boot/deployment-mods.js +82 -6
  19. package/dist/boot/device-lane.d.ts +8 -3
  20. package/dist/boot/device-lane.js +7 -2
  21. package/dist/boot/execution-env.d.ts +4 -2
  22. package/dist/boot/execution-env.js +16 -24
  23. package/dist/boot/execution-lane.d.ts +1 -1
  24. package/dist/boot/governance-seams.d.ts +12 -18
  25. package/dist/boot/governance-seams.js +10 -24
  26. package/dist/boot/leader.d.ts +3 -0
  27. package/dist/boot/leader.js +1 -0
  28. package/dist/boot/memory-consolidation.js +2 -2
  29. package/dist/boot/org-delivery.js +15 -2
  30. package/dist/boot/resolve-spec.d.ts +4 -1
  31. package/dist/boot/resolve-spec.js +29 -19
  32. package/dist/boot/retention-lane.d.ts +14 -8
  33. package/dist/boot/retention-lane.js +2 -8
  34. package/dist/boot/runner-deps.d.ts +10 -4
  35. package/dist/boot/runner-deps.js +2 -0
  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-04-stores.d.ts +1 -12
  40. package/dist/boot/stage-05-execution-env.d.ts +3 -12
  41. package/dist/boot/stage-05-execution-env.js +8 -5
  42. package/dist/boot/stage-06-runners.d.ts +3 -12
  43. package/dist/boot/stage-06-runners.js +5 -3
  44. package/dist/boot/stage-07-capability-layer.d.ts +3 -12
  45. package/dist/boot/stage-07-capability-layer.js +2 -2
  46. package/dist/boot/stage-08-reapers.d.ts +3 -12
  47. package/dist/boot/stage-09-leader.d.ts +3 -12
  48. package/dist/boot/stage-09-leader.js +2 -3
  49. package/dist/boot/stage-10-http-server.d.ts +3 -12
  50. package/dist/boot/stage-10-http-server.js +5 -3
  51. package/dist/boot/stores.js +4 -4
  52. package/dist/boot/web-admin.d.ts +5 -2
  53. package/dist/boot/web-admin.js +5 -5
  54. package/dist/brain.js +5 -5
  55. package/dist/budget.d.ts +17 -15
  56. package/dist/budget.js +2 -2
  57. package/dist/capabilities/mcp-probe.d.ts +36 -11
  58. package/dist/capabilities/mcp-probe.js +44 -5
  59. package/dist/capabilities/scenarios.d.ts +1 -1
  60. package/dist/capability-facts.d.ts +21 -7
  61. package/dist/capability-facts.js +13 -3
  62. package/dist/checkpoint-identity.d.ts +13 -6
  63. package/dist/config-catalog.js +14 -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 +19 -24
  67. package/dist/config-provider.js +27 -12
  68. package/dist/config-types.d.ts +25 -12
  69. package/dist/config.d.ts +33 -0
  70. package/dist/config.js +52 -20
  71. package/dist/delegated-session-owner.d.ts +2 -2
  72. package/dist/deployment-shape.d.ts +7 -7
  73. package/dist/device-enrollment.d.ts +41 -2
  74. package/dist/device-enrollment.js +27 -12
  75. package/dist/device-store.d.ts +58 -85
  76. package/dist/device-store.js +19 -49
  77. package/dist/device-ws-hub.js +2 -1
  78. package/dist/env-name-allowlist-knobs.d.ts +5 -4
  79. package/dist/exec-cut.d.ts +60 -0
  80. package/dist/exec-cut.js +36 -0
  81. package/dist/execution-lane-caps.d.ts +19 -0
  82. package/dist/execution-lane-caps.js +10 -0
  83. package/dist/execution-placement.d.ts +259 -0
  84. package/dist/execution-placement.js +199 -0
  85. package/dist/field-conflict.d.ts +3 -2
  86. package/dist/field-conflict.js +1 -0
  87. package/dist/gate-settlement.d.ts +71 -0
  88. package/dist/gate-settlement.js +40 -0
  89. package/dist/hooks/classic-stations.d.ts +1 -0
  90. package/dist/hooks/classic-stations.js +1 -0
  91. package/dist/hooks/command-spawn.d.ts +2 -2
  92. package/dist/hooks/hook-runner.js +14 -14
  93. package/dist/hosted-posture.d.ts +2 -2
  94. package/dist/http/admission.js +10 -19
  95. package/dist/http/checkpoint-ctx.d.ts +21 -12
  96. package/dist/http/checkpoint-ctx.js +4 -2
  97. package/dist/http/claimed-run.d.ts +1 -1
  98. package/dist/http/dispatch.d.ts +4 -0
  99. package/dist/http/dispatch.js +3 -0
  100. package/dist/http/durable-run-leg.js +12 -6
  101. package/dist/http/execution-context.d.ts +218 -0
  102. package/dist/http/execution-context.js +330 -0
  103. package/dist/http/memory-compliance-failure-reply.js +3 -0
  104. package/dist/http/resume-legs.d.ts +5 -11
  105. package/dist/http/resume-legs.js +179 -104
  106. package/dist/http/route-ctx.d.ts +44 -59
  107. package/dist/http/route-ctx.js +2 -5
  108. package/dist/http/routes/a2a-serve.js +10 -4
  109. package/dist/http/routes/agents-roster.d.ts +1 -1
  110. package/dist/http/routes/agents-roster.js +5 -1
  111. package/dist/http/routes/approvals-assistant.js +6 -6
  112. package/dist/http/routes/background.d.ts +29 -1
  113. package/dist/http/routes/background.js +29 -2
  114. package/dist/http/routes/capabilities.js +12 -10
  115. package/dist/http/routes/devices.d.ts +13 -19
  116. package/dist/http/routes/devices.js +21 -26
  117. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  118. package/dist/http/routes/mcp-reconnect.js +7 -5
  119. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  120. package/dist/http/routes/memory-origin.d.ts +1 -1
  121. package/dist/http/routes/rules.js +3 -3
  122. package/dist/http/routes/runs.js +11 -7
  123. package/dist/http/routes/session-sync.js +110 -15
  124. package/dist/http/routes/sessions.d.ts +6 -0
  125. package/dist/http/routes/sessions.js +119 -13
  126. package/dist/http/routes/side-query.d.ts +12 -4
  127. package/dist/http/routes/side-query.js +6 -8
  128. package/dist/http/routes/tasks.js +800 -782
  129. package/dist/http/server.d.ts +61 -39
  130. package/dist/http/server.js +11 -0
  131. package/dist/http/session-inherit-env.d.ts +3 -4
  132. package/dist/http/session-inherit-env.js +1 -1
  133. package/dist/http/wire-types.d.ts +22 -18
  134. package/dist/identity-source.d.ts +1 -1
  135. package/dist/index.d.ts +3 -0
  136. package/dist/index.js +1 -0
  137. package/dist/leader/wire.d.ts +7 -0
  138. package/dist/leader/wire.js +5 -4
  139. package/dist/mcp-assertion-header.d.ts +51 -40
  140. package/dist/mcp-assertion-header.js +10 -11
  141. package/dist/mcp-authorization-face.d.ts +27 -0
  142. package/dist/mcp-authorization-face.js +15 -0
  143. package/dist/memory-operator-faces.d.ts +13 -4
  144. package/dist/memory-operator-faces.js +1 -17
  145. package/dist/memory-sync-client.d.ts +53 -14
  146. package/dist/memory-sync-client.js +187 -92
  147. package/dist/memory-sync.d.ts +1 -1
  148. package/dist/memory-sync.js +3 -2
  149. package/dist/model-route-gap.d.ts +6 -22
  150. package/dist/model-route-gap.js +7 -5
  151. package/dist/mods-declaration.d.ts +110 -0
  152. package/dist/mods-declaration.js +109 -0
  153. package/dist/observability/cost-taxonomy.d.ts +2 -2
  154. package/dist/observability/err-text.d.ts +5 -0
  155. package/dist/observability/err-text.js +9 -0
  156. package/dist/observability/fail-open.d.ts +17 -1
  157. package/dist/observability/fail-open.js +17 -1
  158. package/dist/observability/run-terminal.js +0 -1
  159. package/dist/observability/secret-env-scrub.d.ts +1 -1
  160. package/dist/observability/secret-env-scrub.js +1 -1
  161. package/dist/orchestration/workflow-notify-journal.js +4 -4
  162. package/dist/org-memory-admission.d.ts +4 -3
  163. package/dist/org-memory-admission.js +6 -6
  164. package/dist/parked-decide.d.ts +49 -11
  165. package/dist/parked-decide.js +25 -26
  166. package/dist/parked-revive-surface.d.ts +12 -15
  167. package/dist/parked-revive-surface.js +3 -6
  168. package/dist/peer-seat.d.ts +156 -0
  169. package/dist/peer-seat.js +273 -0
  170. package/dist/permission-rule-vocab.d.ts +7 -7
  171. package/dist/permission-rule-vocab.js +3 -3
  172. package/dist/plain-string-record.d.ts +38 -0
  173. package/dist/plain-string-record.js +38 -0
  174. package/dist/plugins/approval-ask-store-file.d.ts +7 -0
  175. package/dist/plugins/approval-ask-store-file.js +11 -0
  176. package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
  177. package/dist/plugins/approval-ask-store-memory.js +12 -0
  178. package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
  179. package/dist/plugins/approval-ask-store-sql.js +30 -12
  180. package/dist/plugins/background-agent-store-sql.js +6 -3
  181. package/dist/plugins/caching-session-store.d.ts +11 -2
  182. package/dist/plugins/caching-session-store.js +15 -3
  183. package/dist/plugins/checkpoint-store-sql.d.ts +21 -25
  184. package/dist/plugins/checkpoint-store-sql.js +13 -15
  185. package/dist/plugins/device-store-sql.d.ts +25 -51
  186. package/dist/plugins/device-store-sql.js +49 -103
  187. package/dist/plugins/execution-placement-sql.d.ts +151 -0
  188. package/dist/plugins/execution-placement-sql.js +223 -0
  189. package/dist/plugins/file-history-store-sql.d.ts +7 -6
  190. package/dist/plugins/file-history-store-sql.js +8 -7
  191. package/dist/plugins/file-run-store.d.ts +6 -1
  192. package/dist/plugins/file-run-store.js +4 -0
  193. package/dist/plugins/fork-routing-session-store.d.ts +6 -0
  194. package/dist/plugins/fork-routing-session-store.js +16 -0
  195. package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
  196. package/dist/plugins/k8s-pod-reaper.js +3 -3
  197. package/dist/plugins/local-checkpoint-store.d.ts +1 -11
  198. package/dist/plugins/local-checkpoint-store.js +0 -1
  199. package/dist/plugins/local-session-store.d.ts +183 -45
  200. package/dist/plugins/local-session-store.js +412 -105
  201. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  202. package/dist/plugins/mailbox-store-sql.js +103 -15
  203. package/dist/plugins/memory-key-guards.d.ts +4 -0
  204. package/dist/plugins/memory-key-guards.js +1 -0
  205. package/dist/plugins/memory-run-store.d.ts +1 -1
  206. package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
  207. package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
  208. package/dist/plugins/one-time-migrations.d.ts +117 -6
  209. package/dist/plugins/one-time-migrations.js +383 -14
  210. package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
  211. package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
  212. package/dist/plugins/permission-rule-store-sql.js +34 -29
  213. package/dist/plugins/pg-pool.d.ts +7 -3
  214. package/dist/plugins/pg-session-storage.d.ts +41 -22
  215. package/dist/plugins/pg-session-storage.js +136 -55
  216. package/dist/plugins/remote-env-adb.d.ts +22 -4
  217. package/dist/plugins/remote-env-adb.js +70 -62
  218. package/dist/plugins/remote-env-device.d.ts +32 -12
  219. package/dist/plugins/remote-env-device.js +18 -17
  220. package/dist/plugins/remote-env-e2b.d.ts +19 -3
  221. package/dist/plugins/remote-env-e2b.js +41 -42
  222. package/dist/plugins/remote-env-file-error.d.ts +2 -0
  223. package/dist/plugins/remote-env-file-error.js +1 -1
  224. package/dist/plugins/remote-env-host.d.ts +23 -8
  225. package/dist/plugins/remote-env-host.js +66 -70
  226. package/dist/plugins/remote-env-k8s.d.ts +13 -16
  227. package/dist/plugins/remote-env-k8s.js +61 -54
  228. package/dist/plugins/remote-env-local-docker.d.ts +30 -5
  229. package/dist/plugins/remote-env-local-docker.js +46 -59
  230. package/dist/plugins/remote-env-ssh.d.ts +18 -4
  231. package/dist/plugins/remote-env-ssh.js +72 -56
  232. package/dist/plugins/remote-shell.d.ts +100 -3
  233. package/dist/plugins/remote-shell.js +166 -2
  234. package/dist/plugins/retention-store-sql.d.ts +23 -8
  235. package/dist/plugins/retention-store-sql.js +13 -1
  236. package/dist/plugins/run-store-sql.d.ts +13 -3
  237. package/dist/plugins/run-store-sql.js +5 -0
  238. package/dist/plugins/schema-consistency.d.ts +15 -5
  239. package/dist/plugins/schema-consistency.js +14 -8
  240. package/dist/plugins/schema-drift-sentence.d.ts +27 -0
  241. package/dist/plugins/schema-drift-sentence.js +38 -0
  242. package/dist/plugins/session-fork.d.ts +63 -12
  243. package/dist/plugins/session-fork.js +38 -21
  244. package/dist/plugins/session-ownership.d.ts +7 -0
  245. package/dist/plugins/session-ownership.js +5 -0
  246. package/dist/plugins/session-row-sql.d.ts +22 -0
  247. package/dist/plugins/session-row-sql.js +81 -0
  248. package/dist/plugins/session-store.d.ts +1 -1
  249. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  250. package/dist/plugins/session-workspace-cell.js +38 -0
  251. package/dist/plugins/store-backend.d.ts +1 -1
  252. package/dist/plugins/store-backend.js +26 -14
  253. package/dist/plugins/store-contracts.d.ts +25 -5
  254. package/dist/plugins/tidb-pool.d.ts +12 -7
  255. package/dist/plugins/tidb-pool.js +15 -3
  256. package/dist/plugins/tidb-session-store.d.ts +74 -28
  257. package/dist/plugins/tidb-session-store.js +132 -53
  258. package/dist/plugins/tool-result-store-sql.d.ts +16 -8
  259. package/dist/plugins/tool-result-store-sql.js +26 -17
  260. package/dist/plugins/worktree-isolation.d.ts +3 -2
  261. package/dist/plugins/worktree-isolation.js +1 -7
  262. package/dist/project-identity.d.ts +1 -14
  263. package/dist/project-identity.js +0 -4
  264. package/dist/project-memory.d.ts +9 -15
  265. package/dist/project-memory.js +5 -4
  266. package/dist/request-key-closure.js +1 -1
  267. package/dist/request-value-closure.d.ts +6 -7
  268. package/dist/request-value-closure.js +1 -3
  269. package/dist/rules-consent.js +6 -4
  270. package/dist/run-local.d.ts +6 -0
  271. package/dist/run-local.js +36 -26
  272. package/dist/runs.d.ts +9 -2
  273. package/dist/runs.js +9 -2
  274. package/dist/sandbox-git-plane.d.ts +2 -2
  275. package/dist/security.d.ts +38 -12
  276. package/dist/security.js +8 -2
  277. package/dist/server-secret-env.d.ts +15 -8
  278. package/dist/server-secret-env.js +4 -2
  279. package/dist/session-children-retire.d.ts +27 -9
  280. package/dist/session-children-retire.js +9 -5
  281. package/dist/session-incarnation.d.ts +19 -10
  282. package/dist/session-incarnation.js +5 -3
  283. package/dist/session-row.d.ts +114 -5
  284. package/dist/session-row.js +55 -5
  285. package/dist/session-titler.d.ts +2 -2
  286. package/dist/session-workspace.d.ts +130 -0
  287. package/dist/session-workspace.js +83 -0
  288. package/dist/spec-fields.d.ts +1 -1
  289. package/dist/standing-answer-grant.d.ts +2 -13
  290. package/dist/standing-answer-grant.js +0 -7
  291. package/dist/task-a2a.d.ts +44 -14
  292. package/dist/task-a2a.js +46 -20
  293. package/dist/task-cwd.d.ts +6 -1
  294. package/dist/task-mcp.d.ts +33 -20
  295. package/dist/task-mcp.js +46 -22
  296. package/dist/tool-approval.d.ts +38 -8
  297. package/dist/tool-approval.js +57 -13
  298. package/dist/trace/core-keyset-guard.d.ts +33 -18
  299. package/dist/trace/engine-notice-wire.d.ts +1 -1
  300. package/dist/trace/engine-notice-wire.js +2 -0
  301. package/dist/trace/exit-seat.d.ts +20 -1
  302. package/dist/trace/exit-seat.js +3 -0
  303. package/dist/trace/extension-call-face.d.ts +33 -0
  304. package/dist/trace/extension-call-face.js +39 -0
  305. package/dist/trace/project.d.ts +26 -7
  306. package/dist/trace/project.js +30 -3
  307. package/dist/trace/redact.js +42 -13
  308. package/dist/trace/self-minted-wire.d.ts +240 -12
  309. package/dist/trace/self-minted-wire.js +100 -4
  310. package/dist/trace/wire-projection-faces.d.ts +6 -6
  311. package/dist/trace/wire-projection-faces.js +6 -5
  312. package/dist/url-refusal.d.ts +1 -1
  313. package/dist/user-authored-text.d.ts +2 -2
  314. package/dist/user-authored-text.js +16 -1
  315. package/dist/web-admin-client/apply-effective.d.ts +17 -3
  316. package/dist/web-admin-client/apply-effective.js +20 -10
  317. package/dist/web-admin-client/center-request.d.ts +3 -2
  318. package/dist/web-admin-client/center-request.js +2 -3
  319. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  320. package/dist/web-admin-client/skills-mcp.js +32 -18
  321. package/dist/web-admin-client/types.d.ts +11 -0
  322. package/package.json +3 -3
  323. package/wire-changes/server@7.138.0.json +446 -0
  324. package/wire-changes/server@7.139.0.json +348 -0
  325. package/dist/http/device-placement.d.ts +0 -175
  326. package/dist/http/device-placement.js +0 -145
  327. package/dist/http/mcp-remote-text.d.ts +0 -5
  328. package/dist/http/mcp-remote-text.js +0 -7
@@ -31,6 +31,9 @@ import type { GateSighting } from "../approval-gate-currency.js";
31
31
  import type { VerifyRoundsSpec } from "./verify-rounds.js";
32
32
  import type { RunAbort } from "../run-cancel-context.js";
33
33
  import type { RunIdentity } from "../identity-source.js";
34
+ import type { PeerLease } from "../peer-seat.js";
35
+ import type { RootedTaskSpec } from "./execution-context.js";
36
+ import type { GateCommitment, GateSettlementEvidence } from "../gate-settlement.js";
34
37
  /** 跨域可变状态 = lens1 §C2 实测的那 8 条「运行期登记簿」,一个对象。
35
38
  * [ref]:`cancelledViaVerb`(按 taskId 键控的「这次是取消动词」旁表)删除 —— 中止成因改挂在每条腿自己的
36
39
  * {@link RunAbort} 句柄上(`inflightRuns` 的值),同寿同删,不再有一张与腿不同寿的旁表。 */
@@ -115,11 +118,13 @@ export interface RouteRequestState {
115
118
  /** B4①(命名化):`prepareSpec` 的返回形(tasks.ts/runs.ts 两域共同消费的属性面)——原为 6 成员内联匿名对象。
116
119
  * 命名后好处不只是过 B4 门:两个消费域现在都能对同一个符号做 `import type` 标注,而不是各自重推断结构。 */
117
120
  export interface PreparedTaskSubmission {
118
- spec: TaskSpec;
121
+ /** 盖过根的 spec(`http/execution-context.ts` `stampSpecRoot` 是唯一写者):`sessionId` 恒在 = 这次提交的根会话。 */
122
+ spec: RootedTaskSpec;
119
123
  /** [ref]:本次提交的执行 Runner —— 场景 hands=none 时是无手孪生(不挂 executionEnvFactory)。
120
124
  * 两个消费域一律用它,不再直接摸 `deps.runner`(那正是「场景表态了、执行点没听」的缝)。 */
121
125
  runner: Runner;
122
- auth?: RequestAuth;
126
+ /** authorizer 交出的身份与根(必填席,恒在;续跑家族的内部车道交它从 checkpoint 重建的那一只)。 */
127
+ auth: RequestAuth;
123
128
  verify?: VerifyRoundsSpec;
124
129
  cascade?: boolean;
125
130
  jobId?: string;
@@ -135,19 +140,23 @@ export interface PreparedTaskSubmission {
135
140
  */
136
141
  warnings?: readonly EntryProblem[];
137
142
  /**
138
- * [ref](device lane 首绑写协议,design §4.3.2 两阶段 placement)—— **已校验、未写库**的 placement 意图。
139
- *
140
- * 缺席的两种含义都等价于「本腿没有 placement 要提交」:①本部署不是 device 车道(绝大多数);
141
- * ②本腿不是 fresh 根提交(resume 家族根本不经 `prepareSpec`)。在场 ⇒ 每个 `runStore.createRun(`
142
- * 成功点**必须**把它交给 `commitPlacement`(唯一写入属主;类级机器门钉住每个调用点都接了)。
143
+ * 这次执行的**执行上下文**(唯一解析器 `http/execution-context.ts` `resolveExecutionContext` 的产物:根 · 住客 · 放置态 · 车道 · device 车道的设备意图)。
144
+ * 提交门在装配之前铸(fresh);`/decide` 的宿主新 turn 交续跑折叠器铸的那一只(continuation)。每个 `runStore.createRun(` 成功点**必须**把它交给
145
+ * 认领之后的唯一写口 `commitExecutionContext`(类级机器门钉住每个调用点都接了)。
143
146
  */
144
- bindingIntent?: import("./device-placement.js").DevicePlacementIntent;
147
+ execution: import("./execution-context.js").RootedExecutionContext;
145
148
  /**
146
149
  * 这次提交的**受理座**(`run-acceptance.ts`):标题器只在引擎受理之后起。提交门(`prepareSpec`)随 spec 一起铸;
147
150
  * 三条提交腿只喂它(持流的形逐帧 `observe`,终局落行之后 `settle` 一次),不再各自判「什么时候起标题」。
148
151
  * 不经提交门的内部车道(`/decide` 的宿主新 turn)铸一只不带标题器的座:不起标题。
149
152
  */
150
153
  acceptance: import("../run-acceptance.js").RunAcceptance;
154
+ /**
155
+ * 这次装配的**宿主租约**:受理漏斗在装配之前开(`peerDesk.lease("fresh")`),装配在它之下登记交给引擎的对端声明;漏斗成功即把它交到这里 ——
156
+ * 拿到本产物的那只函数紧接着写 `try { … } finally { prepared.peerLease.release() }`,其后每个出口(早退、合流败方、建行拒、驱动返回)按构造都放。
157
+ * 活过那段作用域的驱动(后台腿)经 `heldFor(prepared.peerLease, () => …)` 自持一只,自己落定才放。续跑家族的内部车道交它自己漏斗开的那一只。
158
+ */
159
+ peerLease: PeerLease;
151
160
  }
152
161
  /**
153
162
  * 一条会话的**续跑重建输入**(`checkpoint_ctx` 的行体)—— 提交腿写一次,续跑腿读它重建 spec。
@@ -218,6 +227,11 @@ export interface WorkflowDecisionDelivery {
218
227
  /** B4①(命名化):`driveResumeIntoRunLog` 的入参形(resume 家族共用)——原为 9 成员内联匿名对象。 */
219
228
  export interface DriveResumeArgs {
220
229
  token: CheckpointToken;
230
+ /**
231
+ * 这条续跑的执行上下文(续跑折叠器经唯一解析器铸的 continuation 形:根 = checkpoint 的会话)。本腿认领之后(wake 的建行 / `markResuming` CAS 赢)
232
+ * 交给唯一写口 `commitExecutionContext`(按解析器那一读的住客再断言 writer(L) / absent 落格;住客换了 ⇒ 释放 + 404)。
233
+ */
234
+ execution: import("./execution-context.js").RootedExecutionContext;
221
235
  /**
222
236
  * 🔴 **[ref] 合并复审 F2 [high] —— 「本次续跑真正用的那个体」,由本腿在赢下 CAS 之后写回耐久层**
223
237
  * (验真后修;必填 —— `driveResumeIntoRunLog` 今天恰有**四个**调用点:legacy `/decide`(`resumeCheckpoint`)、
@@ -252,6 +266,11 @@ export interface DriveResumeArgs {
252
266
  sessionId: string;
253
267
  ctx: CheckpointCtx;
254
268
  };
269
+ /**
270
+ * 这条续跑的**宿主租约**(续跑漏斗 `resolveResumeSpecOrBlocked` 开、装配在它之下登记对端声明;调用方作用域的 `finally` 放)。
271
+ * 驱动腿经 `heldFor(args.peerLease, () => driveResumeLeg(…))` 自持一只:受理早回的那一形里,腿活过调用方作用域,自己落定才放。
272
+ */
273
+ peerLease: PeerLease;
255
274
  /** [ref]:续跑腿的执行 Runner。resume 会用同一条 resolveSpec 重解析场景,故与首跑选同一只 —— 否则
256
275
  * 一次续跑就把手带 band 重新 mount 回来(收窄只在首跑成立 = 没成立)。 */
257
276
  runner: Runner;
@@ -261,6 +280,12 @@ export interface DriveResumeArgs {
261
280
  taskConfig: Omit<TaskSpec, "objective" | "sessionId">;
262
281
  resumeObjective: string;
263
282
  outcome: ResumeOutcome;
283
+ /**
284
+ * 🔴 **这条腿到达边界时能证什么**(必填 —— 缺 = 编译错;闭集,语义属主 = `gate-settlement.ts` 的 {@link GateCommitment})。同一条驱动路径既续本请求点名的门
285
+ * (`/decide` 引擎臂、`/resume`、`/plan_review` ⇒ `gate_resolved`,带执行**前**从选中那一行捕获、过了信任边界的身份),也替宿主投递(wake ⇒ `delivered`)。
286
+ * 它只是分类:位由受理点与终局体两个边界按它铸,从不只凭这一句声明铸;缺席的取消出口(`checkpoint.resume_aborted` / 抛出臂)不铸。
287
+ */
288
+ commitment: GateCommitment;
264
289
  verifyRounds: VerifyRoundsSpec | undefined;
265
290
  /**
266
291
  * **R-1**(车CJ 收车档 §10 R-1;[ref] 批落地)—— 本条 resume 腿**装配期**铸下的用户面通告
@@ -433,10 +458,7 @@ export interface RouteLegs {
433
458
  decider?: {
434
459
  principal?: string;
435
460
  explicitOperator: boolean;
436
- }): Promise<{
437
- status: number;
438
- body: object;
439
- }>;
461
+ }): Promise<ResumeLegOutcome>;
440
462
  /** notify-wake 域:唤醒一条 parked checkpoint。 */
441
463
  resumeWake(sessionId: string, message: string | undefined, caller: string | undefined, req: IncomingMessage | undefined,
442
464
  /** [ref] 件 S-1:透传给 {@link DriveResumeArgs.acceptEarly}(200 受理语义)。HTTP `/wake` 腿传 `true`
@@ -444,7 +466,7 @@ export interface RouteLegs {
444
466
  acceptEarly?: boolean,
445
467
  /** 透传给 {@link DriveResumeArgs.workflowDecision}。HTTP `/wake` 腿**不设**此位 —— 它是 `/decide` 第三条车道投递腿的专用席,
446
468
  * 而两条腿共用这一条驱动路径正是 `notify-wake.ts` 那条「wake 会烧模型,所以复用 resume 家族的同一条腿」的成文纪律。 */
447
- workflowDecision?: WorkflowDecisionDelivery): Promise<HostLegOutcome>;
469
+ workflowDecision?: WorkflowDecisionDelivery): Promise<ResumeLegOutcome>;
448
470
  /**
449
471
  * assistant plan_review 三态腿的**纯读段**([ref],7.97.0):选卡 → 属主轻读 → 载入 → 行级 scope → 门类 → 这条决断
450
472
  * 唯一的一次 ctx 读。任何一步拒 ⇒ `{ok:false, answer}`(不改任何状态;端点在消费一次性 nonce **之前**把它答出去);
@@ -481,10 +503,7 @@ export interface RouteLegs {
481
503
  acceptEarly?: boolean,
482
504
  /** [ref]:批准之后这条会话继续用的 permission mode(闭集 `PLAN_APPROVE_PERMISSION_MODES_AFTER`;只在把计划带出 plan 的决定
483
505
  * —— `approve` / `edit` —— 上有意义,缺席 ⇒ `"default"`)。语义与判据的唯一属主 = `resume-legs.ts` 的同名形参顶注。 */
484
- permissionModeAfter?: PlanApprovePermissionModeAfter): Promise<{
485
- status: number;
486
- body: object;
487
- }>;
506
+ permissionModeAfter?: PlanApprovePermissionModeAfter): Promise<ResumeLegOutcome>;
488
507
  }
489
508
  /**
490
509
  * [ref] 件 S-2 —— resume 族**受理点的断连留痕**(裁定:维持 detach,补观察面)。
@@ -673,48 +692,13 @@ export type RouteDecl = RouteDispatchDecl | RouteGateDecl;
673
692
  * api-error-text-freeze 基线流程。
674
693
  */
675
694
  /**
676
- * resume 家族**引擎腿**的结局:HTTP 应答(`status` / `body`)+ 一位**进程内**事实(不上 wire)。workflow 宿主腿的结局另型({@link HostLegOutcome}),并集 = {@link ResumeLegOutcome}。
677
- *
678
- * `decisionCommitted` —— **这条腿的终局是在引擎受理了这次决定之后才结出来的**([ref] X5,7.112.0):本腿走到了「引擎交回结果」那一步
679
- * (`resumeStream` / `resumeWithVerification` 解析 = core 的全部 pre-CAS 门 + checkpoint resolve CAS 都过了 —— 受理点的判据属主
680
- * = {@link DriveResumeArgs.acceptEarly} 顶注),之后才收场。只由 `driveResumeLeg` 走完引擎那一步的终局出口铸;`CheckpointError` 臂、
681
- * 「中止抛穿」臂、一切 4xx / 拒绝出口一律**不铸**(缺席 ⇒ 证不出提交,读者按「没提交」读 —— fail-closed)。
682
- *
683
- * 读者:`/decide` 的两道「这次决定提交了没有」门 —— [ref] 迟到结算留痕(`noteLateSettleIfAccepted`)与 remember 豁免落店
684
- * (`routes/approvals-assistant.ts`)。它们原先只读 wire 体的 `errorCode !== "cancelled"`(「取消 = 决定没提交」),[ref] 给续跑腿补了
685
- * 「引擎受理之后才被取消」的取消终局之后,`cancelled` 不再等价「没提交」⇒ 两门改读本位:`200 ∧ (errorCode≠cancelled ∨ decisionCommitted)`。
686
- * 🔴 为什么不放进 `result.cancelContext`(主会话初稿的落点):那只快照的契约是「分诊材料,不是状态机输入:任何门 / CAS / resume 判定都不许
687
- * 读它」(`plugins/store-contracts.ts` `RunCancelContext` 顶注)—— 这两道门正是判定;而且门读的是 `/decide` 的平面体、看不到结果对象。
688
- * 受理与否是腿自己的控制流事实,在腿上说,零 wire 变更。
689
- */
690
- export interface EngineLegOutcome {
691
- status: number;
692
- body: object;
693
- decisionCommitted?: true;
694
- }
695
- /**
696
- * **workflow 宿主腿的结局(wake / fresh)—— 型上就没有 `decisionCommitted`**([ref] ②,7.113.0;`never` 让「这条路径不铸」成为**编译事实**,不是靠人记)。
697
- *
698
- * 为什么这条路径不该有该位:`/decide` 的 workflow 第三条车道(`resumeCheckpoint` 的 parked 分流,`planWorkflowParkedDecide` 之后)与 `POST /v1/sessions/:id/wake`
699
- * 把决定 / 消息交给**宿主会话的下一跑**(`resumeWake` / `startFreshHostTurn`)。它们的 200 只到「已交给宿主的下一跑」—— 子代 checkpoint 的 resolve CAS 发生在
700
- * workflow 引擎自己驱动那一步,server 观察不到(`resume-legs.ts` 该分流处「刻意不落 `late_settle`」同源)。「这次决定被引擎受理了」这句话在这里**没有可说的对象**,
701
- * 于是 `decisionCommitted` 在这条路径上语义含混(修前:`resumeWake` 的收尾是 `driveResumeIntoRunLog` 的 `ResumeLegOutcome`,非受理点早返形(无 `acceptEarly`)下它会把
702
- * 引擎腿的 `decisionCommitted:true` 原样带出去 —— 型被声明返回型 `{status, body}` 擦掉了,运行期仍在)。读者核实:两道提交门(`noteLateSettleIfAccepted` 迟到结算留痕、
703
- * `/decide` 的 remember 豁免落店)在这条路径上今天**零读取**(前者该分流直接 `return`、不调;后者的 `pendingGrant` 只由引擎腿的 `onResumeCommitted` 回调置位,宿主腿不调它,且 `planWorkflowParkedDecide` 对 workflow 车道的 `remember` 本就 fail-closed 拒 —— `decide.workflow_remember_unsupported`,`parked-decide.ts`)——
704
- * 无读者 ⇒ 剥掉该位,而不是留一个「铸了也没人读、读了不知道什么意思」的位。出口经 {@link buildHostLegOutcome} 只带 `status` / `body`。
695
+ * resume 家族一条腿的结局:HTTP 应答(`status` / `body`)。「这次决定提交了没有」只有一种说法 —— 体上的结清证据 `decisionCommitted`
696
+ * (唯一铸点 / 唯一读法 = `gate-settlement.ts`);进程内不另带第二个提交位。
705
697
  */
706
- export interface HostLegOutcome {
698
+ export interface ResumeLegOutcome {
707
699
  status: number;
708
700
  body: object;
709
- decisionCommitted?: never;
710
701
  }
711
- /** resume 家族一条腿的结局:引擎腿(可带 `decisionCommitted`)∪ workflow 宿主腿(型上没有)。`out.decisionCommitted === true` 同时收窄到引擎腿。 */
712
- export type ResumeLegOutcome = EngineLegOutcome | HostLegOutcome;
713
- /** 把任意腿的结局折成宿主腿结局:**只带 `status` / `body`**(丢掉引擎腿的 `decisionCommitted` —— 见 {@link HostLegOutcome})。纯数据。 */
714
- export declare function buildHostLegOutcome(out: {
715
- status: number;
716
- body: object;
717
- }): HostLegOutcome;
718
702
  /**
719
703
  * **「决定/续跑已受理」那一枚 200 收据的单一铸点**([ref])。
720
704
  *
@@ -724,11 +708,12 @@ export declare function buildHostLegOutcome(out: {
724
708
  * 都不该加**;写成两份字面量,漂开的那天没有任何东西会说话。
725
709
  *
726
710
  * `bindingEnforced` 的语义不因铸点而变(「这次决定按绑定坐标受理,不是盲目放行」),故两腿同带。
711
+ *
712
+ * `evidence` = 结清证据(`gate-settlement.ts`):引擎续跑腿的受理点交 `buildSettlementEvidence({ at: "engine_accepted", … })` 铸的那一份
713
+ * (`gate_resolved` 腿 ⇒ 位 + 身份;wake 的 `delivered` ⇒ 空);没跨过任何边界的回执(宿主新一跑、同一决定重投)交 `NO_SETTLEMENT_EVIDENCE`。
714
+ * 调用方不手写证据键 —— 体上的两键只有那一只铸点。
727
715
  */
728
- export declare function acceptedResumingReceipt(taskId: string, sessionId: string): {
729
- status: number;
730
- body: object;
731
- };
716
+ export declare function acceptedResumingReceipt(taskId: string, sessionId: string, evidence: GateSettlementEvidence): ResumeLegOutcome;
732
717
  /** 一条腿的结局是不是一张「已受理」回执,是 ⇒ 回执上那一跑的 `taskId`(同一张回执的读法与 {@link acceptedResumingReceipt} 住在一起;
733
718
  * `/decide` 的 workflow 车道据它把投递那一跑记在决定行上)。 */
734
719
  export declare function acceptedTaskIdOf(out: {
@@ -21,11 +21,8 @@ export function assertNever(x) {
21
21
  throw new Error(`unreachable route id: ${String(x)}`);
22
22
  }
23
23
  export const ROUTE_METHOD_ORDER = ["GET", "POST", "PUT", "PATCH", "DELETE"];
24
- export function buildHostLegOutcome(out) {
25
- return { status: out.status, body: out.body };
26
- }
27
- export function acceptedResumingReceipt(taskId, sessionId) {
28
- return { status: 200, body: { taskId, sessionId, status: "resuming", bindingEnforced: true } };
24
+ export function acceptedResumingReceipt(taskId, sessionId, evidence) {
25
+ return { status: 200, body: { taskId, sessionId, status: "resuming", bindingEnforced: true, ...evidence } };
29
26
  }
30
27
  export function acceptedTaskIdOf(out) {
31
28
  const body = out.body;
@@ -272,11 +272,17 @@ async function handleMessageSend({ req, res, ctx, params, serve, runStore }) {
272
272
  const prepared = await ctx.legs.prepareSpec(req, res, body, (e) => {
273
273
  typed = e;
274
274
  });
275
- if (typed)
276
- throw rpcErrorFromTypedFailure(typed, skillId);
277
- if (!prepared)
275
+ if (!prepared) {
276
+ if (typed)
277
+ throw rpcErrorFromTypedFailure(typed, skillId);
278
278
  return undefined;
279
- receipt = await createDurableRun(req, res, ctx, prepared, idem);
279
+ }
280
+ try {
281
+ receipt = await createDurableRun(req, res, ctx, prepared, idem);
282
+ }
283
+ finally {
284
+ prepared.peerLease.release();
285
+ }
280
286
  }
281
287
  if (!receipt)
282
288
  return undefined;
@@ -25,7 +25,7 @@
25
25
  * · **能力位同源**:`capabilities.agentRoster` 与这里的 501 谓词是同一个 `deps.backgroundAgentStore`。
26
26
  */
27
27
  import type { IncomingMessage, ServerResponse } from "node:http";
28
- import type { BackgroundAgentRowSummary } from "@sema-agent/core";
28
+ import { type BackgroundAgentRowSummary } from "@sema-agent/core";
29
29
  import type { RouteCtx, RouteMatch, RouteIdsOf } from "../route-ctx.js";
30
30
  /** 名册窗的扫描上限(与 fleet 的终态窗同姿势:读面要有界)。窗满时回体带 `truncated:true`,
31
31
  * `total` 于是诚实地是「窗内行数」而不是租户全量 —— 一个悄悄封顶的 total 比没有 total 更坏。 */
@@ -1,7 +1,9 @@
1
+ import { chosenAgentName } from "@sema-agent/core";
1
2
  import { sendJson, sendError } from "../send.js";
2
3
  import { fleetViewOf, sendUnauthorized } from "../principal-gate.js";
3
4
  import { assertNever } from "../route-ctx.js";
4
5
  import * as facts from "../../capability-facts.js";
6
+ import { backgroundAgentNameOf } from "../../background-agent-name.js";
5
7
  export const ROSTER_SCAN_LIMIT = 500;
6
8
  const ROSTER_STATUSES = ["running", "parked", "completed", "failed", "killed"];
7
9
  const ROSTER_KEY_DISPOSITION = {
@@ -24,9 +26,11 @@ export function rosterAgentId(row) {
24
26
  return row.handle;
25
27
  }
26
28
  function projectRow(r) {
29
+ const name = backgroundAgentNameOf(r);
27
30
  return {
28
31
  handle: rosterAgentId(r),
29
- ...(r.name !== undefined ? { name: r.name } : {}),
32
+ name,
33
+ ...(chosenAgentName(r.handle, name) === undefined ? { anonymous: true } : {}),
30
34
  ...(r.agentType !== undefined ? { agentType: r.agentType } : {}),
31
35
  status: r.status,
32
36
  spawnedAt: r.spawnedAt,
@@ -22,7 +22,7 @@ import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal, sessionCaller
22
22
  import { readOwnedRun, readOwnedSession } from "../owner-access.js";
23
23
  import { governanceOriginOf } from "../../runtime-governance.js";
24
24
  import { deploymentListAsks } from "../../approval.js";
25
- import { callGrantBindingOf, questionGrantBindingOf } from "../../standing-answer-grant.js";
25
+ import { grantBindingOf } from "../../standing-answer-grant.js";
26
26
  import { sendResumeOutcome } from "../route-ctx.js";
27
27
  import { assertNever } from "../route-ctx.js";
28
28
  import * as facts from "../../capability-facts.js";
@@ -31,6 +31,7 @@ import { recordFailOpen } from "../../observability/fail-open.js";
31
31
  import { MAX_CHECKPOINT_ID_CHARS, durableRowIdentity } from "../../checkpoint-identity.js";
32
32
  import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "../../request-key-closure.js";
33
33
  import { fieldConflictFacts } from "../../field-conflict.js";
34
+ import { buildSettlementEvidence, decisionCommittedOf } from "../../gate-settlement.js";
34
35
  const DECIDE_BODY_DECLARED_KEYS = [
35
36
  "answer", "boundCallId", "boundInputHash", "checkpointToken", "decidedBy", "decision", "feedback", "reason", "remember", "updatedInput",
36
37
  ];
@@ -259,6 +260,7 @@ async function decideIdempotentReplay(cs, args) {
259
260
  decision,
260
261
  bindingEnforced: true,
261
262
  ...(executionOutcome !== undefined ? { executionOutcome } : {}),
263
+ ...buildSettlementEvidence({ at: "replay_matched" }),
262
264
  },
263
265
  };
264
266
  }
@@ -352,8 +354,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
352
354
  sendError(res, 404, "request.route_unsupported", "not found");
353
355
  return;
354
356
  }
355
- const rows = await deps.approvalExemptionStore.list(exSessionId);
356
- sendJson(res, 200, { exemptions: rows.filter((r) => callGrantBindingOf({ toolName: r.toolName }).kind === "none") });
357
+ sendJson(res, 200, { exemptions: await deps.approvalExemptionStore.list(exSessionId) });
357
358
  return;
358
359
  }
359
360
  if (exToolName === undefined) {
@@ -870,8 +871,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
870
871
  }
871
872
  : undefined;
872
873
  const out = await resumeCheckpoint(sessionId, decision, body.reason ?? undefined, hostDecisionFacts(decidedBy, deciderPrincipal), req, answer, binding, grantOnCommit, true, { principal: deciderPrincipal, explicitOperator });
873
- const outErrorCode = out.body.errorCode;
874
- if (pendingGrant !== undefined && out.status === 200 && (outErrorCode !== "cancelled" || out.decisionCommitted === true) && deps.approvalExemptionStore) {
874
+ if (pendingGrant !== undefined && decisionCommittedOf(out) && deps.approvalExemptionStore) {
875
875
  try {
876
876
  await deps.approvalExemptionStore.grant(pendingGrant.sessionId, pendingGrant.toolName, deciderPrincipal ?? null);
877
877
  rememberApplied = true;
@@ -954,7 +954,7 @@ function projectPendingForWire(row, deployment) {
954
954
  governanceForced: governanceForced === true,
955
955
  requiresRealApproval: row.requiresRealApproval === true,
956
956
  mandated: row.mandated === true || row.ruleOffersAbsence === "mandated",
957
- grantBinding: questionGrantBindingOf(row, deploymentListQuestion),
957
+ grantBinding: grantBindingOf(row),
958
958
  classes: row.classes,
959
959
  deploymentListQuestion,
960
960
  });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * [ref](core 7.32.0 [ref] 的 server 半场;wire 形 [ref] / [ref] / [ref] 定)—— **会话后台任务的两只读口**:
3
3
  *
4
- * `GET /v1/sessions/:id/background` → 200 `{ rows: [{ id, description?, type, status, createdAt, sessionScoped, retained? }] }`
4
+ * `GET /v1/sessions/:id/background` → 200 `{ rows: [{ id, description?, type, status, createdAt, sessionScoped, retained?, sessionId? }] }`
5
5
  * `POST /v1/sessions/:id/background/stop` body `{ includeRetained: boolean }` → 200 `{ receipts: [{ id, outcome }] }`
6
6
  *
7
7
  * 消费方:cli [ref] ①(删前核登记)/ [ref](退出三选一:「Exit and stop tasks」= `includeRetained:true`,`sema kill` =
@@ -28,6 +28,16 @@
28
28
  * oracle);显式 operator 可读可停任何会话(监督动作)。零模型工作 ⇒ 不进 billable、不吃 drain 503;停止口是 mutating ⇒
29
29
  * `rateLimited`。能力位 `background: { listFace, exitFaces }` 与两口的 501 从同一个谓词推导(会话属主面在场)。
30
30
  *
31
+ * ── 结清口:子代结清只有一只协调器 ─────────────────────────────────────────────────────────────────
32
+ * `POST /v1/sessions/:id/children/retire`(无体)→ 200 `{ children: string[] }`:**显式**结清以本会话为根的后台子代(running 停掉、parked
33
+ * 作废卡并对账翻终态),与 `DELETE /v1/sessions/:id` 同调**一只**预检 `probeSessionChildren` 与**一只**协调器 `retireSessionChildren`
34
+ * (`session-children-retire.ts`)、同一个 scope(`sessionChildrenScope`)。预检有本副本结束不了的 ⇒ 409 `conflict.session_children_alive{remaining}`
35
+ * (与删除门同码同投影,一只都不动);停不净(时序失败)⇒ 与删除门同一失败契约(通用 500,重试收敛)。`children` = 首轮观察到、且已全部
36
+ * 结清的子代,每项经名册的 id 投影 `rosterAgentId`(与换绑 / 导入两门 409 体的 `children` 同形同投影)。它不判根认领(停止口也不判):
37
+ * 根 run 活着时它可再派子代 —— 由换绑 / 导入门写事务内的认领读兜。鉴权 / 限流 / 无凭据写门与停止口同。
38
+ * **停止口的已发语义逐字不动**:它只中止 running 的后台 agent / workflow 并收 shell,停驻子代与待决卡留着 —— 「停别的、留卡待决」与
39
+ * 「把树结清」是两个意图、两只口。
40
+ *
31
41
  * 🔴 **停止口在无凭据写门内**(`credentialGated: ["POST"]`;车OL F1 = 7.102.0 合并复审 gpt-6 腿 [high]):门的两个合取项它都中
32
42
  * —— ① 授权的唯一输入是 principal 头(属主判 `sessionOwnerOk` 与 operator 臂 `explicitOperatorOk` 读的都是 `gatedPrincipal`,
33
43
  * 无 service 凭证的部署上它收调用方自报的头);② 不可撤销、爆炸半径跨租户(知道会话 id + 自报它的属主 ⇒ 停掉那个会话的后台
@@ -53,12 +63,24 @@ export interface SessionBackgroundRow {
53
63
  createdAt: number;
54
64
  sessionScoped: boolean;
55
65
  retained?: true;
66
+ /** 后台 agent 行的子会话 id(引擎绑定之后才有;缺席 ⇒ 键不在)。 */
67
+ sessionId?: string;
56
68
  }
57
69
  /** 停止口的一条受据(`outcome` 逐字透传 core 闭集)。 */
58
70
  export interface SessionBackgroundReceipt {
59
71
  id: string;
60
72
  outcome: BackgroundShellHardKillReceipt["outcome"];
61
73
  }
74
+ /** 结清口的路径(换绑 / 导入两门的子代半 409 以它点名下一步;与执行面拒体带 `forkPath` 同一形)。 */
75
+ export declare function childrenRetirePath(sessionId: string): string;
76
+ /**
77
+ * 子代半命中的拒体两键(换绑 409 `device.rebind_active_run`、导入 409 `session_active` 同一只):`children` = 预检那一次枚举观察到的活子代
78
+ * (`probeSessionChildren` 的 `liveChildren`)经名册 id 投影,`retirePath` = 结清口。
79
+ */
80
+ export declare function liveChildrenRefusal(sessionId: string, liveChildren: readonly string[]): {
81
+ children: string[];
82
+ retirePath: string;
83
+ };
62
84
  export declare function handleBackground(req: IncomingMessage, res: ServerResponse, match: RouteMatch<BackgroundRouteId>, ctx: RouteCtx): Promise<void>;
63
85
  export declare const BACKGROUND_ROUTES: readonly [{
64
86
  readonly id: "session-background-list";
@@ -71,6 +93,12 @@ export declare const BACKGROUND_ROUTES: readonly [{
71
93
  readonly label: "/v1/sessions/:id/background/stop";
72
94
  readonly credentialGated: readonly ["POST"];
73
95
  readonly methods: readonly ["POST"];
96
+ }, {
97
+ readonly id: "session-children-retire";
98
+ readonly pattern: RegExp;
99
+ readonly label: "/v1/sessions/:id/children/retire";
100
+ readonly credentialGated: readonly ["POST"];
101
+ readonly methods: readonly ["POST"];
74
102
  }];
75
103
  /** 本域可分派行的 `id` 闭集 —— handler 的 `switch` 按它判穷尽(漏一口 = 编译红)。 */
76
104
  export type BackgroundRouteId = RouteIdsOf<typeof BACKGROUND_ROUTES>;
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { defaultTaskRegistry } from "@sema-agent/core";
3
3
  import { reapSessionBackgroundHard } from "../../background-session-reap.js";
4
+ import { probeSessionChildren, retireSessionChildren, sessionChildrenScope, SessionChildrenStopIncompleteError } from "../../session-children-retire.js";
5
+ import { rosterAgentId } from "./agents-roster.js";
4
6
  import { redactSecrets } from "../../trace/redact.js";
5
7
  import { sendJson, sendError } from "../send.js";
6
8
  import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
@@ -20,6 +22,7 @@ const ROW_KEY_DISPOSITION = {
20
22
  description: "keep",
21
23
  toolUseId: "strip",
22
24
  updatedAt: "strip",
25
+ sessionId: "keep",
23
26
  };
24
27
  void ROW_KEY_DISPOSITION;
25
28
  function projectRow(r) {
@@ -31,13 +34,20 @@ function projectRow(r) {
31
34
  createdAt: r.createdAt,
32
35
  sessionScoped: r.sessionScoped,
33
36
  ...(r.retained === true ? { retained: true } : {}),
37
+ ...(r.sessionId !== undefined ? { sessionId: r.sessionId } : {}),
34
38
  };
35
39
  }
40
+ export function childrenRetirePath(sessionId) {
41
+ return `/v1/sessions/${encodeURIComponent(sessionId)}/children/retire`;
42
+ }
43
+ export function liveChildrenRefusal(sessionId, liveChildren) {
44
+ return { children: liveChildren.map((handle) => rosterAgentId({ handle })), retirePath: childrenRetirePath(sessionId) };
45
+ }
36
46
  const StopBodySchema = z.object({ includeRetained: z.boolean() }).strict();
37
47
  export async function handleBackground(req, res, match, ctx) {
38
48
  const { deps } = ctx;
39
49
  const id = match.decl.id;
40
- if (id === "session-background-stop" && ctx.helpers.rateLimited(req, res))
50
+ if ((id === "session-background-stop" || id === "session-children-retire") && ctx.helpers.rateLimited(req, res))
41
51
  return;
42
52
  const principal = gatedPrincipal(req, deps.config);
43
53
  if (deps.config.requirePrincipal && principal === undefined) {
@@ -71,7 +81,7 @@ export async function handleBackground(req, res, match, ctx) {
71
81
  const operator = explicitOperatorOk(principal, deps.config);
72
82
  if (!operator && !ctx.helpers.sessionOwnerOk(req, res, await sessionOwnerCellOf(owned.sessionStorage, sessionId, owner)))
73
83
  return;
74
- const scope = owner ?? "default";
84
+ const scope = sessionChildrenScope(owner);
75
85
  switch (id) {
76
86
  case "session-background-list": {
77
87
  const rows = defaultTaskRegistry.list({ owner: sessionId, sessionId, scope }, { limit: LIST_LIMIT }).map(projectRow);
@@ -83,6 +93,22 @@ export async function handleBackground(req, res, match, ctx) {
83
93
  sendJson(res, 200, { receipts: receipts.map((r) => ({ id: r.taskId, outcome: r.outcome })) });
84
94
  return;
85
95
  }
96
+ case "session-children-retire": {
97
+ const sources = { agentStore: deps.backgroundAgentStore, checkpointStore: deps.checkpointStore, registry: defaultTaskRegistry };
98
+ const reach = await probeSessionChildren(sources, sessionId, scope);
99
+ if (!reach.reachable) {
100
+ deps.logger?.warn("session_children_retire_unreachable", { sessionId, remaining: reach.unreachableHandles.length, handles: reach.unreachableHandles });
101
+ sendError(res, 409, "conflict.session_children_alive", "this session still has background agents running where this replica cannot stop them (on another replica, for example), so nothing was retired — `remaining` lists their ids (the same ids `GET /v1/agents/roster` shows); retry once they finish, or stop them where they run", { remaining: reach.unreachableHandles.map((handle) => rosterAgentId({ handle })) });
102
+ return;
103
+ }
104
+ const out = await retireSessionChildren(sources, sessionId, scope);
105
+ if (out.stopped === "incomplete") {
106
+ deps.logger?.warn("session_children_retire_incomplete", { sessionId, remaining: out.remainingHandles.length, handles: out.remainingHandles });
107
+ throw new SessionChildrenStopIncompleteError(sessionId, out.remainingHandles.length);
108
+ }
109
+ sendJson(res, 200, { children: out.observed.map((handle) => rosterAgentId({ handle })) });
110
+ return;
111
+ }
86
112
  default:
87
113
  return assertNever(id);
88
114
  }
@@ -90,5 +116,6 @@ export async function handleBackground(req, res, match, ctx) {
90
116
  export const BACKGROUND_ROUTES = [
91
117
  { id: "session-background-list", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/background$/, label: "/v1/sessions/:id/background", methods: ["GET"] },
92
118
  { id: "session-background-stop", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/background\/stop$/, label: "/v1/sessions/:id/background/stop", credentialGated: ["POST"], methods: ["POST"] },
119
+ { id: "session-children-retire", pattern: /^\/v1\/sessions\/(?<sessionId>[^/]+)\/children\/retire$/, label: "/v1/sessions/:id/children/retire", credentialGated: ["POST"], methods: ["POST"] },
93
120
  ];
94
121
  //# sourceMappingURL=background.js.map
@@ -1,4 +1,4 @@
1
- import { PERMISSION_MODES, USER_ATTACHMENT_SEAT, expandTiers, reasoningEffortLevelsOf, routePairingStatus } from "@sema-agent/core";
1
+ import { PERMISSION_MODES, USER_ATTACHMENT_SEAT, expandTiers, reasoningEffortLevelsOf, routePairingStatus, thinkingOffSpelled } from "@sema-agent/core";
2
2
  import { modelImageInput, modelReasoningDecl, isAtMentionable, selectableAliasNames, selectableCatalog } from "../../model-select.js";
3
3
  import { buildDroppedEntriesWire } from "../../web-admin-client/read-warnings.js";
4
4
  import { buildCandidateRejectedWire } from "../../web-admin-client/candidate-gate.js";
@@ -12,7 +12,7 @@ import { leaderEntryRefusal, leaderRunEntry } from "../../leader/run-entry.js";
12
12
  import { autonomyKeepsOpenWriteFace, autonomyModeFloor } from "../../runtime-governance.js";
13
13
  import { DEVICE_PROTOCOL_VERSION, DEVICE_WS_PATH } from "../../device-ws-protocol.js";
14
14
  import { a2aInjectionHonored } from "../../task-a2a.js";
15
- import { buildEmptyMcpProbeFace, buildMcpInjectionRefusal, buildMcpProbeCacheKey, buildMcpProbeInflightRefusal, buildMcpProbeRateKey, buildMcpProbeRateRefusal, createMcpProbeLocal, buildMcpProbeDeclarationRefusal, buildMcpProbeIncompleteRefusal, McpProbeIncomplete, McpProbeMisaligned, MCP_PROBE_HTTP_STATUS, MCP_PROBE_MAX_INFLIGHT, MCP_PROBE_VERDICT_TIMEOUT_MS, createMcpProbeWalk, readMcpProbeBody, } from "../../capabilities/mcp-probe.js";
15
+ import { buildEmptyMcpProbeFace, buildMcpInjectionRefusal, buildMcpProbeCacheKey, buildMcpProbeInflightRefusal, buildMcpProbeRateKey, buildMcpProbeRateRefusal, createMcpProbeLocal, buildMcpProbeDeclarationRefusal, buildMcpProbeIncompleteRefusal, McpProbeIncomplete, McpProbeMisaligned, MCP_PROBE_HTTP_STATUS, MCP_PROBE_MAX_INFLIGHT, MCP_PROBE_VERDICT_TIMEOUT_MS, countMcpProbeDeclarations, createMcpProbeWalk, readMcpProbeBody, } from "../../capabilities/mcp-probe.js";
16
16
  import { mcpForScenario } from "../../web-admin-client/facade.js";
17
17
  import { sendScenarioNotFound } from "../scenario-read-face.js";
18
18
  import { sendJson, sendError } from "../send.js";
@@ -87,6 +87,7 @@ export async function handleCapabilities(req, res, match, ctx) {
87
87
  artifacts: facts.durableRuns(deps).available,
88
88
  approvals: facts.checkpointPark(deps).available,
89
89
  approvalReadRootGrant: facts.readRootGrant(deps).available,
90
+ modsDeploymentPackages: facts.deploymentModPackages(deps).available ? true : undefined,
90
91
  sessions: facts.sessionAuditView(deps).available,
91
92
  deviceExecutor: facts.deviceLane(deps).available
92
93
  ? {
@@ -249,7 +250,7 @@ export async function handleCapabilities(req, res, match, ctx) {
249
250
  return;
250
251
  }
251
252
  const scenarioName = await deps.scenarioGate(principal, scenarioParams[0]);
252
- await serveMcpProbeFace(res, ctx, principal, mcpForScenario(deps.config.mcpServers, scenarioName) ?? [], "deployment");
253
+ await serveMcpProbeFace(res, ctx, principal, { declaredBy: "deployment", specs: mcpForScenario(deps.config.mcpServers, scenarioName) ?? [] });
253
254
  return;
254
255
  }
255
256
  case "capability-mcp-probe": {
@@ -267,7 +268,7 @@ export async function handleCapabilities(req, res, match, ctx) {
267
268
  sendMcpProbeRefusal(res, ctx, parsed.refusal, "bad_request");
268
269
  return;
269
270
  }
270
- await serveMcpProbeFace(res, ctx, principal, parsed.specs, "caller");
271
+ await serveMcpProbeFace(res, ctx, principal, { declaredBy: "caller", drafts: parsed.drafts });
271
272
  return;
272
273
  }
273
274
  case "models": {
@@ -282,6 +283,7 @@ export async function handleCapabilities(req, res, match, ctx) {
282
283
  provider: m.provider,
283
284
  ...(modelReasoningDecl(m) !== undefined ? { reasoning: modelReasoningDecl(m) } : {}),
284
285
  ...(modelImageInput(m) !== "undeclared" ? { vision: modelImageInput(m) === "yes" } : {}),
286
+ supportsThinkingOff: thinkingOffSpelled(m),
285
287
  ...(m.contextWindow ? { contextWindow: m.contextWindow } : {}),
286
288
  ...(m.maxTokens ? { maxOutputTokens: m.maxTokens } : {}),
287
289
  ...((levels) => (levels !== undefined ? { supportedEffortLevels: [...levels] } : {}))(reasoningEffortLevelsOf(m)),
@@ -322,18 +324,18 @@ function sendMcpProbeRefusal(res, ctx, refusal, outcome, retryAfterSec) {
322
324
  ...(retryAfterSec !== undefined ? { retryAfterSec } : {}),
323
325
  });
324
326
  }
325
- async function serveMcpProbeFace(res, ctx, principal, specs, declaredBy) {
327
+ async function serveMcpProbeFace(res, ctx, principal, input) {
326
328
  const { deps } = ctx;
327
329
  const local = ctx.local.capabilities.mcpProbe;
328
- if (specs.length === 0) {
330
+ if (countMcpProbeDeclarations(input) === 0) {
329
331
  deps.metrics?.inc("mcp_probe_total", { outcome: "empty" });
330
332
  sendJson(res, 200, buildEmptyMcpProbeFace());
331
333
  return;
332
334
  }
333
- const key = buildMcpProbeCacheKey(principal, specs);
335
+ const key = buildMcpProbeCacheKey(principal, input);
334
336
  const hit = local.cached(key, Date.now());
335
337
  if (hit !== undefined) {
336
- await answerMcpProbe(res, ctx, hit, "cached", declaredBy);
338
+ await answerMcpProbe(res, ctx, hit, "cached", input.declaredBy);
337
339
  return;
338
340
  }
339
341
  if (local.inflight() >= MCP_PROBE_MAX_INFLIGHT) {
@@ -345,9 +347,9 @@ async function serveMcpProbeFace(res, ctx, principal, specs, declaredBy) {
345
347
  sendMcpProbeRefusal(res, ctx, buildMcpProbeRateRefusal(), "rate_limited", verdict.retryAfterSec);
346
348
  return;
347
349
  }
348
- const walk = createMcpProbeWalk(specs, principal);
350
+ const walk = createMcpProbeWalk(input, principal, deps.peerDesk);
349
351
  local.remember(key, Date.now(), walk);
350
- await answerMcpProbe(res, ctx, walk.answer, "probed", declaredBy);
352
+ await answerMcpProbe(res, ctx, walk.answer, "probed", input.declaredBy);
351
353
  }
352
354
  async function answerMcpProbe(res, ctx, answer, outcome, declaredBy) {
353
355
  const { deps } = ctx;
@@ -8,19 +8,19 @@
8
8
  * **hub 在场性**判 —— v1 部署形 = 单副本(基线稿 §4.6),汇聚端的活连接集就是本副本的 presence
9
9
  * 真源;`name` = displayName,空串诚实缺席;`lastSeenAt` ISO-8601)。**吊销行不列**:revoked 是
10
10
  * 终态(重注册 = 新 deviceId),列出来只会被壳当换绑目标然后撞 404。
11
- * · `GET /v1/devices/sessions/:rootSessionId` —— S-4 绑定读面(壳换绑 UX 的全部材料):
12
- * `{ rootSessionId, boundDevice: { deviceId, rev, boundAt } }`。稿 §4 允许「并入 session 读面」或
13
- * 「独立读面」两形,选**独立读面**:sessions 的 GET 面承诺「无参请求字节不变」(routes/sessions.ts
14
- * 的 additive contract 注),且 audit 投影由 boot/session-faces 组装、不持 device 店 —— 并入即跨
15
- * 装配缝加一次店读;独立读面把 device wire 族收在一个文件里,户口与门序自洽。
16
- * · `POST /v1/devices/sessions/:rootSessionId/rebind` —— 显式换绑动词(v2 稿 §2 门序五步,逐字):
17
- * ① gatedPrincipal + owner 门(非属主 404 反枚举同串,[ref] 纪律)
11
+ * · 绑定读面在会话族:`GET /v1/sessions/:id/execution-placement`(`routes/sessions.ts`;体 = 执行放置格的 wire 投影,
12
+ * device 车道上是 `{ role: "writer", lane: "device", device: { deviceId, rev } }`)。绑定是根会话行上的执行格,读只有会话店的执行席一只。
13
+ * · `POST /v1/devices/sessions/:rootSessionId/rebind` —— 显式换绑动词(v2 稿 §2 门序五步):
14
+ * ① gatedPrincipal + owner 门 —— 一次行读(会话店执行席:住客 + 格),属主门判**格上**两属主列(非属主 404 反枚举同串,[ref] 纪律)
18
15
  * ② 目标设备在场门(不存在 / 不是该域的 / 已吊销 ⇒ 404 **同串** —— 设备 id 也是不可枚举面)
19
- * ③ 无活跃 run 门(**CAS 前判、CAS 语句不兼职**;durable 判据 = run store 的 session claim
20
- * `getActiveTaskId`,hub 内存在途计数不作门 —— 内存态跨副本不可见;拒 = 409
21
- * `device.rebind_active_run` 体带 `activeTaskId`)
22
- * ④ 前态 CAS(店内单事务:`rev` 恒变列 + `session_rebound` 审计行;输 = 409
23
- * `device.rebind_rev_conflict` 体带 `currentRev` —— [ref] currentPending 哲学,壳一跳重定位)
16
+ * ③ 无活跃 run 门:池级 `getActiveTaskId` 读一次作**早拒**(零审计行);**保证**在设备店的换绑事务里 —— 同一连接先锁根会话行、
17
+ * 再锁定读 `task_active`,有认领 ⇒ 不写(读写序律;hub 内存在途计数不作门 —— 内存态跨副本不可见);两处同拒 409
18
+ * `device.rebind_active_run` 体带 `activeTaskId`
19
+ * ③′ 子代半(写前一次观察):以根为根的活后台子代(running / parked;`probeSessionChildren` 那一次枚举,scope = 会话属主 `sessionChildrenScope`,
20
+ * 与删除门同读)⇒ 同码 409,体 `children`(名册 id)+ `retirePath`(显式结清口 —— 先结清、再换绑)
21
+ * ④ 前态 CAS(店内单事务:按门那一读的住客 ∧ 格上两属主列 ∧ rev 定址,`execution_rev` 恒变列 + `session_rebound` 审计行;输 = 409
22
+ * `device.rebind_rev_conflict` 体带 `currentRev` —— [ref] currentPending 哲学,壳一跳重定位;门后会话被删并同 id 重登记 ⇒ 住客不符 ⇒ 设备 404,
23
+ * 永不写到新住客的格上)
24
24
  * ⑤ 赢 = 200 `{ rootSessionId, deviceId, rev, workspaceCarryover: "none" }`。
25
25
  * 幂等形:toDeviceId ≡ 当前 ∧ expectedRev ≡ 当前 rev ⇒ 200 回显不加 rev(真幂等,不写审计)。
26
26
  * `workspaceCarryover: "none"` 是 **wire 冻结字面**(v2 稿 §5):换绑只改路由,不搬字节 —— 壳据
@@ -34,7 +34,7 @@
34
34
  * 才构成一个 device 域;缺 tenant claim 的凭据(纯 principal 头 / 静态 token 直连)**判不出域** ⇒
35
35
  * 非 operator 一律响亮 403(fail-closed + loud,[ref]:静默空列表是假健康)——基线稿对 enrollment 的
36
36
  * 同向裁定是「缺 tenant/global scope 明确拒注册」。enrollment/管理端点与**首绑写协议**都从
37
- * 这同一个函数取域([ref] 起它住在 `http/device-placement.ts` —— 提交腿也要用,放在路由域里是层序倒挂),
37
+ * 这同一个函数取域(它住在 `http/execution-context.ts` —— 提交腿的设备准入也要用,放在路由域里是层序倒挂),
38
38
  * 不得手抄第二份派生。
39
39
  *
40
40
  * ── 反枚举 404 的单一铸造点 ──────────────────────────────────────────────────────────────────────
@@ -81,12 +81,6 @@ export declare const DEVICES_ROUTES: readonly [{
81
81
  readonly pattern: RegExp;
82
82
  readonly label: "/v1/devices/sessions/:id/rebind";
83
83
  readonly methods: readonly ["POST"];
84
- }, {
85
- readonly id: "device-session";
86
- readonly pattern: RegExp;
87
- readonly label: "/v1/devices/sessions/:id";
88
- readonly credentialGated: readonly ["GET"];
89
- readonly methods: readonly ["GET"];
90
84
  }, {
91
85
  readonly prefix: "/v1/devices/";
92
86
  readonly credentialGated: readonly ["POST"];