@sema-agent/server 7.131.0 → 7.133.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 (509) hide show
  1. package/MIGRATION.md +575 -0
  2. package/README.md +12 -9
  3. package/README.zh-CN.md +9 -6
  4. package/USAGE.md +326 -98
  5. package/deploy/sema-up/README.md +19 -1
  6. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -2
  7. package/deploy/sema-up/chart/templates/server.yaml +5 -2
  8. package/deploy/sema-up/stack/compose.yaml +3 -0
  9. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
  10. package/dist/adoption/plan.d.ts +3 -2
  11. package/dist/adoption/plan.js +7 -5
  12. package/dist/adoption/runner.js +3 -10
  13. package/dist/adoption/sql.d.ts +3 -2
  14. package/dist/adoption/sql.js +3 -5
  15. package/dist/adoption/wire.d.ts +4 -1
  16. package/dist/adoption/wire.js +1 -0
  17. package/dist/approval-ask-audit-store.d.ts +31 -13
  18. package/dist/approval-ask-audit-store.js +272 -74
  19. package/dist/approval-ask-machine.d.ts +15 -0
  20. package/dist/approval-ask-machine.js +6 -0
  21. package/dist/approval-ask-retention.d.ts +136 -0
  22. package/dist/approval-ask-retention.js +102 -0
  23. package/dist/approval-card.d.ts +82 -20
  24. package/dist/approval-card.js +46 -16
  25. package/dist/approval-deny-reasons.d.ts +1 -1
  26. package/dist/approval.d.ts +9 -0
  27. package/dist/approval.js +24 -3
  28. package/dist/audit.d.ts +33 -19
  29. package/dist/audit.js +12 -16
  30. package/dist/auth-bridge.d.ts +4 -0
  31. package/dist/auth-bridge.js +10 -11
  32. package/dist/bake-runner/main.js +3 -2
  33. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  34. package/dist/boot/deployment-mods.d.ts +31 -0
  35. package/dist/boot/deployment-mods.js +26 -0
  36. package/dist/boot/execution-env.d.ts +1 -1
  37. package/dist/boot/execution-env.js +27 -12
  38. package/dist/boot/execution-lane.js +11 -0
  39. package/dist/boot/leader.js +2 -0
  40. package/dist/boot/memory-boundary.d.ts +2 -2
  41. package/dist/boot/memory-boundary.js +2 -2
  42. package/dist/boot/memory-consolidation.d.ts +5 -4
  43. package/dist/boot/memory-consolidation.js +7 -6
  44. package/dist/boot/org-delivery.d.ts +28 -0
  45. package/dist/boot/org-delivery.js +20 -0
  46. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  47. package/dist/boot/read-deny-anchor-audit.js +36 -0
  48. package/dist/boot/reapers.d.ts +3 -0
  49. package/dist/boot/reapers.js +21 -1
  50. package/dist/boot/resolve-spec.d.ts +4 -5
  51. package/dist/boot/resolve-spec.js +74 -123
  52. package/dist/boot/runner-deps.d.ts +19 -8
  53. package/dist/boot/runner-deps.js +15 -2
  54. package/dist/boot/session-faces.d.ts +2 -7
  55. package/dist/boot/session-faces.js +4 -2
  56. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  57. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  58. package/dist/boot/stage-03-store-backend.js +2 -2
  59. package/dist/boot/stage-04-stores.d.ts +1 -1
  60. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  61. package/dist/boot/stage-05-execution-env.js +7 -10
  62. package/dist/boot/stage-06-runners.d.ts +4 -2
  63. package/dist/boot/stage-06-runners.js +11 -7
  64. package/dist/boot/stage-07-capability-layer.d.ts +4 -2
  65. package/dist/boot/stage-07-capability-layer.js +27 -6
  66. package/dist/boot/stage-08-reapers.d.ts +4 -2
  67. package/dist/boot/stage-08-reapers.js +2 -2
  68. package/dist/boot/stage-09-leader.d.ts +6 -9
  69. package/dist/boot/stage-10-http-server.d.ts +6 -9
  70. package/dist/boot/stage-10-http-server.js +7 -9
  71. package/dist/boot/stores.d.ts +1 -1
  72. package/dist/boot/stores.js +7 -8
  73. package/dist/boot/web-admin.d.ts +4 -3
  74. package/dist/boot/web-admin.js +186 -98
  75. package/dist/boot/workflow-orchestration.d.ts +2 -0
  76. package/dist/boot/workflow-orchestration.js +2 -1
  77. package/dist/brain.js +25 -25
  78. package/dist/breaker-key.d.ts +6 -0
  79. package/dist/breaker-key.js +29 -0
  80. package/dist/budget.js +5 -4
  81. package/dist/caller-text-echo.d.ts +23 -0
  82. package/dist/caller-text-echo.js +21 -0
  83. package/dist/capabilities/brand-identity.d.ts +26 -0
  84. package/dist/capabilities/brand-identity.js +27 -0
  85. package/dist/capabilities/center-plugins.d.ts +7 -0
  86. package/dist/capabilities/center-plugins.js +1 -1
  87. package/dist/capabilities/center-prompts.d.ts +32 -59
  88. package/dist/capabilities/center-prompts.js +18 -32
  89. package/dist/capabilities/composite-members.d.ts +1 -1
  90. package/dist/capabilities/composite-members.js +1 -1
  91. package/dist/capabilities/engine-capabilities.js +2 -1
  92. package/dist/capabilities/execution-lane.js +2 -2
  93. package/dist/capabilities/hands-lane.d.ts +30 -6
  94. package/dist/capabilities/hands-lane.js +11 -1
  95. package/dist/capabilities/mcp-probe.d.ts +48 -3
  96. package/dist/capabilities/mcp-probe.js +47 -135
  97. package/dist/capabilities/scenarios.d.ts +95 -45
  98. package/dist/capabilities/scenarios.js +53 -38
  99. package/dist/capabilities/skills.d.ts +19 -4
  100. package/dist/capabilities/skills.js +30 -10
  101. package/dist/capability-facts.d.ts +34 -4
  102. package/dist/capability-facts.js +28 -2
  103. package/dist/center-config-tier.d.ts +69 -0
  104. package/dist/center-config-tier.js +42 -0
  105. package/dist/checkpoint-identity.d.ts +16 -0
  106. package/dist/checkpoint-identity.js +19 -0
  107. package/dist/config-catalog.d.ts +17 -10
  108. package/dist/config-catalog.js +47 -32
  109. package/dist/config-d-migration.d.ts +3 -1
  110. package/dist/config-d-migration.js +2 -2
  111. package/dist/config-dir-trust.d.ts +114 -0
  112. package/dist/config-dir-trust.js +388 -0
  113. package/dist/config-invariants.d.ts +18 -5
  114. package/dist/config-invariants.js +35 -3
  115. package/dist/config-lkg.d.ts +9 -9
  116. package/dist/config-lkg.js +20 -4
  117. package/dist/config-provider.d.ts +9 -2
  118. package/dist/config-provider.js +18 -6
  119. package/dist/config-types.d.ts +75 -20
  120. package/dist/config-types.js +8 -0
  121. package/dist/config.d.ts +13 -0
  122. package/dist/config.js +137 -62
  123. package/dist/delegated-session-owner.d.ts +185 -0
  124. package/dist/delegated-session-owner.js +119 -0
  125. package/dist/device-protocol.d.ts +5 -3
  126. package/dist/device-protocol.js +1 -1
  127. package/dist/device-ws-hub.js +21 -2
  128. package/dist/device-ws-protocol.d.ts +70 -6
  129. package/dist/device-ws-protocol.js +50 -22
  130. package/dist/elicitation.js +3 -2
  131. package/dist/entry-problems.d.ts +34 -0
  132. package/dist/entry-problems.js +14 -0
  133. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  134. package/dist/env-name-allowlist-knobs.js +5 -4
  135. package/dist/exec-bounds.d.ts +15 -3
  136. package/dist/exec-bounds.js +4 -0
  137. package/dist/execution-lane-caps.d.ts +56 -10
  138. package/dist/execution-lane-caps.js +50 -0
  139. package/dist/field-conflict.d.ts +43 -0
  140. package/dist/field-conflict.js +20 -0
  141. package/dist/fleet/fleet-bus.d.ts +80 -6
  142. package/dist/fleet/fleet-bus.js +41 -9
  143. package/dist/hooks/classic-stations.d.ts +91 -0
  144. package/dist/hooks/classic-stations.js +27 -0
  145. package/dist/hooks/command-spawn.d.ts +57 -0
  146. package/dist/hooks/command-spawn.js +139 -0
  147. package/dist/hooks/hook-llm.d.ts +8 -5
  148. package/dist/hooks/hook-llm.js +79 -36
  149. package/dist/hooks/hook-runner.d.ts +349 -95
  150. package/dist/hooks/hook-runner.js +855 -568
  151. package/dist/hooks/hook-source.d.ts +44 -0
  152. package/dist/hooks/hook-source.js +40 -0
  153. package/dist/http/active-run-conflict.d.ts +5 -13
  154. package/dist/http/active-run-conflict.js +17 -27
  155. package/dist/http/admission.d.ts +8 -2
  156. package/dist/http/admission.js +188 -101
  157. package/dist/http/approval-sweeps.js +99 -21
  158. package/dist/http/caller-trust.d.ts +77 -0
  159. package/dist/http/caller-trust.js +143 -0
  160. package/dist/http/checkpoint-ctx.d.ts +41 -24
  161. package/dist/http/checkpoint-ctx.js +23 -16
  162. package/dist/http/claimed-run.d.ts +14 -0
  163. package/dist/http/claimed-run.js +15 -0
  164. package/dist/http/device-placement.d.ts +2 -4
  165. package/dist/http/device-placement.js +7 -11
  166. package/dist/http/dispatch.d.ts +0 -4
  167. package/dist/http/dispatch.js +15 -13
  168. package/dist/http/durable-run-leg.d.ts +6 -33
  169. package/dist/http/durable-run-leg.js +36 -10
  170. package/dist/http/health-self-report.d.ts +7 -5
  171. package/dist/http/health-self-report.js +4 -18
  172. package/dist/http/owner-access.d.ts +56 -0
  173. package/dist/http/owner-access.js +42 -0
  174. package/dist/http/principal-gate.d.ts +70 -28
  175. package/dist/http/principal-gate.js +61 -15
  176. package/dist/http/readiness.d.ts +11 -5
  177. package/dist/http/readiness.js +10 -3
  178. package/dist/http/refusal-body.d.ts +21 -0
  179. package/dist/http/refusal-body.js +4 -0
  180. package/dist/http/request-guards.d.ts +43 -23
  181. package/dist/http/request-guards.js +53 -17
  182. package/dist/http/resume-ctx-read.d.ts +2 -5
  183. package/dist/http/resume-ctx-read.js +7 -4
  184. package/dist/http/resume-legs.d.ts +28 -4
  185. package/dist/http/resume-legs.js +169 -91
  186. package/dist/http/retired-service-deps.d.ts +43 -0
  187. package/dist/http/retired-service-deps.js +18 -0
  188. package/dist/http/route-ctx.d.ts +44 -28
  189. package/dist/http/route-ctx.js +4 -0
  190. package/dist/http/route-table.js +2 -0
  191. package/dist/http/routes/a2a-serve.js +3 -4
  192. package/dist/http/routes/admin-config-refresh.js +1 -1
  193. package/dist/http/routes/admin-drain.js +1 -1
  194. package/dist/http/routes/admin-memory-optout.js +5 -1
  195. package/dist/http/routes/adoption.js +2 -9
  196. package/dist/http/routes/approvals-assistant.js +49 -48
  197. package/dist/http/routes/background.js +3 -2
  198. package/dist/http/routes/capabilities.js +21 -5
  199. package/dist/http/routes/config-catalog.d.ts +3 -3
  200. package/dist/http/routes/config-catalog.js +1 -1
  201. package/dist/http/routes/devices.js +3 -17
  202. package/dist/http/routes/diagnostics.js +6 -2
  203. package/dist/http/routes/images.js +6 -7
  204. package/dist/http/routes/leader.js +1 -8
  205. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  206. package/dist/http/routes/mcp-reconnect.js +3 -2
  207. package/dist/http/routes/memory-bundle.js +1 -1
  208. package/dist/http/routes/memory-compliance.js +1 -1
  209. package/dist/http/routes/memory-consolidation.js +1 -1
  210. package/dist/http/routes/memory-origin.js +1 -1
  211. package/dist/http/routes/memory-policy.d.ts +1 -1
  212. package/dist/http/routes/memory-policy.js +3 -2
  213. package/dist/http/routes/notify-wake.js +8 -21
  214. package/dist/http/routes/observability.js +2 -2
  215. package/dist/http/routes/principal-inbox.d.ts +39 -0
  216. package/dist/http/routes/principal-inbox.js +76 -0
  217. package/dist/http/routes/retention-ops.js +2 -9
  218. package/dist/http/routes/rules.js +5 -18
  219. package/dist/http/routes/runs.d.ts +5 -0
  220. package/dist/http/routes/runs.js +34 -107
  221. package/dist/http/routes/session-sync.js +15 -26
  222. package/dist/http/routes/sessions.js +30 -40
  223. package/dist/http/routes/shared-memory.js +2 -13
  224. package/dist/http/routes/side-query.js +8 -7
  225. package/dist/http/routes/tasks.js +67 -21
  226. package/dist/http/routes/workflows.d.ts +351 -0
  227. package/dist/http/routes/workflows.js +153 -119
  228. package/dist/http/send.d.ts +33 -10
  229. package/dist/http/send.js +21 -4
  230. package/dist/http/server.d.ts +27 -18
  231. package/dist/http/server.js +34 -6
  232. package/dist/http/stored-body-replay.js +17 -4
  233. package/dist/http/submit-ctx.d.ts +25 -0
  234. package/dist/http/submit-ctx.js +40 -0
  235. package/dist/http/submit-replay.d.ts +4 -3
  236. package/dist/http/submit-replay.js +5 -4
  237. package/dist/http/wire-types.d.ts +11 -8
  238. package/dist/index.d.ts +2 -2
  239. package/dist/index.js +1 -2
  240. package/dist/lane-home-probe.d.ts +112 -0
  241. package/dist/lane-home-probe.js +101 -0
  242. package/dist/leader/endpoint.d.ts +4 -1
  243. package/dist/leader/endpoint.js +11 -10
  244. package/dist/limit-refusal.d.ts +59 -0
  245. package/dist/limit-refusal.js +15 -0
  246. package/dist/main.js +1 -1
  247. package/dist/mcp-assertion-header.d.ts +32 -19
  248. package/dist/mcp-assertion-header.js +10 -9
  249. package/dist/mcp-tool-faces.d.ts +5 -0
  250. package/dist/mcp-tool-faces.js +6 -4
  251. package/dist/memory-project-layer.d.ts +37 -0
  252. package/dist/memory-project-layer.js +46 -0
  253. package/dist/memory-scope.d.ts +24 -17
  254. package/dist/memory-scope.js +24 -8
  255. package/dist/memory-sync-client.d.ts +27 -13
  256. package/dist/memory-sync-client.js +107 -18
  257. package/dist/model-entry-refusal.d.ts +9 -1
  258. package/dist/model-entry-refusal.js +6 -1
  259. package/dist/model-plane-face.d.ts +51 -0
  260. package/dist/model-plane-face.js +24 -0
  261. package/dist/model-provider.d.ts +44 -12
  262. package/dist/model-provider.js +21 -2
  263. package/dist/model-route-endpoint.d.ts +9 -12
  264. package/dist/model-route-endpoint.js +8 -11
  265. package/dist/model-route-env.d.ts +22 -0
  266. package/dist/model-route-env.js +2 -0
  267. package/dist/model-route-gap.d.ts +60 -30
  268. package/dist/model-route-gap.js +21 -15
  269. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  270. package/dist/observability/corrupt-read-seat.js +9 -0
  271. package/dist/observability/cost-taxonomy.d.ts +7 -7
  272. package/dist/observability/err-text.d.ts +6 -0
  273. package/dist/observability/err-text.js +18 -4
  274. package/dist/observability/fail-open.d.ts +76 -14
  275. package/dist/observability/fail-open.js +76 -14
  276. package/dist/observability/metrics.js +4 -1
  277. package/dist/observability/prompt-manifest.d.ts +33 -82
  278. package/dist/observability/prompt-manifest.js +122 -17
  279. package/dist/observability/run-terminal.js +14 -6
  280. package/dist/observability/secret-env-scrub.d.ts +13 -1
  281. package/dist/observability/secret-env-scrub.js +13 -1
  282. package/dist/observability/tool-trace.d.ts +16 -21
  283. package/dist/observability/tool-trace.js +58 -49
  284. package/dist/operator-ask.d.ts +10 -0
  285. package/dist/operator-ask.js +3 -0
  286. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  287. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  288. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  289. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  290. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  291. package/dist/orchestration/workflow-notify-journal.js +27 -34
  292. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  293. package/dist/orchestration/workflow-park-decision.js +118 -0
  294. package/dist/org-delivery-gate.d.ts +81 -0
  295. package/dist/org-delivery-gate.js +59 -0
  296. package/dist/panel-tool-names.d.ts +23 -0
  297. package/dist/panel-tool-names.js +8 -0
  298. package/dist/parked-decide.d.ts +39 -6
  299. package/dist/parked-decide.js +98 -51
  300. package/dist/parked-revive-surface.d.ts +9 -2
  301. package/dist/parked-revive-surface.js +11 -8
  302. package/dist/per-task-image.d.ts +7 -1
  303. package/dist/per-task-image.js +3 -3
  304. package/dist/permission-rule-list.d.ts +1 -1
  305. package/dist/permission-rule-list.js +2 -2
  306. package/dist/plain-string-record.d.ts +25 -0
  307. package/dist/plain-string-record.js +32 -0
  308. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  309. package/dist/plugins/approval-ask-store-file.js +21 -0
  310. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  311. package/dist/plugins/approval-ask-store-memory.js +34 -2
  312. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  313. package/dist/plugins/approval-ask-store-sql.js +37 -1
  314. package/dist/plugins/blob-backend.d.ts +83 -15
  315. package/dist/plugins/blob-backend.js +68 -15
  316. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  317. package/dist/plugins/breaker-state-sql.js +4 -4
  318. package/dist/plugins/caching-session-store.d.ts +3 -0
  319. package/dist/plugins/caching-session-store.js +3 -0
  320. package/dist/plugins/checkpoint-store-sql.d.ts +28 -4
  321. package/dist/plugins/checkpoint-store-sql.js +59 -28
  322. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  323. package/dist/plugins/file-history-store-sql.js +3 -1
  324. package/dist/plugins/file-run-store.d.ts +5 -13
  325. package/dist/plugins/file-run-store.js +9 -0
  326. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  327. package/dist/plugins/fork-routing-session-store.js +12 -1
  328. package/dist/plugins/host-platform.d.ts +2 -0
  329. package/dist/plugins/host-platform.js +2 -1
  330. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  331. package/dist/plugins/k8s-bg-scripts.js +22 -0
  332. package/dist/plugins/local-checkpoint-store.d.ts +2 -0
  333. package/dist/plugins/local-checkpoint-store.js +11 -3
  334. package/dist/plugins/local-session-store.d.ts +1 -1
  335. package/dist/plugins/local-session-store.js +1 -1
  336. package/dist/plugins/local-session-window.d.ts +39 -0
  337. package/dist/plugins/local-session-window.js +54 -0
  338. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  339. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  340. package/dist/plugins/mailbox-store-sql.js +77 -52
  341. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  342. package/dist/plugins/memory-run-store.d.ts +5 -13
  343. package/dist/plugins/memory-run-store.js +8 -0
  344. package/dist/plugins/one-time-migrations.d.ts +245 -93
  345. package/dist/plugins/one-time-migrations.js +668 -207
  346. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  347. package/dist/plugins/org-delivery-quota-store.js +228 -0
  348. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  349. package/dist/plugins/permission-rule-store-sql.js +4 -18
  350. package/dist/plugins/pg-pool.js +39 -7
  351. package/dist/plugins/pg-session-storage.d.ts +20 -8
  352. package/dist/plugins/pg-session-storage.js +34 -21
  353. package/dist/plugins/remote-env-adb.d.ts +12 -12
  354. package/dist/plugins/remote-env-adb.js +21 -6
  355. package/dist/plugins/remote-env-device.js +3 -0
  356. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  357. package/dist/plugins/remote-env-e2b.js +21 -9
  358. package/dist/plugins/remote-env-host.d.ts +9 -9
  359. package/dist/plugins/remote-env-host.js +10 -32
  360. package/dist/plugins/remote-env-k8s.d.ts +103 -38
  361. package/dist/plugins/remote-env-k8s.js +288 -95
  362. package/dist/plugins/remote-env-local-docker.d.ts +27 -13
  363. package/dist/plugins/remote-env-local-docker.js +82 -11
  364. package/dist/plugins/remote-env-ssh.d.ts +21 -12
  365. package/dist/plugins/remote-env-ssh.js +117 -5
  366. package/dist/plugins/remote-shell.d.ts +0 -1
  367. package/dist/plugins/remote-shell.js +1 -2
  368. package/dist/plugins/retention-store-sql.js +6 -2
  369. package/dist/plugins/run-store-sql.d.ts +16 -14
  370. package/dist/plugins/run-store-sql.js +22 -2
  371. package/dist/plugins/schema-consistency.d.ts +54 -0
  372. package/dist/plugins/schema-consistency.js +52 -0
  373. package/dist/plugins/schema-indexes.js +2 -0
  374. package/dist/plugins/send-user-file.js +2 -2
  375. package/dist/plugins/session-epoch-anchor.d.ts +39 -0
  376. package/dist/plugins/session-epoch-anchor.js +22 -0
  377. package/dist/plugins/session-store.d.ts +18 -8
  378. package/dist/plugins/session-store.js +15 -4
  379. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  380. package/dist/plugins/shared-memory-store-sql.js +5 -16
  381. package/dist/plugins/sql-driver.d.ts +205 -1
  382. package/dist/plugins/sql-driver.js +325 -13
  383. package/dist/plugins/sql-errors.d.ts +64 -0
  384. package/dist/plugins/sql-errors.js +57 -0
  385. package/dist/plugins/store-backend.d.ts +36 -1
  386. package/dist/plugins/store-backend.js +42 -15
  387. package/dist/plugins/store-contracts.d.ts +42 -0
  388. package/dist/plugins/store-contracts.js +1 -0
  389. package/dist/plugins/tidb-pool.d.ts +6 -1
  390. package/dist/plugins/tidb-pool.js +44 -10
  391. package/dist/plugins/tidb-session-storage.d.ts +8 -11
  392. package/dist/plugins/tidb-session-storage.js +6 -16
  393. package/dist/plugins/tidb-session-store.d.ts +24 -4
  394. package/dist/plugins/tidb-session-store.js +29 -5
  395. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  396. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  397. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  398. package/dist/plugins/workflow-run-store-sql.js +50 -9
  399. package/dist/posture-source.d.ts +18 -10
  400. package/dist/posture-source.js +4 -1
  401. package/dist/project-identity.d.ts +45 -6
  402. package/dist/project-identity.js +162 -8
  403. package/dist/project-layer-key.d.ts +28 -0
  404. package/dist/project-layer-key.js +29 -0
  405. package/dist/prompts-domain-validate.d.ts +17 -61
  406. package/dist/prompts-domain-validate.js +9 -119
  407. package/dist/question.js +3 -2
  408. package/dist/read-deny-entry.d.ts +25 -0
  409. package/dist/read-deny-entry.js +12 -0
  410. package/dist/read-face-posture.d.ts +37 -11
  411. package/dist/read-face-posture.js +32 -3
  412. package/dist/request-key-closure.d.ts +9 -2
  413. package/dist/request-key-closure.js +11 -5
  414. package/dist/request-value-closure.d.ts +5 -0
  415. package/dist/request-value-closure.js +11 -3
  416. package/dist/rule-entry-list.d.ts +38 -11
  417. package/dist/rule-entry-list.js +106 -30
  418. package/dist/rules-consent.js +3 -4
  419. package/dist/run-acceptance.d.ts +19 -3
  420. package/dist/run-acceptance.js +13 -1
  421. package/dist/run-local.js +54 -24
  422. package/dist/runs.d.ts +15 -8
  423. package/dist/runs.js +35 -1
  424. package/dist/runtime-caps-resolver.d.ts +16 -6
  425. package/dist/runtime-caps-resolver.js +18 -5
  426. package/dist/sandbox-pkg-source.d.ts +19 -1
  427. package/dist/sandbox-pkg-source.js +29 -7
  428. package/dist/security.d.ts +87 -9
  429. package/dist/security.js +52 -9
  430. package/dist/server-secret-env.d.ts +44 -18
  431. package/dist/server-secret-env.js +49 -8
  432. package/dist/session-error-wire.d.ts +19 -2
  433. package/dist/session-error-wire.js +5 -1
  434. package/dist/session-incarnation.d.ts +15 -5
  435. package/dist/session-incarnation.js +16 -9
  436. package/dist/session-titled-frame.d.ts +33 -0
  437. package/dist/session-titled-frame.js +4 -0
  438. package/dist/session-titler.d.ts +9 -1
  439. package/dist/session-titler.js +27 -6
  440. package/dist/spec-fields.d.ts +28 -16
  441. package/dist/spec-fields.js +66 -54
  442. package/dist/sql-engine-posture.d.ts +31 -4
  443. package/dist/sql-engine-posture.js +36 -1
  444. package/dist/stored-request-tool-axes-m19.d.ts +19 -4
  445. package/dist/stored-request-tool-axes-m19.js +30 -2
  446. package/dist/task-a2a.d.ts +9 -1
  447. package/dist/task-a2a.js +73 -47
  448. package/dist/task-cwd.d.ts +1 -1
  449. package/dist/task-mcp.d.ts +46 -58
  450. package/dist/task-mcp.js +82 -107
  451. package/dist/task-settings.d.ts +95 -108
  452. package/dist/task-settings.js +77 -98
  453. package/dist/text-bidi.d.ts +1 -48
  454. package/dist/text-bidi.js +3 -5
  455. package/dist/tighten-only-seats.d.ts +150 -0
  456. package/dist/tighten-only-seats.js +196 -0
  457. package/dist/tool-approval.d.ts +304 -88
  458. package/dist/tool-approval.js +623 -239
  459. package/dist/trace/core-keyset-guard.d.ts +122 -20
  460. package/dist/trace/engine-notice-wire.d.ts +11 -3
  461. package/dist/trace/engine-notice-wire.js +8 -1
  462. package/dist/trace/exit-seat.d.ts +28 -0
  463. package/dist/trace/exit-seat.js +76 -0
  464. package/dist/trace/ledger-events.d.ts +6 -3
  465. package/dist/trace/ledger-sink.js +4 -1
  466. package/dist/trace/model-segment.js +1 -1
  467. package/dist/trace/project.d.ts +98 -17
  468. package/dist/trace/project.js +98 -50
  469. package/dist/trace/projection-table.d.ts +100 -0
  470. package/dist/trace/projection-table.js +130 -0
  471. package/dist/trace/redact.d.ts +54 -0
  472. package/dist/trace/redact.js +778 -152
  473. package/dist/trace/self-minted-wire.d.ts +345 -1
  474. package/dist/trace/self-minted-wire.js +117 -1
  475. package/dist/trace/wire-projection-faces.d.ts +377 -3
  476. package/dist/trace/wire-projection-faces.js +164 -1
  477. package/dist/url-refusal.js +2 -0
  478. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  479. package/dist/web-admin-client/apply-effective.js +56 -5
  480. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  481. package/dist/web-admin-client/candidate-gate.js +3 -3
  482. package/dist/web-admin-client/facade.d.ts +1 -1
  483. package/dist/web-admin-client/facade.js +1 -1
  484. package/dist/web-admin-client/http-client.js +0 -6
  485. package/dist/web-admin-client/read-face.js +5 -9
  486. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  487. package/dist/web-admin-client/read-warnings.js +10 -0
  488. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  489. package/dist/web-admin-client/skills-mcp.js +11 -9
  490. package/dist/web-admin-client/types.d.ts +2 -3
  491. package/dist/workspace-cache.d.ts +35 -0
  492. package/dist/workspace-cache.js +36 -0
  493. package/dist/workspace-space.d.ts +3 -2
  494. package/dist/workspace-space.js +2 -0
  495. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +65 -10
  496. package/package.json +8 -4
  497. package/scripts/drain-check-local.mjs +34 -1
  498. package/scripts/drain-check.mysql.sql +30 -2
  499. package/scripts/drain-check.pg.sql +29 -2
  500. package/dist/capabilities/memory-notice.d.ts +0 -138
  501. package/dist/capabilities/memory-notice.js +0 -64
  502. package/dist/capabilities/prompt.d.ts +0 -17
  503. package/dist/capabilities/prompt.js +0 -6
  504. package/dist/mcp-content-origin.d.ts +0 -34
  505. package/dist/mcp-content-origin.js +0 -10
  506. package/dist/tool-axis-words.d.ts +0 -27
  507. package/dist/tool-axis-words.js +0 -9
  508. package/dist/wire-name-bound.d.ts +0 -14
  509. package/dist/wire-name-bound.js +0 -13
@@ -0,0 +1,185 @@
1
+ /**
2
+ * 🔴 **委派子代会话的属主 —— 一条律;三处写点,各自的来源集;属主 = 判断时见证到的,写只 CAS 子行自己**(S-1770)。
3
+ *
4
+ * ## 律
5
+ * 一只委派子代会话(引擎为委派铸的 placed 会话,或停驻时从私店拷进宿主店的子代会话)的属主**不是它自己的事实**:它是发起它的那棵委派树的
6
+ * **根会话的属主**。树根是**铸行那一刻存在的那只根行**(id + 住客化身,`session-incarnation.ts`);此后同 id 另起的会话不是这棵树的根。所以写下的属主 =
7
+ * **判断时见证到的**(读法顺序按写点交来的来源集):
8
+ * 1. 根会话在本店有行 ⇒ 它的属主(根无主 ⇒ 子代也无主 —— 与根同读数,不是另一种读数);
9
+ * 2. 根行不在 ⇒ 父会话的行(同上);
10
+ * 3. 来源集里一只都不在本店(孤行)⇒ 写一只**任何已验身份都不可能等于**的哨兵属主 `_orphan:<子代 id>`(前缀登记在 `security.ts` 的保留主体前缀里,
11
+ * 鉴权边界拒任何报出它的主体)+ 写落下之后一行 warn `delegated_session_owner_orphaned`。宁可没人能读,也不让谁都能读。
12
+ * 一只记下的根 / 父是空串(`''`)⇒ 读作没有那一格(与 `session-placement.ts` 的 `placementFromRow` 同一个读法;M23 的扫描句在同一条语句里对连接键套
13
+ * `NULLIF(…, '')`,与判据同读)。
14
+ * **写只 CAS 子行自己**:`owner IS NULL ∧ incarnation = 判断时读到的子行化身 ∧ placed`({@link CLAIM_OWNERLESS_PLACED_SQL},两方言各一句)。判断与写之间
15
+ * 根 / 父被删、被同 id 重建、换住客 —— 写进去的仍是**判断时那棵树**的属主,这正是律要的:不锁根、不读根、不要求「根到写时还是那个根」。之后与任何会话一样
16
+ * 首写不可变(`register` 的首写律)。判断那一刻的检查照旧在判断里做、不进 CAS:宿主行记下的根是不是这一棵;调用方见证之后根行换没换住客
17
+ * (停驻挂点,{@link assertRootStillWitnessed})。
18
+ *
19
+ * **根先于父**:父的属主按同一条律恒等于根的属主,读根一次就到;而读父会把「父行是升级前写下的无主 placed 行」(滚动升级窗里旧副本建的、
20
+ * 一次性迁移还没回填的)那一格 NULL 传下去。父只在根行不在时才读。
21
+ *
22
+ * ## 无主的子代行
23
+ * 属主为 NULL 的 placed 行有两种来历:律给的(根无主 —— 一棵无身份调用方的树),或还没定下的(7.132.x 及更早写下的;判断时它读到的来源本身还是一只无主的
24
+ * 委派子代行)。会话行属主门上的无主放行臂对它**只对 fleet 调用方成立**(出示 service 凭据、不报主体;唯一判据 `security.ts` 的 `ownerlessRowOpen`,
25
+ * 会话面 / fork / 删除 / 审批豁免 / notify 同读;dev-open 照旧 fleet-wide),具名主体与没有 service 凭据的无身份调用方一律与别人的行同体 404 ——
26
+ * 所以还没定下的行只是暂时只有 fleet 面读得到,不是谁都能读。它们由一次性迁移 M23(窗内每次起服)或这一行的下一次停驻(停驻挂点的认领)
27
+ * 定下;两处都不承诺「一次收敛」。
28
+ * **只有记录委派树的店才认领**:SQL 两店的 placed 行记着委派树(placement 元组);local 店不实现 placement、不记委派树 ⇒ 没有可按的树 ⇒ 不认领
29
+ * (它的无主子代行在停驻挂点照「属主不是律给的那一只 ⇒ 拒」处置;收口是 local 店的 placement 协议)。不按更松的判据认领。
30
+ *
31
+ * ## 三处写点 —— 律一只,**来源集按写点不同**(都调这里,不各写一份判据)
32
+ * · **SQL 两店按 placement 建行**(`plugins/tidb-session-store.ts` / `plugins/pg-session-storage.ts` 的 acquire,{@link createPlacedSessionRow}):来源 = placement 上的
33
+ * 根、父两只 id({@link ownerSourcesOf});读来源 → 判 → 一条 INSERT(属主与 placement 同一笔落库,首写律)。读来源之后来源才变有主(滚动升级窗里别的副本的
34
+ * M23 回填了无主的父)⇒ 写下的仍是判断时的读数 —— 按律如此;那一行若因此无主,照上一节处置。
35
+ * · **停驻挂点**(`plugins/session-store.ts` 的 `ensureChildSessionDurableWithPromotion`;快路 + 晋升):来源 = **调用方交出的委派树根,只有这一只**,连同
36
+ * 调用方判断那一刻读到的根行住客({@link DelegationRootWitness};见证值,判断用,不进 CAS):请求腿 = 本请求经鉴权解析的会话,住客 = 受理时那一读
37
+ * (`boot/resolve-spec.ts`);`/decide` 的赎回腿 = 行上的 `rootSessionId`,住客 = 赎回腿 claim 前后两读的那一位(`parked-revive-surface.ts`,每一行都交)。
38
+ * 挂点先读一次根行:它在而住客不是见证的那一位 ⇒ 根被删、同 id 换了住客 ⇒ **拒**(不按新住客晋升 / 认领,{@link assertRootStillWitnessed});它不在 ⇒ 律给哨兵
39
+ * (根真没了)。不回落父:core 的挂点 `ensureChildSessionDurable(sessionId)` 只交子代 id,父 id 在这一写点拿不到;两条候选读法都读不到东西 ——
40
+ * ① 读 `session_meta(子代).placement_parent_session_id`:晋升的拷贝分支恰恰只在子代**没有** placed 行时才走(有 placed 行 ⇒ 快路),这一列按构造为空;
41
+ * ② 登记簿(后台子代行 / core 注册簿):core 接口没有「按子代 sessionId 查行」的读口,SQL 表 `background_agent.session_id` 无索引,加一条只在一处用的私有读腿
42
+ * = 多一只读法;`scope` 列还把匿名折成 `"default"`(与真名 `default` 的租户不可分)。行上没有根(没有会话的取用形)⇒ 来源集空 ⇒ 哨兵。
43
+ * 宿主行的属主经 {@link settleDelegatedOwner} 定下:宿主行记着委派树而不是这一棵 ⇒ 拒(两边属主都是 null 也拒);记着这一棵 ⇒ 按**它记录的** placement(根、父)
44
+ * 再判一次(与 M23 对同一行同读数 —— 见证只答「是不是这一棵、根住客换没换」,来源集取行上记的),无主 ⇒ 认领(CAS 子行),认领试了没落下 ⇒ 拒;然后比对 ——
45
+ * 不是律给的那一只 ⇒ 停驻否决(晋升建行前那一道与停驻快路同一只函数、同一句拒因)。
46
+ * · **一次性迁移 M23**(`plugins/one-time-migrations.ts`):存量 placed 行,来源 = 根、父(扫描时同一条语句连读两行);回填 = 认领(同一句,绑扫描读到的子行化身)。
47
+ * local 店不实现 placement(子代住私店、只经停驻挂点进宿主店),没有第一处与第三处,也不认领(上一节)。
48
+ */
49
+ import type { SessionPlacement } from "@sema-agent/core";
50
+ import { type SessionFace, type SessionFaceReader } from "./session-incarnation.js";
51
+ /** 孤行哨兵属主的前缀(保留主体前缀:`security.ts` 的 `RESERVED_PRINCIPAL_PREFIXES` 登记它,任何身份来源报出带它的主体都在鉴权边界被拒)。 */
52
+ export declare const ORPHAN_SESSION_OWNER_PREFIX = "_orphan:";
53
+ /** 一只推不出属主的子代会话的哨兵属主(每只会话一个值:即便哪天有谁误配出这个串,也只够得着这一只)。 */
54
+ export declare function orphanSessionOwner(sessionId: string): string;
55
+ /** 这只属主是不是孤行哨兵(自己推不出、或继承自一只推不出的祖先 —— 两者都没有任何调用方匹配)。前缀的知识只住在本模块。 */
56
+ export declare function isOrphanSessionOwner(owner: string | null): boolean;
57
+ /** 一只来源行在本店的读数:`undefined` = 本店没有这一行;`null` = 行在、无主;串 = 属主(与 `ownerOf` 的三态同义)。 */
58
+ export type SourceOwnerReading = string | null | undefined;
59
+ /** 律的判决:继承自哪一行(属主可以是 `null` = 来源行无主),或孤行(哨兵属主)。 */
60
+ export type InheritedOwner = {
61
+ readonly kind: "inherited";
62
+ readonly owner: string | null;
63
+ readonly from: string;
64
+ } | {
65
+ readonly kind: "orphan";
66
+ readonly owner: string;
67
+ };
68
+ /** placement 上律要读的两格(调用方交的 `SessionPlacement`,或店里落库的那一份元组)。 */
69
+ export type PlacementSources = Pick<SessionPlacement, "rootSessionId" | "parentSessionId">;
70
+ /** placement 上的来源 id,按读法顺序(根在前、父在后;去空、去重 —— 一层子代的根就是父)。 */
71
+ export declare function ownerSourcesOf(placement: PlacementSources): readonly string[];
72
+ /** 律本身(纯函数)。`sources` = 按读法顺序的 `[来源 id, 本店读数]`;第一只**在本店有行**的来源给出属主,一只都没有 ⇒ 孤行。 */
73
+ export declare function inheritedOwnerOf(childId: string, sources: ReadonlyArray<readonly [string, SourceOwnerReading]>): InheritedOwner;
74
+ /**
75
+ * 判据读到的**委派树**:记下的根(`null` = 没有根 —— 缺或空串)+ 判据那一读里根行的住客(化身;`null` = 那一读里根行不在本店)。
76
+ * 只在判断里用(停驻挂点:宿主行记下的根是不是这一棵、调用方见证之后根住客换没换);**不进认领句**(律:写只 CAS 子行自己)。
77
+ */
78
+ export interface DelegationTree {
79
+ readonly root: string | null;
80
+ readonly rootIncarnation: string | null;
81
+ }
82
+ /** 律的一次判:判决 + 读的来源 id(warn 行用)+ 判据读到的委派树(判断用)。 */
83
+ export interface JudgedOwner {
84
+ readonly inherited: InheritedOwner;
85
+ readonly sources: readonly string[];
86
+ readonly tree: DelegationTree;
87
+ }
88
+ /**
89
+ * **唯一的判据**(纯函数;三处写点都调它):按记录的来源(placement 上的根、父)与它们此刻在本店的会话面(属主 + 化身;本店没有的 id 不在表里)判一次 ——
90
+ * 律给出属主(判断时见证到的),同时交出判据读到的委派树(判断用)。
91
+ */
92
+ export declare function judgeDelegatedOwner(childId: string, placement: PlacementSources, faces: ReadonlyMap<string, SessionFace>): JudgedOwner;
93
+ /** 一次读回若干来源行的会话面(属主 + 化身;本店没有的 id 不在表里)。 */
94
+ export type SourceFacesReader = (ids: readonly string[]) => Promise<ReadonlyMap<string, SessionFace>>;
95
+ /** 按律读一次来源行、判一次(纯读,不告警 —— 孤行 warn 只在写落下之后,{@link reportWrittenOwner})。读失败照原样上抛(调用方的写随之失败,不猜一个属主)。 */
96
+ export declare function readDelegatedOwner(childId: string, placement: PlacementSources, read: SourceFacesReader): Promise<JudgedOwner>;
97
+ /** 孤行那一行 warn 的去处(logger 的子集)。 */
98
+ export interface OrphanOwnerLog {
99
+ warn(msg: string, fields?: Record<string, unknown>): void;
100
+ }
101
+ /** 写点的名字(warn 行的 `site` 字段:运维据它知道是哪一笔写下的哨兵)。`promotion` = 停驻挂点(晋升建行,或对宿主无主行的认领)。 */
102
+ export type DelegatedOwnerSite = "placed_create" | "promotion";
103
+ /** 一笔写**已落下**之后调:写下的是孤行哨兵 ⇒ 一行 warn(写失败的那一笔走不到这里,不留下「已写」的话)。继承来的属主(含继承自孤行祖先的哨兵)不告。 */
104
+ export declare function reportWrittenOwner(log: OrphanOwnerLog, site: DelegatedOwnerSite, childId: string, judged: JudgedOwner): void;
105
+ /**
106
+ * 委派继承律的**认领句**(唯一的写法;停驻挂点对宿主无主行的认领与一次性迁移 M23 的回填**同一只**,都按 {@link claimOwnerlessParams} 绑参;窗随 M23)。
107
+ * **只 CAS 子行自己**:这一位住客(判断时读到的化身)、仍无主、placed 行(原文 CAS,第二次命中零行)。不读根、不锁根、不连接 —— 判断与写之间根 / 父的变化
108
+ * 不改变写进去的值(律:属主 = 判断时见证到的,本文件头注)。两方言只差占位符。
109
+ * MySQL 协议下 mysql2 缺省带 FOUND_ROWS,但条件里有 `owner IS NULL` ⇒ 匹配行数 = 改写行数。
110
+ */
111
+ export declare const CLAIM_OWNERLESS_PLACED_SQL: Readonly<Record<"tidb" | "pg", string>>;
112
+ /** 认领句的绑参(两方言同序;店与 M23 都经它,不各排一份):`incarnation` = 判断时读到的子行化身。 */
113
+ export declare function claimOwnerlessParams(sessionId: string, incarnation: string, owner: string): readonly [string, string, string];
114
+ /** 店的认领口(SQL 两店 + 缓存包装层同一个签名):这只会话的那一位住客(`incarnation`)仍是无主的 placed 行 ⇒ 写 `owner`,返回是否写成。 */
115
+ export type OwnerlessClaim = (sessionId: string, incarnation: string, owner: string) => Promise<boolean>;
116
+ /** 一只 SQL 店建 placed 行要交的 I/O(两店按各自方言交,都是店的既有读写口;读法与顺序只住在 {@link createPlacedSessionRow})。 */
117
+ export interface PlacedRowWriter {
118
+ /** 来源行的会话面一次读回。 */
119
+ readonly readFaces: SourceFacesReader;
120
+ /** 建行那一条 INSERT(属主与 placement 同一条落库;行已在 ⇒ 一个字节不动,首写律)。 */
121
+ insert(owner: string | null): Promise<void>;
122
+ /** 写后读回本行的属主(店的 `ownerOf`:`undefined` = 行不在)—— 只为判「本笔的哨兵落没落下」。 */
123
+ readOwner(): Promise<string | null | undefined>;
124
+ }
125
+ /**
126
+ * **SQL 两店建 placed 行的那一只**:读来源 → 判 → INSERT(属主 = 判断时见证到的,与 placement 同一笔落库)。INSERT 是首写律(行已在 ⇒ 不动),
127
+ * 所以落库的那一行未必是本笔写的(同 id 的另一位建行者先落了)⇒ 读回属主:带着本笔判出的孤行哨兵才告一行 warn;别的 ⇒ 什么都没写下,不告。
128
+ * 不写后复核:判断之后来源才变有主,写下的仍是判断时的读数(律;那一行若因此无主,由无主放行臂的判据挡住非 fleet 调用方、由 M23 / 下一次停驻定下 —— 本文件头注)。
129
+ */
130
+ export declare function createPlacedSessionRow(sessionId: string, placement: PlacementSources, io: PlacedRowWriter, log: OrphanOwnerLog): Promise<void>;
131
+ /**
132
+ * 停驻挂点的**见证**:调用方交出的委派树根 + 调用方判断那一刻读到的根行住客(化身;`null` = 那一读里根行不在)。请求腿 = 鉴权解析的会话与受理时那一读;
133
+ * 赎回腿 = 行上的根与 claim 前后两读的那一位住客。
134
+ */
135
+ export interface DelegationRootWitness {
136
+ readonly sessionId: string;
137
+ readonly incarnation: string | null;
138
+ }
139
+ /**
140
+ * 「根行的化身已换 ⇒ 拒」在停驻挂点的那一道(挂点的见证是更早的一读,不是这一读):此刻根行在、而住客不是见证的那一位(见证那一读里不在,或是另一位)⇒ 抛。
141
+ * 根行此刻不在 ⇒ 不抛(根真没了 ⇒ 律给哨兵,不是「换了住客」)。这是**判断**的一部分;判断之后、写之前根的变化不改变写进去的值(律:属主 = 判断时见证到的)。
142
+ */
143
+ export declare function assertRootStillWitnessed(sessionId: string, witness: DelegationRootWitness | undefined, judged: JudgedOwner): void;
144
+ /** 记录委派树的宿主店多交的两只口(SQL 两店与缓存包装层;local 店两只都没有 ⇒ 没有可按的树 ⇒ 不认领)。 */
145
+ export interface DelegationTreeStore {
146
+ /** 本行落库的 placement 元组(`undefined` = 本行不是 placed 行 ⇒ 不记委派树)。 */
147
+ readonly placementOf: (sessionId: string) => Promise<PlacementSources | undefined>;
148
+ readonly claimOwnerless: OwnerlessClaim;
149
+ }
150
+ /** 停驻挂点要的宿主店口(属主读 / 属主 + 化身一次读;记录委派树的店另有 {@link DelegationTreeStore})。 */
151
+ export interface DelegatedOwnerHost {
152
+ readonly ownerOf: (sessionId: string) => Promise<string | null | undefined>;
153
+ readonly sessionFaceOf: SessionFaceReader;
154
+ /** 缺席 = 这只店不记委派树(不实现 placement)。 */
155
+ readonly trees: DelegationTreeStore | undefined;
156
+ }
157
+ /**
158
+ * 「这只宿主店能不能承载委派子代的属主」—— 只判这一处(停驻快路与晋升建行读同一只):`ownerOf` + 住客脸都在 ⇒ 绑好的 {@link DelegatedOwnerHost};缺任一只 ⇒ `undefined`
159
+ * (调用方响亮拒:读不回属主的宿主店上,停驻出来的就是一只谁都能读的转录)。记录委派树的两只口(`placementOf` + `claimOwnerless`)**同在或同缺**:同在 ⇒ `trees`;
160
+ * 同缺 ⇒ 不记委派树;只有一只 ⇒ `undefined`(记着树却认领不了,或认领却读不回树 —— 半只店不猜)。住客脸的「能不能」沿用 `sessionFaceReadOf` 的同一处判法。
161
+ */
162
+ export declare function delegatedOwnerHostOf(store: {
163
+ readonly ownerOf?: (sessionId: string) => Promise<string | null | undefined>;
164
+ readonly placementOf?: (sessionId: string) => Promise<PlacementSources | undefined>;
165
+ readonly claimOwnerless?: OwnerlessClaim;
166
+ }): DelegatedOwnerHost | undefined;
167
+ /** {@link settleDelegatedOwner} 的结果:这一笔是否写下了属主(认领成了);`judged` = 宿主行记着委派树时按**它记录的树**的那一次判(缺席 = 行不记委派树,按调用方的判)。 */
168
+ export interface SettledOwner {
169
+ readonly claimed: boolean;
170
+ readonly judged: JudgedOwner | undefined;
171
+ }
172
+ /**
173
+ * 宿主店里这只会话的行属主**按律定下来**(停驻快路与晋升建行前**同一只函数、同一句拒因**;拒句不带任何一方的属主值 —— 它会进停驻否决的错误面)。
174
+ * `owner` / `tree` = 调用方按见证的根判出的属主与委派树(晋升建行写下的就是它)。
175
+ * 1. 宿主行**记着委派树**(SQL 店的 placed 行):记下的根 ≠ `tree.root` ⇒ 抛(不以属主是否 null 为前提:那是另一棵树的行);是这一棵 ⇒ **按它记录的 placement(根、父)
176
+ * 再判一次**(唯一判据 {@link judgeDelegatedOwner} —— 与 M23 / 建行复核对这一行同读数:根不在取父),调用方那一读之后根住客变了 ⇒ 抛({@link assertRootStillWitnessed},
177
+ * 调用方的读数当见证);行无主而律给出属主 ⇒ 认领(CAS 刚读到的那一位住客;写进去的是这一次判的读数)—— **试了没落下 ⇒ 抛**(这一行在判断之后被写过 /
178
+ * 删过 / 换了住客,不由事后的属主比对放行);
179
+ * 2. 不记委派树的行(local 店的行、SQL 店的普通行)不认领、不再判;
180
+ * 3. 比对 —— 此刻的属主不是律给的那一只(记着委派树 ⇒ 按记录的树判的那一只;否则 = `owner`)⇒ 抛。
181
+ */
182
+ export declare function settleDelegatedOwner(host: DelegatedOwnerHost, sessionId: string, owner: string | null, tree: DelegationTree): Promise<SettledOwner>;
183
+ /** 按 id 逐只读会话面的店(`sessionFaceOf`)→ {@link SourceFacesReader}。 */
184
+ export declare function sourceFacesVia(sessionFaceOf: SessionFaceReader): SourceFacesReader;
185
+ //# sourceMappingURL=delegated-session-owner.d.ts.map
@@ -0,0 +1,119 @@
1
+ import { sessionFaceReadOf } from "./session-incarnation.js";
2
+ export const ORPHAN_SESSION_OWNER_PREFIX = "_orphan:";
3
+ export function orphanSessionOwner(sessionId) {
4
+ return `${ORPHAN_SESSION_OWNER_PREFIX}${sessionId}`;
5
+ }
6
+ export function isOrphanSessionOwner(owner) {
7
+ return owner !== null && owner.startsWith(ORPHAN_SESSION_OWNER_PREFIX);
8
+ }
9
+ function sourceIdOf(id) {
10
+ return id === undefined || id === "" ? undefined : id;
11
+ }
12
+ export function ownerSourcesOf(placement) {
13
+ const out = [];
14
+ for (const id of [sourceIdOf(placement.rootSessionId), sourceIdOf(placement.parentSessionId)]) {
15
+ if (id !== undefined && !out.includes(id))
16
+ out.push(id);
17
+ }
18
+ return out;
19
+ }
20
+ export function inheritedOwnerOf(childId, sources) {
21
+ for (const [id, reading] of sources) {
22
+ if (reading !== undefined)
23
+ return { kind: "inherited", owner: reading, from: id };
24
+ }
25
+ return { kind: "orphan", owner: orphanSessionOwner(childId) };
26
+ }
27
+ export function judgeDelegatedOwner(childId, placement, faces) {
28
+ const sources = ownerSourcesOf(placement);
29
+ const inherited = inheritedOwnerOf(childId, sources.map((id) => [id, faces.get(id)?.owner]));
30
+ const root = sourceIdOf(placement.rootSessionId) ?? null;
31
+ return { inherited, sources, tree: { root, rootIncarnation: root === null ? null : (faces.get(root)?.incarnation ?? null) } };
32
+ }
33
+ export async function readDelegatedOwner(childId, placement, read) {
34
+ const ids = ownerSourcesOf(placement);
35
+ return judgeDelegatedOwner(childId, placement, ids.length === 0 ? new Map() : await read(ids));
36
+ }
37
+ export function reportWrittenOwner(log, site, childId, judged) {
38
+ if (judged.inherited.kind !== "orphan")
39
+ return;
40
+ log.warn("delegated_session_owner_orphaned", {
41
+ sessionId: childId,
42
+ site,
43
+ sources: judged.sources,
44
+ owner: judged.inherited.owner,
45
+ note: "a delegated session's owner could not be derived: none of the sessions it was delegated from has a row in this session store (none was named, or they are gone), " +
46
+ "so the session was written with an owner no caller can match — only fleet-wide and operator faces reach it. If someone should read it, set its owner in the session store by hand",
47
+ });
48
+ }
49
+ export const CLAIM_OWNERLESS_PLACED_SQL = {
50
+ tidb: "UPDATE session_meta SET owner = ? WHERE session_id = ? AND incarnation = ? AND owner IS NULL AND placement_kind IS NOT NULL",
51
+ pg: "UPDATE session_meta SET owner = $1 WHERE session_id = $2 AND incarnation = $3 AND owner IS NULL AND placement_kind IS NOT NULL",
52
+ };
53
+ export function claimOwnerlessParams(sessionId, incarnation, owner) {
54
+ return [owner, sessionId, incarnation];
55
+ }
56
+ export async function createPlacedSessionRow(sessionId, placement, io, log) {
57
+ const judged = await readDelegatedOwner(sessionId, placement, io.readFaces);
58
+ await io.insert(judged.inherited.owner);
59
+ if (judged.inherited.kind === "orphan" && (await io.readOwner()) === judged.inherited.owner)
60
+ reportWrittenOwner(log, "placed_create", sessionId, judged);
61
+ }
62
+ export function assertRootStillWitnessed(sessionId, witness, judged) {
63
+ if (witness === undefined || judged.tree.rootIncarnation === null || judged.tree.rootIncarnation === witness.incarnation)
64
+ return;
65
+ throw new Error(`session "${sessionId}" cannot be parked as this delegated session: its delegation tree's root session was deleted and its id registered again since the delegation was judged, ` +
66
+ "so the root's current owner is not the owner of this delegation tree");
67
+ }
68
+ export function delegatedOwnerHostOf(store) {
69
+ const ownerOf = store.ownerOf;
70
+ const sessionFaceOf = sessionFaceReadOf(store);
71
+ const placementOf = store.placementOf;
72
+ const claimOwnerless = store.claimOwnerless;
73
+ if (typeof ownerOf !== "function" || sessionFaceOf === undefined)
74
+ return undefined;
75
+ if ((typeof placementOf === "function") !== (typeof claimOwnerless === "function"))
76
+ return undefined;
77
+ const trees = typeof placementOf === "function" && typeof claimOwnerless === "function"
78
+ ? { placementOf: (id) => placementOf.call(store, id), claimOwnerless: (id, incarnation, owner) => claimOwnerless.call(store, id, incarnation, owner) }
79
+ : undefined;
80
+ return { ownerOf: (id) => ownerOf.call(store, id), sessionFaceOf, trees };
81
+ }
82
+ export async function settleDelegatedOwner(host, sessionId, owner, tree) {
83
+ const refuse = () => new Error(`session "${sessionId}" cannot be parked as this delegated session: the host session store holds a session with this id that is not this delegation tree's ` +
84
+ "(a different owner, or a different recorded delegation root), and the transcript is neither copied into it nor attested by it");
85
+ const face = await host.sessionFaceOf(sessionId);
86
+ const trees = host.trees;
87
+ const recorded = face === undefined || trees === undefined ? undefined : await trees.placementOf(sessionId);
88
+ let expected = owner;
89
+ let judged;
90
+ let claimed = false;
91
+ if (face !== undefined && trees !== undefined && recorded !== undefined) {
92
+ if ((sourceIdOf(recorded.rootSessionId) ?? null) !== tree.root)
93
+ throw refuse();
94
+ judged = await readDelegatedOwner(sessionId, recorded, sourceFacesVia(host.sessionFaceOf));
95
+ if (tree.root !== null)
96
+ assertRootStillWitnessed(sessionId, { sessionId: tree.root, incarnation: tree.rootIncarnation }, judged);
97
+ expected = judged.inherited.owner;
98
+ if (face.owner === null && expected !== null) {
99
+ claimed = await trees.claimOwnerless(sessionId, face.incarnation, expected);
100
+ if (!claimed)
101
+ throw refuse();
102
+ }
103
+ }
104
+ if ((await host.ownerOf(sessionId)) !== expected)
105
+ throw refuse();
106
+ return { claimed, judged };
107
+ }
108
+ export function sourceFacesVia(sessionFaceOf) {
109
+ return async (ids) => {
110
+ const out = new Map();
111
+ for (const id of ids) {
112
+ const face = await sessionFaceOf(id);
113
+ if (face !== undefined)
114
+ out.set(id, face);
115
+ }
116
+ return out;
117
+ };
118
+ }
119
+ //# sourceMappingURL=delegated-session-owner.js.map
@@ -7,9 +7,11 @@
7
7
  *
8
8
  * 为什么是精选而不是把 `device-ws-protocol.ts` 整只暴露:那个模块里还有**服务端**自己的半场(帧解析与准入、
9
9
  * 验签、各种铸 id)。把它们变成公开面,等于替实现细节背上 semver。这里只再导出 executor **会用到**的:
10
- * 闭集常量、两向帧型、以及构造握手签名字节所需的两只纯函数。要加名字 = 改这只文件 + 同批改
10
+ * 闭集常量、两向帧型、构造握手签名字节所需的两只纯函数,以及两份「随协议修订号走」的事实:结局码按修订号分行的增量表
11
+ * (`DEVICE_OUTCOME_ERROR_CODES_BY_REVISION` + 取集函数 `deviceOutcomeErrorCodesAt`:executor 按自己声明的修订号裁剪会发的词)与
12
+ * helloAck 数值位的界表(`DEVICE_HELLO_ACK_BOUNDS` + 判据 `deviceHelloAckNumberFlaw`:executor 读到越界值 ⇒ 协议违例)。要加名字 = 改这只文件 + 同批改
11
13
  * `test/device-protocol-subpath.test.ts` 的闭集(新增公开面必须是一次有意的动作)。
12
14
  */
13
- export { DEVICE_WS_PATH, DEVICE_PROTOCOL_VERSION, DEVICE_MIN_PROTOCOL_VERSION, DEVICE_HELLO_DOMAIN_TAG, DEVICE_WS_LIMITS, DEVICE_FRAME_TYPES, DEVICE_SERVER_FRAME_TYPES, DEVICE_HELLO_REJECT_CODES, DEVICE_BYE_REASONS, DEVICE_GOODBYE_REASONS, DEVICE_CHUNK_KINDS, DEVICE_CANCEL_STATES, DEVICE_INSTRUCTION_KINDS, DEVICE_OUTCOME_ERROR_CODES, buildHelloSignaturePayload, decodeUlid16, } from "./device-ws-protocol.js";
14
- export type { DeviceFrameType, DeviceServerFrameType, DeviceHelloRejectCode, DeviceByeReason, DeviceGoodbyeReason, DeviceChunkKind, DeviceCancelState, DeviceInstructionKind, DeviceOutcomeErrorCode, WireFileInfo, WireWriteExpectation, DeviceInstructionOutcome, DeviceInstructionArgsByKind, DeviceChallengeFrame, DeviceHelloAckFrame, DeviceHelloRejectFrame, DeviceInstructionFrame, DevicePayloadFrame, DeviceServerFrame, DeviceHelloFrame, DeviceFrame, DeviceResultFrame, DeviceChunkFrame, DeviceGoodbyeFrame, DeviceHeartbeatFrame, } from "./device-ws-protocol.js";
15
+ export { DEVICE_WS_PATH, DEVICE_PROTOCOL_VERSION, DEVICE_MIN_PROTOCOL_VERSION, DEVICE_HELLO_DOMAIN_TAG, DEVICE_WS_LIMITS, DEVICE_FRAME_TYPES, DEVICE_SERVER_FRAME_TYPES, DEVICE_HELLO_REJECT_CODES, DEVICE_BYE_REASONS, DEVICE_GOODBYE_REASONS, DEVICE_CHUNK_KINDS, DEVICE_CANCEL_STATES, DEVICE_INSTRUCTION_KINDS, DEVICE_OUTCOME_ERROR_CODES, DEVICE_OUTCOME_ERROR_CODES_BY_REVISION, DEVICE_HELLO_ACK_BOUNDS, buildHelloSignaturePayload, decodeUlid16, deviceOutcomeErrorCodesAt, deviceHelloAckNumberFlaw, } from "./device-ws-protocol.js";
16
+ export type { DeviceFrameType, DeviceServerFrameType, DeviceHelloRejectCode, DeviceByeReason, DeviceGoodbyeReason, DeviceChunkKind, DeviceCancelState, DeviceInstructionKind, DeviceOutcomeErrorCode, DeviceProtocolRevision, DeviceHelloAckNumericField, WireFileInfo, WireWriteExpectation, DeviceInstructionOutcome, DeviceInstructionArgsByKind, DeviceChallengeFrame, DeviceHelloAckFrame, DeviceHelloRejectFrame, DeviceInstructionFrame, DevicePayloadFrame, DeviceServerFrame, DeviceHelloFrame, DeviceFrame, DeviceResultFrame, DeviceChunkFrame, DeviceGoodbyeFrame, DeviceHeartbeatFrame, } from "./device-ws-protocol.js";
15
17
  //# sourceMappingURL=device-protocol.d.ts.map
@@ -1,2 +1,2 @@
1
- export { DEVICE_WS_PATH, DEVICE_PROTOCOL_VERSION, DEVICE_MIN_PROTOCOL_VERSION, DEVICE_HELLO_DOMAIN_TAG, DEVICE_WS_LIMITS, DEVICE_FRAME_TYPES, DEVICE_SERVER_FRAME_TYPES, DEVICE_HELLO_REJECT_CODES, DEVICE_BYE_REASONS, DEVICE_GOODBYE_REASONS, DEVICE_CHUNK_KINDS, DEVICE_CANCEL_STATES, DEVICE_INSTRUCTION_KINDS, DEVICE_OUTCOME_ERROR_CODES, buildHelloSignaturePayload, decodeUlid16, } from "./device-ws-protocol.js";
1
+ export { DEVICE_WS_PATH, DEVICE_PROTOCOL_VERSION, DEVICE_MIN_PROTOCOL_VERSION, DEVICE_HELLO_DOMAIN_TAG, DEVICE_WS_LIMITS, DEVICE_FRAME_TYPES, DEVICE_SERVER_FRAME_TYPES, DEVICE_HELLO_REJECT_CODES, DEVICE_BYE_REASONS, DEVICE_GOODBYE_REASONS, DEVICE_CHUNK_KINDS, DEVICE_CANCEL_STATES, DEVICE_INSTRUCTION_KINDS, DEVICE_OUTCOME_ERROR_CODES, DEVICE_OUTCOME_ERROR_CODES_BY_REVISION, DEVICE_HELLO_ACK_BOUNDS, buildHelloSignaturePayload, decodeUlid16, deviceOutcomeErrorCodesAt, deviceHelloAckNumberFlaw, } from "./device-ws-protocol.js";
2
2
  //# sourceMappingURL=device-protocol.js.map
@@ -2,7 +2,8 @@ import http from "node:http";
2
2
  import { createHash } from "node:crypto";
3
3
  import { WebSocketServer } from "ws";
4
4
  import { DEVICE_DRAINING_CODE, DEVICE_ERROR_CODES, DEVICE_ERROR_STATUS, assertDeviceIdShape, } from "./device-store.js";
5
- import { DEVICE_MIN_PROTOCOL_VERSION, DEVICE_WS_LIMITS, DEVICE_WS_PATH, DEVICE_PROTOCOL_VERSION, asInstructionKind, buildHelloSignaturePayload, formatGeneration, mintChallengeNonce, mintInstructionId, parseDeviceFrameText, verifyHelloSignature, } from "./device-ws-protocol.js";
5
+ import { MAX_TIMER_DELAY_MS } from "./config-types.js";
6
+ import { DEVICE_HELLO_ACK_BOUNDS, DEVICE_MIN_PROTOCOL_VERSION, DEVICE_WS_LIMITS, DEVICE_WS_PATH, DEVICE_PROTOCOL_VERSION, asInstructionKind, buildHelloSignaturePayload, deviceHelloAckNumberFlaw, formatGeneration, mintChallengeNonce, mintInstructionId, parseDeviceFrameText, verifyHelloSignature, } from "./device-ws-protocol.js";
6
7
  const OUTCOME_UNKNOWN_SENTENCE = "outcome unknown — the command may have already executed on the device; verify its effect before re-running";
7
8
  const SETTLED_MEMO_MAX = 512;
8
9
  const DRAIN_RETRY_AFTER_SEC = 15;
@@ -45,6 +46,23 @@ const ALLOWED_SUCCESS_OUTCOME_KINDS = {
45
46
  createTempDir: ["write"],
46
47
  createTempFile: ["write"],
47
48
  };
49
+ const HELLO_ACK_FIELD_SOURCES = {
50
+ heartbeatIntervalMs: "option heartbeatIntervalMs",
51
+ maxInflight: "option maxInflightPerDevice (env DEVICE_MAX_INFLIGHT_PER_DEVICE)",
52
+ reconnectGraceMs: "option reconnectGraceMs (env DEVICE_RECONNECT_GRACE_MS)",
53
+ cancelGraceMs: "option cancelGraceMs",
54
+ chunkGapTimeoutMs: "option chunkGapTimeoutMs",
55
+ executionCeilingGraceMs: "option execGraceMs (sent as helloAck.executionCeilingGraceMs)",
56
+ };
57
+ function assertHelloAckNumbersInBounds(values) {
58
+ for (const field of Object.keys(DEVICE_HELLO_ACK_BOUNDS)) {
59
+ const flaw = deviceHelloAckNumberFlaw(field, values[field]);
60
+ if (flaw !== null) {
61
+ throw new Error(`device lane: ${HELLO_ACK_FIELD_SOURCES[field]}=${String(values[field])} ${flaw} — it is sent to every executor verbatim in helloAck ` +
62
+ `(docs/DEVICE-EXECUTOR-PROTOCOL.md §1.5), so an out-of-range value is refused at startup instead of being clamped or replaced by a default`);
63
+ }
64
+ }
65
+ }
48
66
  export function createDeviceWsHub(options) {
49
67
  const { store, enrollment, replicaId, logger, metrics } = options;
50
68
  const heartbeatIntervalMs = options.heartbeatIntervalMs ?? 15_000;
@@ -57,6 +75,7 @@ export function createDeviceWsHub(options) {
57
75
  const cancelGraceMs = options.cancelGraceMs ?? 10_000;
58
76
  const reconnectGraceMs = options.reconnectGraceMs ?? 120_000;
59
77
  const chunkGapTimeoutMs = options.chunkGapTimeoutMs ?? 30_000;
78
+ assertHelloAckNumbersInBounds({ heartbeatIntervalMs, maxInflight: maxInflightPerDevice, reconnectGraceMs, cancelGraceMs, chunkGapTimeoutMs, executionCeilingGraceMs: execGraceMs });
60
79
  const supersede = options.supersede ?? "stale_only";
61
80
  const chunkBufferMaxEntries = options.chunkBufferMaxEntries ?? 64;
62
81
  const chunkBufferMaxBytes = options.chunkBufferMaxBytes ?? 2 * 1024 * 1024;
@@ -960,7 +979,7 @@ export function createDeviceWsHub(options) {
960
979
  p.ceilingTimer = setTimeout(() => {
961
980
  metrics?.inc("device_outcome_unknown_total", { cause: "exec_ceiling" });
962
981
  settleUnknown(p, `the device did not report a result within timeoutMs+grace (${input.timeoutMs}+${execGraceMs}ms)`);
963
- }, input.timeoutMs + execGraceMs);
982
+ }, Math.min(input.timeoutMs + execGraceMs, MAX_TIMER_DELAY_MS));
964
983
  let onAbort;
965
984
  if (input.signal) {
966
985
  if (input.signal.aborted)
@@ -1,9 +1,16 @@
1
1
  import type { ExecutionErrorCode, FileErrorCode } from "@sema-agent/core";
2
2
  /** 唯一的 upgrade 路径(§5.1:`GET /v1/device/ws`)。 */
3
3
  export declare const DEVICE_WS_PATH = "/v1/device/ws";
4
- /** 本 server 铸/认的协议版本。加帧/加字段 = additive 不升版;改语义 = 升版(§5.6)。 */
5
- export declare const DEVICE_PROTOCOL_VERSION = 1;
6
- /** 低于此版本 ⇒ `helloReject: protocol_version_unsupported`(响亮拒,不静默降级,[ref])。 */
4
+ /**
5
+ * 本 server 铸/认的协议**修订号**(wire 上的 `protocolVersion`)。
6
+ *
7
+ * 规则(§5.6):加帧 / 加字段 = additive 不升号;改语义 = 升号;**executor → server 方向的闭集加词也升号** —— 修订号是 executor 在发 hello 之前
8
+ * 唯一能读到的「这台 server 受理什么」的事实(challenge 帧带它),所以 server 受理的词集必须是修订号的函数
9
+ * ({@link DEVICE_OUTCOME_ERROR_CODES_BY_REVISION},协议文档同名表),不另发第二份列表。闭集变了而修订号没变 = 同一个修订号两种受理集
10
+ * (7.132.0 给结局码加 `liveness_no_output` 时没抬号,本版补到 2)。类型锁:抬了号而表没有这一行 ⇒ 编译红。
11
+ */
12
+ export declare const DEVICE_PROTOCOL_VERSION: DeviceProtocolRevision;
13
+ /** 低于此版本 ⇒ `helloReject: protocol_version_unsupported`(响亮拒,不静默降级,[ref])。兼容窗 = [MIN, 当前]。 */
7
14
  export declare const DEVICE_MIN_PROTOCOL_VERSION = 1;
8
15
  /** 签名的 domain tag —— 换域即换 tag,防止别处的 Ed25519 签名被拿来当握手用。 */
9
16
  export declare const DEVICE_HELLO_DOMAIN_TAG = "sema.device.hello.v1";
@@ -60,10 +67,24 @@ export type DeviceInstructionKind = (typeof DEVICE_INSTRUCTION_KINDS)[number];
60
67
  export declare function asInstructionKind(v: string): DeviceInstructionKind | null;
61
68
  /**
62
69
  * 结果帧的错误码闭集 = core 的 `FileErrorCode ∪ ExecutionErrorCode`(§5.1「error code 全闭集」:
63
- * 未知值映射为协议错误而非透传 string)。`satisfies` 保证这里没有 core 不认识的词。
70
+ * 未知值映射为协议错误而非透传 string)—— **按修订号分行的增量表,是这个闭集唯一的一份来源**:
71
+ * 修订号 N 的词集 = 行 ≤ N 的并({@link deviceOutcomeErrorCodesAt}),{@link DEVICE_OUTCOME_ERROR_CODES} 是当前修订号的那一份(派生)。
72
+ * 已发布修订号的行不许改(测试冻结了集合哈希);加词 = 抬 {@link DEVICE_PROTOCOL_VERSION} + 加一行。`satisfies` 保证这里没有 core 不认识的词。
64
73
  */
65
- export declare const DEVICE_OUTCOME_ERROR_CODES: readonly ["aborted", "already_exists", "auth_failed", "callback_error", "invalid", "is_directory", "not_directory", "not_found", "not_supported", "outcome_unknown", "permission_denied", "precondition_failed", "shell_unavailable", "spawn_error", "suspended", "target_unavailable", "timeout", "transport_lost", "unknown"];
66
- export type DeviceOutcomeErrorCode = (typeof DEVICE_OUTCOME_ERROR_CODES)[number];
74
+ export declare const DEVICE_OUTCOME_ERROR_CODES_BY_REVISION: {
75
+ readonly 1: readonly ["aborted", "already_exists", "auth_failed", "callback_error", "invalid", "is_directory", "not_directory", "not_found", "not_supported", "outcome_unknown", "permission_denied", "precondition_failed", "shell_unavailable", "spawn_error", "suspended", "target_unavailable", "timeout", "transport_lost", "unknown"];
76
+ readonly 2: readonly ["liveness_no_output"];
77
+ };
78
+ /** 本包认得的协议修订号(= 增量表的行键)。 */
79
+ export type DeviceProtocolRevision = keyof typeof DEVICE_OUTCOME_ERROR_CODES_BY_REVISION;
80
+ export type DeviceOutcomeErrorCode = (typeof DEVICE_OUTCOME_ERROR_CODES_BY_REVISION)[DeviceProtocolRevision][number];
81
+ /**
82
+ * 协议修订号 `revision` 受理的结局码全集(行 ≤ revision 的并,字典序)。executor 按**自己声明的修订号**取这一份来裁剪自己会发的词
83
+ * (声明哪个修订号见协议文档 §1.2)。不是本包认得的修订号 ⇒ 抛 `RangeError`(不静默给空集或全集)。
84
+ */
85
+ export declare function deviceOutcomeErrorCodesAt(revision: number): readonly DeviceOutcomeErrorCode[];
86
+ /** 当前修订号受理的结局码全集(派生自增量表;server 解析 `result` 帧时判的就是它)。 */
87
+ export declare const DEVICE_OUTCOME_ERROR_CODES: readonly DeviceOutcomeErrorCode[];
67
88
  /**
68
89
  * **编译期**对账的另一半:core 往 `FileErrorCode`/`ExecutionErrorCode` 加一个词而本表没跟 ⇒ `Exclude`
69
90
  * 不再是 `never` ⇒ `return v` 编译红。`satisfies` 只挡「本表多写了 core 不认的词」,这条挡「本表漏了
@@ -229,6 +250,7 @@ export type DeviceChallengeFrame = {
229
250
  export type DeviceHelloAckFrame = {
230
251
  t: "helloAck";
231
252
  ok: true;
253
+ /** 六个数值位(本位与 `maxInflight` 与下面四个宽限位)的合法域见 {@link DEVICE_HELLO_ACK_BOUNDS};server 起服校验,wire 上恒在界。 */
232
254
  heartbeatIntervalMs: number;
233
255
  maxInflight: number;
234
256
  /** ⚠️ canonical 十进制**字符串**(F18:SQL BIGINT 超 2^53,wire 不声明装不下 SQL 类型的字段)。 */
@@ -256,6 +278,48 @@ export type DeviceHelloRejectFrame = {
256
278
  code: DeviceHelloRejectCode;
257
279
  minProtocolVersion?: number;
258
280
  };
281
+ /**
282
+ * helloAck 六个数值位的**合法域**(闭区间、整数)—— 一处成文:汇聚端创建时按它校验(越界 ⇒ 响亮拒启,不夹取、不换成缺省),
283
+ * 运维旋钮(`DEVICE_RECONNECT_GRACE_MS` / `DEVICE_MAX_INFLIGHT_PER_DEVICE`)的解析器与配置目录行读它,协议文档 §1.5 的 Form 栏由它派生(机器门逐行对账)。
284
+ * 所以 **wire 上恒在界**:executor 读到越界值 ⇒ 当协议违例关连接(与 `bad_shape` 同形),**不**读成缺省。
285
+ *
286
+ * · ms 位下界 `0` = 该计时器不留宽限(立即到点)—— 真含义,放行;`heartbeatIntervalMs` 下界 `1`:`0` 的心跳周期无法执行(忙等),
287
+ * 且汇聚端据它判 STALE(`0 × misses` ⇒ 一连上就失联);
288
+ * · ms 位上界 = {@link MAX_TIMER_DELAY_MS}(汇聚端自己拿这些值武装 `setTimeout`,更大的值被折成 1 ms —— 宽限被静默抹掉);
289
+ * · `maxInflight` 下界 `1`(`0` ⇒ 每次派发都 `device.busy`),无上界(安全整数内)。
290
+ */
291
+ export declare const DEVICE_HELLO_ACK_BOUNDS: {
292
+ readonly heartbeatIntervalMs: {
293
+ readonly min: 1;
294
+ readonly max: 2147483647;
295
+ };
296
+ readonly maxInflight: {
297
+ readonly min: 1;
298
+ readonly max: number;
299
+ };
300
+ readonly reconnectGraceMs: {
301
+ readonly min: 0;
302
+ readonly max: 2147483647;
303
+ };
304
+ readonly cancelGraceMs: {
305
+ readonly min: 0;
306
+ readonly max: 2147483647;
307
+ };
308
+ readonly chunkGapTimeoutMs: {
309
+ readonly min: 0;
310
+ readonly max: 2147483647;
311
+ };
312
+ readonly executionCeilingGraceMs: {
313
+ readonly min: 0;
314
+ readonly max: 2147483647;
315
+ };
316
+ };
317
+ export type DeviceHelloAckNumericField = keyof typeof DEVICE_HELLO_ACK_BOUNDS;
318
+ /**
319
+ * helloAck 某数值位的值是否在界:在界 ⇒ `null`;越界(含非数 / NaN / 小数)⇒ 一句话(`must be an integer in [0, 2147483647]`)。
320
+ * 汇聚端创建时用它拒启;executor 收到 helloAck 后用它判(越界 ⇒ 协议违例)。值不是数时同样越界(`unknown` 入参:直接喂 `JSON.parse` 的字段)。
321
+ */
322
+ export declare function deviceHelloAckNumberFlaw(field: DeviceHelloAckNumericField, value: unknown): string | null;
259
323
  export type DeviceInstructionFrame = {
260
324
  [K in DeviceInstructionKind]: {
261
325
  t: "instruction";
@@ -1,6 +1,7 @@
1
1
  import { createPublicKey, randomBytes, randomInt, verify as edVerify } from "node:crypto";
2
+ import { MAX_TIMER_DELAY_MS, inWholeNumberBounds, wholeNumberBoundsRule } from "./config-types.js";
2
3
  export const DEVICE_WS_PATH = "/v1/device/ws";
3
- export const DEVICE_PROTOCOL_VERSION = 1;
4
+ export const DEVICE_PROTOCOL_VERSION = 2;
4
5
  export const DEVICE_MIN_PROTOCOL_VERSION = 1;
5
6
  export const DEVICE_HELLO_DOMAIN_TAG = "sema.device.hello.v1";
6
7
  export const DEVICE_WS_LIMITS = {
@@ -42,27 +43,42 @@ const INSTRUCTION_KIND_SET = new Set(DEVICE_INSTRUCTION_KINDS);
42
43
  export function asInstructionKind(v) {
43
44
  return INSTRUCTION_KIND_SET.has(v) ? v : null;
44
45
  }
45
- export const DEVICE_OUTCOME_ERROR_CODES = [
46
- "aborted",
47
- "already_exists",
48
- "auth_failed",
49
- "callback_error",
50
- "invalid",
51
- "is_directory",
52
- "not_directory",
53
- "not_found",
54
- "not_supported",
55
- "outcome_unknown",
56
- "permission_denied",
57
- "precondition_failed",
58
- "shell_unavailable",
59
- "spawn_error",
60
- "suspended",
61
- "target_unavailable",
62
- "timeout",
63
- "transport_lost",
64
- "unknown",
65
- ];
46
+ export const DEVICE_OUTCOME_ERROR_CODES_BY_REVISION = {
47
+ 1: [
48
+ "aborted",
49
+ "already_exists",
50
+ "auth_failed",
51
+ "callback_error",
52
+ "invalid",
53
+ "is_directory",
54
+ "not_directory",
55
+ "not_found",
56
+ "not_supported",
57
+ "outcome_unknown",
58
+ "permission_denied",
59
+ "precondition_failed",
60
+ "shell_unavailable",
61
+ "spawn_error",
62
+ "suspended",
63
+ "target_unavailable",
64
+ "timeout",
65
+ "transport_lost",
66
+ "unknown",
67
+ ],
68
+ 2: ["liveness_no_output"],
69
+ };
70
+ export function deviceOutcomeErrorCodesAt(revision) {
71
+ if (!Number.isInteger(revision) || revision < 1 || revision > DEVICE_PROTOCOL_VERSION) {
72
+ throw new RangeError(`device-ws: no outcome-code set for protocol revision ${String(revision)} (this package knows revisions 1..${DEVICE_PROTOCOL_VERSION})`);
73
+ }
74
+ const out = [];
75
+ for (const [rev, codes] of Object.entries(DEVICE_OUTCOME_ERROR_CODES_BY_REVISION)) {
76
+ if (Number(rev) <= revision)
77
+ out.push(...codes);
78
+ }
79
+ return out.sort();
80
+ }
81
+ export const DEVICE_OUTCOME_ERROR_CODES = deviceOutcomeErrorCodesAt(DEVICE_PROTOCOL_VERSION);
66
82
  const OUTCOME_ERROR_CODE_SET = new Set(DEVICE_OUTCOME_ERROR_CODES);
67
83
  export function assertCoreOutcomeErrorCodesAreCovered(v) {
68
84
  return v;
@@ -70,6 +86,18 @@ export function assertCoreOutcomeErrorCodesAreCovered(v) {
70
86
  export const DEVICE_FRAME_REJECT_REASONS = ["frame_too_large", "not_json", "not_object", "unknown_type", "bad_shape"];
71
87
  export const DEVICE_FRAME_GATE_REJECT_REASONS = ["pre_hello", "generation_mismatch", "unknown_instruction", "instruction_not_committed", "device_mismatch"];
72
88
  export const DEVICE_FRAME_AUDIT_REASONS = [...DEVICE_FRAME_REJECT_REASONS, ...DEVICE_FRAME_GATE_REJECT_REASONS];
89
+ export const DEVICE_HELLO_ACK_BOUNDS = {
90
+ heartbeatIntervalMs: { min: 1, max: MAX_TIMER_DELAY_MS },
91
+ maxInflight: { min: 1, max: Number.MAX_SAFE_INTEGER },
92
+ reconnectGraceMs: { min: 0, max: MAX_TIMER_DELAY_MS },
93
+ cancelGraceMs: { min: 0, max: MAX_TIMER_DELAY_MS },
94
+ chunkGapTimeoutMs: { min: 0, max: MAX_TIMER_DELAY_MS },
95
+ executionCeilingGraceMs: { min: 0, max: MAX_TIMER_DELAY_MS },
96
+ };
97
+ export function deviceHelloAckNumberFlaw(field, value) {
98
+ const bounds = DEVICE_HELLO_ACK_BOUNDS[field];
99
+ return inWholeNumberBounds(bounds, value) ? null : `must be ${wholeNumberBoundsRule(bounds)}`;
100
+ }
73
101
  function reject(reason, detail) {
74
102
  return { ok: false, reason, detail };
75
103
  }
@@ -2,6 +2,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { uuidv7, delimitUntrusted } from "@sema-agent/core";
3
3
  import { redactSecrets, redactDeep } from "./trace/redact.js";
4
4
  import { recordFailOpen } from "./observability/fail-open.js";
5
+ import { refusalBody } from "./http/refusal-body.js";
5
6
  const MAX_ELICIT_MESSAGE_CHARS = 4096;
6
7
  const MAX_ELICIT_SCHEMA_BYTES = 8192;
7
8
  export const DEFAULT_ELICITATION_THROTTLE = {
@@ -145,10 +146,10 @@ export class ElicitationCoordinator {
145
146
  respond(id, principal, body) {
146
147
  const parsed = parseElicitationResponse(body);
147
148
  if (!parsed.ok)
148
- return { status: 400, body: { error: parsed.error, errorCode: "request.body_shape" } };
149
+ return { status: 400, body: refusalBody({ errorCode: "request.body_shape", message: parsed.error }) };
149
150
  const entry = this.pending.get(id);
150
151
  if (!entry || (entry.owner !== null && entry.owner !== principal)) {
151
- return { status: 404, body: { error: "no pending elicitation for this id (answered, expired, or not on this replica)", errorCode: "elicitation.not_pending" } };
152
+ return { status: 404, body: refusalBody({ errorCode: "elicitation.not_pending", message: "no pending elicitation for this id (answered, expired, or not on this replica)" }) };
152
153
  }
153
154
  entry.settle(parsed.value);
154
155
  return { status: 200, body: { elicitationId: id, delivery: "applied", action: parsed.value.action } };