@sema-agent/server 7.132.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 (402) hide show
  1. package/MIGRATION.md +266 -0
  2. package/README.md +8 -9
  3. package/README.zh-CN.md +7 -6
  4. package/USAGE.md +174 -55
  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 +79 -19
  24. package/dist/approval-card.js +44 -13
  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 +27 -10
  29. package/dist/audit.js +12 -16
  30. package/dist/bake-runner/main.js +3 -2
  31. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  32. package/dist/boot/execution-env.js +9 -7
  33. package/dist/boot/execution-lane.js +11 -0
  34. package/dist/boot/leader.js +2 -0
  35. package/dist/boot/memory-boundary.js +2 -2
  36. package/dist/boot/memory-consolidation.d.ts +5 -4
  37. package/dist/boot/memory-consolidation.js +7 -6
  38. package/dist/boot/org-delivery.d.ts +28 -0
  39. package/dist/boot/org-delivery.js +20 -0
  40. package/dist/boot/reapers.d.ts +3 -0
  41. package/dist/boot/reapers.js +21 -1
  42. package/dist/boot/resolve-spec.d.ts +3 -0
  43. package/dist/boot/resolve-spec.js +60 -81
  44. package/dist/boot/runner-deps.js +1 -0
  45. package/dist/boot/session-faces.d.ts +1 -0
  46. package/dist/boot/session-faces.js +4 -2
  47. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  48. package/dist/boot/stage-06-runners.d.ts +2 -0
  49. package/dist/boot/stage-06-runners.js +6 -3
  50. package/dist/boot/stage-07-capability-layer.d.ts +2 -0
  51. package/dist/boot/stage-07-capability-layer.js +24 -3
  52. package/dist/boot/stage-08-reapers.d.ts +2 -0
  53. package/dist/boot/stage-08-reapers.js +2 -2
  54. package/dist/boot/stage-09-leader.d.ts +3 -0
  55. package/dist/boot/stage-10-http-server.d.ts +3 -0
  56. package/dist/boot/stage-10-http-server.js +5 -6
  57. package/dist/boot/stores.js +2 -2
  58. package/dist/boot/web-admin.d.ts +4 -0
  59. package/dist/boot/web-admin.js +151 -63
  60. package/dist/boot/workflow-orchestration.d.ts +2 -0
  61. package/dist/boot/workflow-orchestration.js +2 -1
  62. package/dist/brain.js +8 -3
  63. package/dist/breaker-key.d.ts +6 -0
  64. package/dist/breaker-key.js +29 -0
  65. package/dist/caller-text-echo.d.ts +9 -6
  66. package/dist/caller-text-echo.js +8 -11
  67. package/dist/capabilities/center-plugins.d.ts +7 -0
  68. package/dist/capabilities/center-plugins.js +1 -1
  69. package/dist/capabilities/composite-members.d.ts +1 -1
  70. package/dist/capabilities/composite-members.js +1 -1
  71. package/dist/capabilities/execution-lane.js +2 -2
  72. package/dist/capabilities/hands-lane.d.ts +21 -0
  73. package/dist/capabilities/hands-lane.js +10 -0
  74. package/dist/capabilities/mcp-probe.d.ts +36 -4
  75. package/dist/capabilities/mcp-probe.js +27 -4
  76. package/dist/capabilities/scenarios.d.ts +63 -7
  77. package/dist/capabilities/scenarios.js +39 -7
  78. package/dist/capability-facts.d.ts +34 -4
  79. package/dist/capability-facts.js +28 -2
  80. package/dist/center-config-tier.d.ts +69 -0
  81. package/dist/center-config-tier.js +42 -0
  82. package/dist/checkpoint-identity.d.ts +16 -0
  83. package/dist/checkpoint-identity.js +19 -0
  84. package/dist/config-catalog.d.ts +17 -10
  85. package/dist/config-catalog.js +43 -28
  86. package/dist/config-d-migration.d.ts +3 -1
  87. package/dist/config-d-migration.js +2 -2
  88. package/dist/config-dir-trust.d.ts +114 -0
  89. package/dist/config-dir-trust.js +388 -0
  90. package/dist/config-invariants.d.ts +18 -5
  91. package/dist/config-invariants.js +35 -3
  92. package/dist/config-provider.d.ts +6 -1
  93. package/dist/config-provider.js +6 -2
  94. package/dist/config-types.d.ts +71 -14
  95. package/dist/config-types.js +8 -0
  96. package/dist/config.d.ts +13 -0
  97. package/dist/config.js +121 -59
  98. package/dist/delegated-session-owner.d.ts +185 -0
  99. package/dist/delegated-session-owner.js +119 -0
  100. package/dist/device-protocol.d.ts +5 -3
  101. package/dist/device-protocol.js +1 -1
  102. package/dist/device-ws-hub.js +21 -2
  103. package/dist/device-ws-protocol.d.ts +70 -6
  104. package/dist/device-ws-protocol.js +50 -23
  105. package/dist/elicitation.js +3 -2
  106. package/dist/entry-problems.d.ts +34 -0
  107. package/dist/entry-problems.js +14 -0
  108. package/dist/execution-lane-caps.d.ts +49 -10
  109. package/dist/execution-lane-caps.js +43 -4
  110. package/dist/field-conflict.d.ts +43 -0
  111. package/dist/field-conflict.js +20 -0
  112. package/dist/fleet/fleet-bus.d.ts +80 -6
  113. package/dist/fleet/fleet-bus.js +41 -9
  114. package/dist/hooks/classic-stations.d.ts +10 -5
  115. package/dist/hooks/classic-stations.js +4 -4
  116. package/dist/hooks/command-spawn.d.ts +57 -0
  117. package/dist/hooks/command-spawn.js +139 -0
  118. package/dist/hooks/hook-llm.js +2 -2
  119. package/dist/hooks/hook-runner.d.ts +127 -25
  120. package/dist/hooks/hook-runner.js +668 -287
  121. package/dist/hooks/hook-source.d.ts +44 -0
  122. package/dist/hooks/hook-source.js +40 -0
  123. package/dist/http/active-run-conflict.d.ts +5 -13
  124. package/dist/http/active-run-conflict.js +17 -27
  125. package/dist/http/admission.js +103 -60
  126. package/dist/http/approval-sweeps.js +55 -18
  127. package/dist/http/caller-trust.d.ts +77 -0
  128. package/dist/http/caller-trust.js +143 -0
  129. package/dist/http/checkpoint-ctx.d.ts +16 -16
  130. package/dist/http/checkpoint-ctx.js +13 -10
  131. package/dist/http/claimed-run.d.ts +4 -3
  132. package/dist/http/device-placement.d.ts +2 -4
  133. package/dist/http/device-placement.js +2 -1
  134. package/dist/http/dispatch.js +10 -4
  135. package/dist/http/durable-run-leg.d.ts +4 -32
  136. package/dist/http/durable-run-leg.js +34 -10
  137. package/dist/http/owner-access.d.ts +56 -0
  138. package/dist/http/owner-access.js +42 -0
  139. package/dist/http/principal-gate.d.ts +48 -23
  140. package/dist/http/principal-gate.js +39 -12
  141. package/dist/http/readiness.d.ts +11 -5
  142. package/dist/http/readiness.js +10 -3
  143. package/dist/http/request-guards.d.ts +16 -23
  144. package/dist/http/request-guards.js +30 -15
  145. package/dist/http/resume-ctx-read.d.ts +2 -5
  146. package/dist/http/resume-ctx-read.js +7 -4
  147. package/dist/http/resume-legs.d.ts +3 -4
  148. package/dist/http/resume-legs.js +126 -69
  149. package/dist/http/retired-service-deps.d.ts +43 -0
  150. package/dist/http/retired-service-deps.js +18 -0
  151. package/dist/http/route-ctx.d.ts +44 -28
  152. package/dist/http/route-ctx.js +4 -0
  153. package/dist/http/route-table.js +2 -0
  154. package/dist/http/routes/a2a-serve.js +3 -4
  155. package/dist/http/routes/admin-config-refresh.js +1 -1
  156. package/dist/http/routes/admin-drain.js +1 -1
  157. package/dist/http/routes/admin-memory-optout.js +5 -1
  158. package/dist/http/routes/adoption.js +2 -9
  159. package/dist/http/routes/approvals-assistant.js +49 -48
  160. package/dist/http/routes/background.js +3 -2
  161. package/dist/http/routes/capabilities.js +20 -4
  162. package/dist/http/routes/config-catalog.d.ts +3 -3
  163. package/dist/http/routes/config-catalog.js +1 -1
  164. package/dist/http/routes/devices.js +3 -17
  165. package/dist/http/routes/diagnostics.js +5 -1
  166. package/dist/http/routes/images.js +6 -7
  167. package/dist/http/routes/leader.js +1 -8
  168. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  169. package/dist/http/routes/mcp-reconnect.js +3 -2
  170. package/dist/http/routes/memory-bundle.js +1 -1
  171. package/dist/http/routes/memory-compliance.js +1 -1
  172. package/dist/http/routes/memory-consolidation.js +1 -1
  173. package/dist/http/routes/memory-origin.js +1 -1
  174. package/dist/http/routes/memory-policy.d.ts +1 -1
  175. package/dist/http/routes/memory-policy.js +3 -2
  176. package/dist/http/routes/notify-wake.js +8 -21
  177. package/dist/http/routes/observability.js +2 -2
  178. package/dist/http/routes/principal-inbox.d.ts +39 -0
  179. package/dist/http/routes/principal-inbox.js +76 -0
  180. package/dist/http/routes/retention-ops.js +2 -9
  181. package/dist/http/routes/rules.js +5 -18
  182. package/dist/http/routes/runs.d.ts +5 -0
  183. package/dist/http/routes/runs.js +33 -106
  184. package/dist/http/routes/session-sync.js +15 -28
  185. package/dist/http/routes/sessions.js +30 -40
  186. package/dist/http/routes/side-query.js +6 -4
  187. package/dist/http/routes/tasks.js +50 -14
  188. package/dist/http/routes/workflows.d.ts +351 -0
  189. package/dist/http/routes/workflows.js +153 -119
  190. package/dist/http/send.d.ts +24 -3
  191. package/dist/http/send.js +14 -2
  192. package/dist/http/server.d.ts +17 -18
  193. package/dist/http/server.js +15 -13
  194. package/dist/http/stored-body-replay.js +17 -4
  195. package/dist/http/submit-ctx.js +4 -3
  196. package/dist/http/submit-replay.d.ts +4 -3
  197. package/dist/http/submit-replay.js +5 -4
  198. package/dist/http/wire-types.d.ts +10 -7
  199. package/dist/index.d.ts +1 -1
  200. package/dist/index.js +0 -1
  201. package/dist/lane-home-probe.d.ts +78 -9
  202. package/dist/lane-home-probe.js +78 -12
  203. package/dist/leader/endpoint.d.ts +4 -1
  204. package/dist/leader/endpoint.js +11 -10
  205. package/dist/limit-refusal.d.ts +17 -5
  206. package/dist/limit-refusal.js +4 -1
  207. package/dist/mcp-assertion-header.d.ts +31 -10
  208. package/dist/mcp-assertion-header.js +10 -3
  209. package/dist/mcp-tool-faces.js +1 -3
  210. package/dist/memory-project-layer.d.ts +37 -0
  211. package/dist/memory-project-layer.js +46 -0
  212. package/dist/memory-scope.d.ts +23 -16
  213. package/dist/memory-scope.js +24 -8
  214. package/dist/memory-sync-client.d.ts +27 -13
  215. package/dist/memory-sync-client.js +107 -18
  216. package/dist/model-entry-refusal.d.ts +9 -1
  217. package/dist/model-entry-refusal.js +6 -1
  218. package/dist/model-plane-face.d.ts +51 -0
  219. package/dist/model-plane-face.js +24 -0
  220. package/dist/model-provider.d.ts +5 -0
  221. package/dist/model-provider.js +4 -2
  222. package/dist/model-route-endpoint.d.ts +8 -11
  223. package/dist/model-route-endpoint.js +4 -7
  224. package/dist/model-route-env.d.ts +22 -0
  225. package/dist/model-route-env.js +2 -0
  226. package/dist/model-route-gap.d.ts +60 -30
  227. package/dist/model-route-gap.js +21 -15
  228. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  229. package/dist/observability/corrupt-read-seat.js +9 -0
  230. package/dist/observability/err-text.d.ts +6 -0
  231. package/dist/observability/err-text.js +18 -4
  232. package/dist/observability/fail-open.d.ts +35 -9
  233. package/dist/observability/fail-open.js +35 -9
  234. package/dist/observability/metrics.js +1 -0
  235. package/dist/observability/prompt-manifest.d.ts +3 -17
  236. package/dist/observability/prompt-manifest.js +45 -42
  237. package/dist/observability/tool-trace.js +9 -9
  238. package/dist/operator-ask.d.ts +10 -0
  239. package/dist/operator-ask.js +3 -0
  240. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  241. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  242. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  243. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  244. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  245. package/dist/orchestration/workflow-notify-journal.js +27 -34
  246. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  247. package/dist/orchestration/workflow-park-decision.js +118 -0
  248. package/dist/org-delivery-gate.d.ts +81 -0
  249. package/dist/org-delivery-gate.js +59 -0
  250. package/dist/panel-tool-names.d.ts +23 -0
  251. package/dist/panel-tool-names.js +8 -0
  252. package/dist/parked-decide.d.ts +39 -6
  253. package/dist/parked-decide.js +98 -51
  254. package/dist/parked-revive-surface.d.ts +9 -2
  255. package/dist/parked-revive-surface.js +11 -8
  256. package/dist/permission-rule-list.d.ts +1 -1
  257. package/dist/permission-rule-list.js +2 -2
  258. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  259. package/dist/plugins/approval-ask-store-file.js +21 -0
  260. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  261. package/dist/plugins/approval-ask-store-memory.js +34 -2
  262. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  263. package/dist/plugins/approval-ask-store-sql.js +37 -1
  264. package/dist/plugins/blob-backend.d.ts +38 -25
  265. package/dist/plugins/blob-backend.js +29 -19
  266. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  267. package/dist/plugins/breaker-state-sql.js +4 -4
  268. package/dist/plugins/caching-session-store.d.ts +3 -0
  269. package/dist/plugins/caching-session-store.js +3 -0
  270. package/dist/plugins/checkpoint-store-sql.d.ts +14 -0
  271. package/dist/plugins/checkpoint-store-sql.js +20 -2
  272. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  273. package/dist/plugins/file-history-store-sql.js +3 -1
  274. package/dist/plugins/file-run-store.d.ts +5 -13
  275. package/dist/plugins/file-run-store.js +9 -0
  276. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  277. package/dist/plugins/fork-routing-session-store.js +12 -1
  278. package/dist/plugins/host-platform.d.ts +2 -0
  279. package/dist/plugins/host-platform.js +2 -1
  280. package/dist/plugins/local-checkpoint-store.d.ts +2 -0
  281. package/dist/plugins/local-checkpoint-store.js +6 -1
  282. package/dist/plugins/local-session-store.d.ts +1 -1
  283. package/dist/plugins/local-session-window.d.ts +39 -0
  284. package/dist/plugins/local-session-window.js +54 -0
  285. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  286. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  287. package/dist/plugins/mailbox-store-sql.js +77 -52
  288. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  289. package/dist/plugins/memory-run-store.d.ts +5 -13
  290. package/dist/plugins/memory-run-store.js +8 -0
  291. package/dist/plugins/one-time-migrations.d.ts +110 -5
  292. package/dist/plugins/one-time-migrations.js +320 -8
  293. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  294. package/dist/plugins/org-delivery-quota-store.js +228 -0
  295. package/dist/plugins/pg-pool.js +39 -7
  296. package/dist/plugins/pg-session-storage.d.ts +17 -4
  297. package/dist/plugins/pg-session-storage.js +29 -6
  298. package/dist/plugins/remote-env-adb.d.ts +6 -12
  299. package/dist/plugins/remote-env-adb.js +12 -2
  300. package/dist/plugins/remote-env-k8s.d.ts +8 -12
  301. package/dist/plugins/remote-env-k8s.js +23 -5
  302. package/dist/plugins/remote-env-local-docker.d.ts +10 -15
  303. package/dist/plugins/remote-env-local-docker.js +12 -9
  304. package/dist/plugins/remote-env-ssh.d.ts +10 -16
  305. package/dist/plugins/remote-env-ssh.js +17 -11
  306. package/dist/plugins/run-store-sql.d.ts +16 -14
  307. package/dist/plugins/run-store-sql.js +22 -2
  308. package/dist/plugins/schema-indexes.js +2 -0
  309. package/dist/plugins/send-user-file.js +2 -2
  310. package/dist/plugins/session-epoch-anchor.d.ts +32 -6
  311. package/dist/plugins/session-epoch-anchor.js +2 -3
  312. package/dist/plugins/session-store.d.ts +18 -8
  313. package/dist/plugins/session-store.js +15 -4
  314. package/dist/plugins/sql-driver.d.ts +74 -11
  315. package/dist/plugins/sql-driver.js +210 -12
  316. package/dist/plugins/sql-errors.d.ts +23 -0
  317. package/dist/plugins/sql-errors.js +21 -0
  318. package/dist/plugins/store-backend.d.ts +36 -1
  319. package/dist/plugins/store-backend.js +40 -14
  320. package/dist/plugins/store-contracts.d.ts +42 -0
  321. package/dist/plugins/store-contracts.js +1 -0
  322. package/dist/plugins/tidb-pool.d.ts +6 -1
  323. package/dist/plugins/tidb-pool.js +44 -10
  324. package/dist/plugins/tidb-session-storage.d.ts +3 -2
  325. package/dist/plugins/tidb-session-storage.js +2 -2
  326. package/dist/plugins/tidb-session-store.d.ts +24 -4
  327. package/dist/plugins/tidb-session-store.js +28 -4
  328. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  329. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  330. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  331. package/dist/plugins/workflow-run-store-sql.js +50 -9
  332. package/dist/posture-source.d.ts +18 -10
  333. package/dist/posture-source.js +4 -1
  334. package/dist/project-identity.d.ts +45 -6
  335. package/dist/project-identity.js +162 -8
  336. package/dist/project-layer-key.d.ts +28 -0
  337. package/dist/project-layer-key.js +29 -0
  338. package/dist/question.js +3 -2
  339. package/dist/read-deny-entry.d.ts +25 -0
  340. package/dist/read-deny-entry.js +12 -0
  341. package/dist/read-face-posture.d.ts +37 -11
  342. package/dist/read-face-posture.js +32 -3
  343. package/dist/request-key-closure.d.ts +7 -0
  344. package/dist/request-key-closure.js +9 -1
  345. package/dist/request-value-closure.d.ts +5 -0
  346. package/dist/request-value-closure.js +11 -3
  347. package/dist/rule-entry-list.d.ts +26 -2
  348. package/dist/rule-entry-list.js +72 -12
  349. package/dist/rules-consent.js +3 -4
  350. package/dist/run-acceptance.d.ts +19 -3
  351. package/dist/run-acceptance.js +13 -1
  352. package/dist/run-local.js +36 -6
  353. package/dist/runs.d.ts +10 -9
  354. package/dist/runs.js +21 -3
  355. package/dist/runtime-caps-resolver.d.ts +16 -6
  356. package/dist/runtime-caps-resolver.js +18 -5
  357. package/dist/sandbox-pkg-source.d.ts +19 -1
  358. package/dist/sandbox-pkg-source.js +29 -7
  359. package/dist/security.d.ts +87 -9
  360. package/dist/security.js +52 -9
  361. package/dist/session-error-wire.d.ts +19 -2
  362. package/dist/session-error-wire.js +5 -1
  363. package/dist/session-titled-frame.d.ts +33 -0
  364. package/dist/session-titled-frame.js +4 -0
  365. package/dist/session-titler.d.ts +9 -1
  366. package/dist/session-titler.js +15 -4
  367. package/dist/spec-fields.js +21 -2
  368. package/dist/sql-engine-posture.d.ts +3 -1
  369. package/dist/task-cwd.d.ts +1 -1
  370. package/dist/task-mcp.d.ts +3 -3
  371. package/dist/task-mcp.js +7 -66
  372. package/dist/task-settings.d.ts +89 -88
  373. package/dist/task-settings.js +71 -63
  374. package/dist/text-bidi.d.ts +0 -4
  375. package/dist/text-bidi.js +1 -3
  376. package/dist/tighten-only-seats.d.ts +150 -0
  377. package/dist/tighten-only-seats.js +196 -0
  378. package/dist/tool-approval.d.ts +303 -87
  379. package/dist/tool-approval.js +623 -239
  380. package/dist/trace/core-keyset-guard.d.ts +104 -22
  381. package/dist/trace/exit-seat.d.ts +28 -0
  382. package/dist/trace/exit-seat.js +76 -0
  383. package/dist/trace/ledger-events.d.ts +3 -2
  384. package/dist/trace/project.d.ts +80 -12
  385. package/dist/trace/project.js +41 -28
  386. package/dist/trace/projection-table.d.ts +100 -0
  387. package/dist/trace/projection-table.js +130 -0
  388. package/dist/trace/redact.d.ts +54 -0
  389. package/dist/trace/redact.js +778 -152
  390. package/dist/trace/self-minted-wire.d.ts +345 -1
  391. package/dist/trace/self-minted-wire.js +117 -1
  392. package/dist/trace/wire-projection-faces.d.ts +298 -2
  393. package/dist/trace/wire-projection-faces.js +154 -13
  394. package/dist/url-refusal.js +2 -0
  395. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  396. package/dist/web-admin-client/apply-effective.js +56 -5
  397. package/dist/web-admin-client/facade.d.ts +1 -1
  398. package/dist/web-admin-client/facade.js +1 -1
  399. package/dist/web-admin-client/read-face.js +5 -9
  400. package/dist/web-admin-client/skills-mcp.js +4 -5
  401. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
  402. package/package.json +7 -3
@@ -1,35 +1,6 @@
1
- /**
2
- * **一条 durable run 的「建行 + 驱动」单一属主**(S-218 提取;此前整段住在 `routes/runs.ts` 的
3
- * `createDurableRun` 里,只服务 HTTP/A2A 两条提交门)。
4
- *
5
- * 🔴 为什么提成模块:`/decide` 的 workflow 第三条车道在**宿主不在 park 上**时,同样要在宿主会话上起
6
- * 一跑新 turn 把决定交给宿主的模型(S-218)。这条链上挂着 `createRun` 的会话 claim(以及它唯一诚实的
7
- * 409 出路)、checkpoint ctx 落库、fleet 行发布、resume/rewind 两族锚点捕获、审批 ALS、通告投递……
8
- * 手抄第二份 = 把这十来件事复制成两份会漂的实现,而漂开的那天没有任何东西会说话(`createDurableRun`
9
- * 当年从路由体里提取出来时,头注写的就是同一条判词)。方向:`routes/runs.ts` 与 `http/server.ts`
10
- * 都 import 本模块,本模块谁都不 import —— 与 `http/run-meta.ts` 同一条「两个域共用一件 ⇒ 单独成文件」。
11
- *
12
- * 边界(本腿**不**含的东西,留给各自的门):同键重放的回执(`http/submit-replay.ts`)、`Idempotency-Key` 的在飞合流、
13
- * lease / 治理窗 pre-admission、属主门。它们是**提交面**的判据(读请求、写响应),而本腿只吃已经备好的
14
- * `PreparedTaskSubmission` 并交回一份收据 —— 这正是它能被一条没有 `ServerResponse` 的内部车道复用的原因。
15
- */
16
- import { type RunInternals } from "@sema-agent/core";
17
1
  import type { RunStore } from "../plugins/store-backend.js";
18
2
  import type { RunIdemBinding, RunRecord } from "../plugins/store-contracts.js";
19
- import type { PreparedTaskSubmission, RouteCtxBase } from "./route-ctx.js";
20
- /**
21
- * 🔴 **TRUSTED run internals** —— 由**部署方**(不是模型、不是请求体、不是工具参数)铸给这一跑的
22
- * core 内部席位。今天唯一成员 `workflowParkedResume` = 人对一条 parked workflow 子代的决定
23
- * (语义属主 = core `RunInternals.workflowParkedResume` 的 d.ts 顶注:「A host that decided a workflow
24
- * child's parked checkpoint launches the run that re-invokes `Workflow({resumeFromRunId})` with the
25
- * decision here」)。
26
- *
27
- * 为什么单独命名而不是直接摊 `RunInternals`:`RunInternals` 里绝大多数席位是**执行腿自己**铸的
28
- * (onForwardEvent / onTaskNotification / onActivity …),把整只类型开给调用方等于邀请第二个写者;
29
- * 本类型只开「部署方可以决定的那一格」,新增一格时**必须**在这里显式点名,编译期看得见。
30
- * 缺席 ⇒ 对两条腿都是完整 no-op。
31
- */
32
- export type TrustedRunInternals = Pick<RunInternals, "workflowParkedResume">;
3
+ import type { PreparedTaskSubmission, RouteCtxBase, WorkflowDecisionDelivery } from "./route-ctx.js";
33
4
  export interface DurableRunLegArgs {
34
5
  /** `prepareSpec` 的产物,或内部车道按同一条 `resolveSpec` 装配出来的等价形。 */
35
6
  prepared: PreparedTaskSubmission;
@@ -49,8 +20,9 @@ export interface DurableRunLegArgs {
49
20
  elicitOwner: string | null;
50
21
  /** run 行元数据的 `source` 列(调用方的系统身份;内部车道传自己的车道名)。 */
51
22
  source: string | null;
52
- /** 见 {@link TrustedRunInternals}。缺席 ⇒ 与本位存在之前逐字相同。 */
53
- trustedInternals?: TrustedRunInternals;
23
+ /** `/decide` 第三条车道的 fresh 投递腿交来的那一件决定投递(带着的决定 + 起跑之前落行的那一笔;语义与 TRUSTED 纪律的属主 =
24
+ * `route-ctx.ts` 的 {@link WorkflowDecisionDelivery} 顶注)。缺席 ⇒ 只摊本会话已受理的决定行。 */
25
+ workflowDecision?: WorkflowDecisionDelivery;
54
26
  }
55
27
  /**
56
28
  * 本腿的结局:一份收据,或「这次提交的一把幂等钥匙已经指向一条行」(店的原子赢或观察的**观察**臂 —— 本腿不碰
@@ -1,5 +1,6 @@
1
1
  import { uuidv7 } from "@sema-agent/core";
2
- import { runInBackground } from "../runs.js";
2
+ import { heldWorkflowParkDecisions } from "../parked-decide.js";
3
+ import { HEARTBEAT_MS, runInBackground } from "../runs.js";
3
4
  import { fleetRunPublisher, fleetRunLabels } from "../fleet/fleet-bus.js";
4
5
  import { cascadeConfig, runMeta } from "./run-meta.js";
5
6
  import { resolveStreamApprovalGate } from "../tool-approval.js";
@@ -9,12 +10,14 @@ import { commitSessionInheritEnv } from "./session-inherit-env.js";
9
10
  import { clearTurnActivity, readTurnActivityMs } from "../turn-activity.js";
10
11
  import { recordResourceWindow } from "../resource-window.js";
11
12
  import { persistSubmitCtx } from "./submit-ctx.js";
13
+ import { releaseClaimedRun } from "./claimed-run.js";
12
14
  import { serviceVersion } from "../service-version.js";
13
15
  export async function mintAndDriveDurableRun(ctx, runStore, args) {
14
16
  const { deps } = ctx;
15
17
  const { inflightRuns, preemptableRuns, steerableRuns } = ctx.registry;
16
18
  const sessionId = args.prepared.spec.sessionId ?? uuidv7();
17
19
  const taskId = args.clientTaskId ?? uuidv7();
20
+ const trustedInternals = await heldWorkflowParkDecisions(deps, sessionId, args.workflowDecision?.carried);
18
21
  const created = await runStore.createRun(taskId, sessionId, args.prepared.auth?.principal ?? null, deps.instanceId ?? "default", { ...runMeta(args.prepared, args.source), idem: args.idem ?? null });
19
22
  if (created.ok)
20
23
  clearTurnActivity(taskId);
@@ -25,13 +28,34 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
25
28
  if (!created.ok) {
26
29
  return { status: 409, body: await buildActiveRunConflict({ runStore, checkpointStore: deps.checkpointStore, governance: deps.config, runStaleSec: deps.config.runStaleSec, turnActivity: readTurnActivityMs }, sessionId, created.activeTaskId) };
27
30
  }
28
- const placementReject = await commitPlacement(deps, runStore, taskId, args.prepared.bindingIntent);
29
- if (placementReject)
30
- return { status: placementReject.status, body: devicePlacementErrorBody(placementReject) };
31
- if (deps.checkpointStore) {
32
- const ctxRefusal = await persistSubmitCtx(deps.checkpointStore, deps, { runStore, taskId }, sessionId, { body: args.prepared.body, memoryScope: args.prepared.auth?.memoryScope, writerVersion: serviceVersion() });
33
- if (ctxRefusal)
34
- return ctxRefusal;
31
+ const owner = args.prepared.auth?.principal ?? null;
32
+ const preLaunchHeartbeat = setInterval(() => {
33
+ void runStore.heartbeat(taskId, owner).catch(() => { deps.metrics?.inc("run_signal_poll_errors_total", { kind: "heartbeat" }); });
34
+ }, HEARTBEAT_MS);
35
+ preLaunchHeartbeat.unref?.();
36
+ try {
37
+ const placementReject = await commitPlacement(deps, runStore, taskId, args.prepared.bindingIntent);
38
+ if (placementReject)
39
+ return { status: placementReject.status, body: devicePlacementErrorBody(placementReject) };
40
+ if (deps.checkpointStore) {
41
+ const ctxRefusal = await persistSubmitCtx(deps.checkpointStore, deps, { runStore, taskId }, sessionId, { body: args.prepared.body, memoryScope: args.prepared.auth?.memoryScope, writerVersion: serviceVersion() });
42
+ if (ctxRefusal)
43
+ return ctxRefusal;
44
+ }
45
+ if (args.workflowDecision !== undefined) {
46
+ try {
47
+ await args.workflowDecision.commitBeforeLaunch(taskId);
48
+ }
49
+ catch (err) {
50
+ await releaseClaimedRun(runStore, taskId, `the workflow decision this run delivers could not be recorded: ${err instanceof Error ? err.message : String(err)}`, (releaseErr, step) => {
51
+ deps.logger?.warn?.("workflow_decision_run_release_failed", { taskId, sessionId, step, err: releaseErr instanceof Error ? releaseErr.message : String(releaseErr) });
52
+ });
53
+ throw err;
54
+ }
55
+ }
56
+ }
57
+ finally {
58
+ clearInterval(preLaunchHeartbeat);
35
59
  }
36
60
  commitSessionInheritEnv(deps, args.prepared.spec.sessionId, args.prepared.body);
37
61
  const anchorOwner = args.prepared.auth?.principal ?? null;
@@ -65,7 +89,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
65
89
  windowMs: deps.config.streamApproval?.windowMs,
66
90
  windowMarginMs: deps.config.streamAskWindowMarginMs,
67
91
  }
68
- : undefined, deps.checkpointStore, args.prepared.engineNotices, args.trustedInternals, deps.userInputLedger, args.prepared.acceptance);
69
- return { status: 202, body: { taskId, sessionId, status: "running" } };
92
+ : undefined, deps.checkpointStore, args.prepared.engineNotices, trustedInternals, deps.userInputLedger, args.prepared.acceptance);
93
+ return { status: 202, body: { taskId, sessionId, status: "running", ...(args.prepared.warnings !== undefined ? { warnings: args.prepared.warnings } : {}) } };
70
94
  }
71
95
  //# sourceMappingURL=durable-run-leg.js.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 🔴 **属主存取器**(S-487 RH-3 第一期)——「这个调用方读得到这一行 run / 会话吗」的行只经这里取得。
3
+ *
4
+ * ## 律(一条)
5
+ * 判官只有一只:run 行 = `principal-gate.ts` 的 `runOwnerReadable`(三条放行臂,含 [ref] C8 窄互认),会话行 = `sessionOwnerReadable`
6
+ * (dev-open 不钉人;无主行经 `security.ts` 的 `ownerlessRowOpen`;有主行就是 run 那一只)。本模块**不判第二遍、不另写一条**:它只做三件事 ——
7
+ * · 取行(或收下调用点已读到的行)并把行上的属主铸成 `DurableOwnerKey`(品牌铸点之一,见 `security.ts`);
8
+ * · 过判官,读不到 ⇒ `undefined` —— 与「没有这一行」**同一个值**,路由手里拿不到它无权看的行,也就没有第二种拒绝形可漏存在性;
9
+ * · operator 臂由调用点以闭集 {@link OwnerArm} 声明,这里统一并 `explicitOperatorOk`(此前各口手抄 `explicitOperatorOk(...) || owner === principal`)。
10
+ *
11
+ * ## 射程(第一期)
12
+ * 迁进来的口:leader 登记表的读与幂等重放、A2A `tasks/get`、preempt、审批豁免面;`request-guards.ts` 的 `runOwnerOk` 与提交撞键的回执函数
13
+ * (`submit-replay.ts`)经 {@link runRowReadable} 读同一只判官。其余按 run / 会话寻址的口(`runOwnerOk` 16 处、`sessionOwnerOk` 10 处)第二期整体换成
14
+ * {@link readOwnedRun} / {@link readOwnedSession} 并删两只 helper;裸 store 读口按文件计数钉在 `test/owner-gate-single-owner.test.ts`(增即红)。
15
+ */
16
+ import type { ServiceConfig } from "../config-types.js";
17
+ import type { RunRecord } from "../plugins/store-contracts.js";
18
+ import { type OwnerAwareSessionStore, type Principal, type SessionCaller, type SessionOwnerCell } from "../security.js";
19
+ import { type IdentityWiring, type OperatorRoster } from "./principal-gate.js";
20
+ /** 调用点挂哪条臂(闭集):`owner` = 只有属主判官;`owner_or_operator` = 属主判官 ∨ 显式运维(`explicitOperatorOk`,唯一读法)。 */
21
+ export type OwnerArm = "owner" | "owner_or_operator";
22
+ /** 存取器读的配置格:判官要 `requirePrincipal`(C8 臂的单用户条件),operator 臂要运维名册。 */
23
+ export type OwnerAccessConfig = Pick<ServiceConfig, "requirePrincipal"> & OperatorRoster;
24
+ /**
25
+ * 一行 run 行的属主过判官。`owner`:串 = 有主,`null` = 无主(开放);**`undefined` = 属主格坏了**(型上是 `string | null`,盘上的 run.json 少了这个键时
26
+ * 运行期会读成缺席 —— 文件店的形判据不验它)⇒ 谁都读不到(fail-closed,不猜成无主;operator 臂照常并)。leader 登记行的 `owner` 是可选键、缺席 = 无主,
27
+ * 那是 leader 记录型自己的语义,由 {@link createLeaderRunVisibility} 在 leader 边界上显式折成 `null`,不在这里混读。
28
+ */
29
+ export declare function runRowReadable(config: OwnerAccessConfig, principal: Principal | undefined, owner: string | null | undefined, arm: OwnerArm): boolean;
30
+ /**
31
+ * 按**会话**寻址的面上读一行 run 行(会话面板的上一腿 MCP 投影:读的是这只会话最新那条 run 的账本行):判官 = 会话面那一只(`sessionOwnerReadable`:
32
+ * dev-open 不钉人,与会话列表同读);run 行不是委派子代的 placed 会话行 ⇒ 无主时 `placed: false`(与 run 面对无主 run 的读法同)。
33
+ */
34
+ export declare function runRowReadableOnSessionFace(w: IdentityWiring & {
35
+ readonly config: OwnerAccessConfig;
36
+ }, caller: SessionCaller, owner: string | null, arm: OwnerArm): boolean;
37
+ /** run 行读口:行 | `undefined`(没有这一行 ≡ 读不到,同一个值)。 */
38
+ export declare function readOwnedRun(config: OwnerAccessConfig, runStore: {
39
+ getRun(taskId: string): Promise<RunRecord | undefined>;
40
+ }, principal: Principal | undefined, taskId: string, arm: OwnerArm): Promise<RunRecord | undefined>;
41
+ /**
42
+ * 会话行读口:属主一读 + 无主时 placement 一格(`sessionOwnerCellOf`,S-1770)+ 会话面判官。`undefined` = 没有这只会话(`ownerOf` 答 `undefined`)≡ 读不到。
43
+ * 读回的那一格交给调用点(要按属主作答的口用它)。店的读失败照常抛(500,绝不当成「读不到」或「读得到」)。
44
+ */
45
+ export declare function readOwnedSession(w: IdentityWiring & {
46
+ readonly config: OwnerAccessConfig;
47
+ }, store: Pick<OwnerAwareSessionStore, "placementOf"> & {
48
+ ownerOf(sessionId: string): Promise<string | null | undefined>;
49
+ }, caller: SessionCaller, sessionId: string, arm: OwnerArm): Promise<SessionOwnerCell | undefined>;
50
+ /**
51
+ * leader 登记表的判官形(boot 装配一次,`createLeaderEndpoint` 的必填席)—— leader 模块不再自判属主(修前它手抄 `owner == null || owner === requester`,
52
+ * 缺 C8 窄互认臂而注称 mirrors runOwnerOk)。leader 腿的调用方身份经幂等身份结构(`IdemCaller.principal`,即 `gatedPrincipal` 的回值)转手而来,
53
+ * 这里是 `Principal` 的「上游已验」适配铸点(第二期随那只结构带品牌而删)。`config` 是活的配置对象(每次调用现读)。
54
+ */
55
+ export declare function createLeaderRunVisibility(config: OwnerAccessConfig): (principal: string | null | undefined, owner: string | null | undefined) => boolean;
56
+ //# sourceMappingURL=owner-access.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { mintDurableOwnerKey, mintPrincipal, sessionOwnerCellOf } from "../security.js";
2
+ import { explicitOperatorOk, runOwnerReadable, sessionOwnerReadable } from "./principal-gate.js";
3
+ function operatorArmHolds(config, principal, arm) {
4
+ switch (arm) {
5
+ case "owner":
6
+ return false;
7
+ case "owner_or_operator":
8
+ return explicitOperatorOk(principal, config);
9
+ default: {
10
+ const unknownArm = arm;
11
+ throw new Error(`owner access: unknown arm ${String(unknownArm)}`);
12
+ }
13
+ }
14
+ }
15
+ export function runRowReadable(config, principal, owner, arm) {
16
+ if (operatorArmHolds(config, principal, arm))
17
+ return true;
18
+ if (owner === undefined)
19
+ return false;
20
+ return runOwnerReadable(principal, owner === null ? null : mintDurableOwnerKey(owner), config);
21
+ }
22
+ export function runRowReadableOnSessionFace(w, caller, owner, arm) {
23
+ if (operatorArmHolds(w.config, caller.principal, arm))
24
+ return true;
25
+ const cell = owner === null ? { owner: null, placed: false } : { owner: mintDurableOwnerKey(owner) };
26
+ return sessionOwnerReadable(caller, cell, w);
27
+ }
28
+ export async function readOwnedRun(config, runStore, principal, taskId, arm) {
29
+ const row = await runStore.getRun(taskId);
30
+ return row !== undefined && runRowReadable(config, principal, row.owner, arm) ? row : undefined;
31
+ }
32
+ export async function readOwnedSession(w, store, caller, sessionId, arm) {
33
+ const owner = await store.ownerOf(sessionId);
34
+ if (owner === undefined)
35
+ return undefined;
36
+ const cell = await sessionOwnerCellOf(store, sessionId, owner);
37
+ return operatorArmHolds(w.config, caller.principal, arm) || sessionOwnerReadable(caller, cell, w) ? cell : undefined;
38
+ }
39
+ export function createLeaderRunVisibility(config) {
40
+ return (principal, owner) => runRowReadable(config, principal == null ? undefined : mintPrincipal(principal), owner ?? null, "owner");
41
+ }
42
+ //# sourceMappingURL=owner-access.js.map
@@ -1,5 +1,6 @@
1
1
  import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import type { ServiceConfig } from "../config-types.js";
3
+ import { type DurableOwnerKey, type Principal, type SessionCaller, type SessionOwnerCell } from "../security.js";
3
4
  /** Coerce a possibly-repeated request header to a single string (or undefined). */
4
5
  export declare function headerStr(v: string | string[] | undefined): string | undefined;
5
6
  export declare function safeEqual(a: string, b: string): boolean;
@@ -27,7 +28,7 @@ export type IdentitySource = (typeof IDENTITY_SOURCES)[number];
27
28
  * 有可验身份时才是身份,零凭据下它只是谁都能写的字符串;而 JWT 身份不依赖静态 token 的在场。
28
29
  */
29
30
  export declare function verifiedIdentitySource(req: IncomingMessage, config: ServiceConfig): IdentitySource | undefined;
30
- export declare function gatedPrincipal(req: IncomingMessage, config: ServiceConfig): string | undefined;
31
+ export declare function gatedPrincipal(req: IncomingMessage, config: ServiceConfig): Principal | undefined;
31
32
  /**
32
33
  * 「**出示了凭证但验不过** ≠ **没出示**」在 **direct-door principal JWT** 面上的形 —— 本仓
33
34
  * {@link bearerPresentedButUnverified} 早就是同一条律在 Bearer 面的形,这里只是把它推到另一面。
@@ -91,7 +92,7 @@ export declare function sendUnauthorized(res: ServerResponse, face: Unauthorized
91
92
  * 放宽它是一次独立裁定。需要 operator 臂的读面在**自己的调用点**显式并上 {@link explicitOperatorOk} ——
92
93
  * 那样「谁多给了 operator 一条路」在代码上看得见,而不是被偷偷塞进共享谓词里。
93
94
  */
94
- export declare function runOwnerReadable(principal: string | undefined, owner: string | null, config: ServiceConfig): boolean;
95
+ export declare function runOwnerReadable(principal: Principal | undefined, owner: DurableOwnerKey | null, config: Pick<ServiceConfig, "requirePrincipal">): boolean;
95
96
  /** 「这台部署配了任何一把 service 凭证吗」—— fail-closed 写门族的真值源,也是身份来源 `service_token` 接没接线的那一句。**单一属主**:`handle()` 的
96
97
  * 凭证门、[ref] 的 parked 赎回席、[ref] 的 consolidation 阀门启动期告警、以及下面 {@link wiredIdentitySources} 读同一句(手抄迟早只改一处,而这条判据
97
98
  * 决定的是「无凭证部署上写口开不开」)。住在本叶(与 {@link systemFor} 同处)而不是 `dispatch.ts`:身份原语只依赖本叶。 */
@@ -116,32 +117,56 @@ export declare function wiredIdentitySources(w: IdentityWiring): readonly Identi
116
117
  * 会话面的属主谓词({@link sessionOwnerReadable})、`/metrics` 族的免凭据臂(`dispatch.ts`)—— 于是「调用方是谁」在列表、单会话面与指标面上只有一只读法。
117
118
  */
118
119
  export declare function devOpenDeployment(w: IdentityWiring): boolean;
120
+ /** 会话行属主门的调用方的唯一铸点({@link SessionCaller}):验过的主体 + 是不是 fleet 调用方 —— {@link devOpenDeployment} 上的任何调用方,
121
+ * 或出示了这台部署的 service 凭据(`systemFor`)、不报主体的调用方(带主体头的是那位主体,不是 fleet;只读 trace token 不是 service 凭据)。 */
122
+ export declare function sessionCallerOf(req: IncomingMessage, w: IdentityWiring): SessionCaller;
119
123
  /**
120
124
  * **「这个调用方能不能动这只会话」的唯一谓词**(会话面:视图 / head / 事件流 / 记忆状态与自助抹除 / 改名置顶 / 后台任务 / MCP 重连 /
121
- * 面板的上一腿 MCP 投影)。= {@link devOpenDeployment} ∨ {@link runOwnerReadable}:没有身份边界的部署上不钉任何人(与会话列表同读,
122
- * 列表把这只会话列给谁,谁就读得到它);有身份边界的部署上就是 run 面那一条谓词,一字不变。
125
+ * 面板的上一腿 MCP 投影)。没有身份边界的部署({@link devOpenDeployment})上不钉任何人(与会话列表同读,列表把这只会话列给谁,谁就读得到它);
126
+ * 有身份边界的部署上:
127
+ * · **无主的行**经无主臂的唯一判据 `ownerlessRowOpen`(`security.ts`):普通会话照旧对任何调用方开放;委派子代的 placed 行只对 fleet 调用方
128
+ * ({@link sessionCallerOf})开放 —— 任何具名主体、没有 service 凭据的无身份调用方都与别人的行同体(调用点答 404)(S-1770)。
129
+ * 代价如实:属主还没定下的子代行连它将来的属主也暂时读不到(由一次性迁移 M23 / 那一行的下一次停驻定下);
130
+ * · 有主的行就是 run 面那一条谓词({@link runOwnerReadable}),一字不变。
123
131
  * run 面(poll / events / cancel …)不读本谓词 —— 那是 run 资源的门,另一只。
124
132
  */
125
- export declare function sessionOwnerReadable(principal: string | undefined, owner: string | null, w: IdentityWiring): boolean;
126
- /** The EXPLICIT operator gate (IMAGE-API-DESIGN.md §P2.4a MUST-FIX) — NOT the bare `isOperator` (whose empty-list
127
- * "true-for-all" is an unrestricted-write exec door the instant OPERATOR_PRINCIPALS is empty). Requires a non-empty
128
- * operator set AND a present principal IN it. The same form the preempt/resume endpoints already use.
129
- * 🔴 SINGLE mint point (E1): a byte-identical twin `explicitOperator` used to live below (image-visibility lane)
130
- * and two more hand-inlined copies sat in approvals-assistant — a future hardening of this judgment would have
131
- * missed them. All call sites now consume THIS function; do not re-introduce a sibling. */
132
- export declare function explicitOperatorOk(principal: string | undefined, operatorPrincipals: readonly string[]): boolean;
133
+ export declare function sessionOwnerReadable(caller: SessionCaller, cell: SessionOwnerCell, w: IdentityWiring): boolean;
134
+ /**
135
+ * 运维名册 —— 「谁是 operator」读的两格(都住在部署配置里):具名名单 `OPERATOR_PRINCIPALS` 与单用户形的显式声明 `OPERATOR_SINGLE_USER`。
136
+ * 两者互斥(声明只在单用户形、名单为空时成立;否则拒启 —— 一个事实一个来源,`config-invariants.ts` 的 `operator-single-user`)。
137
+ */
138
+ export type OperatorRoster = Pick<ServiceConfig, "operatorPrincipals" | "operatorSingleUser">;
139
+ /**
140
+ * 🔴 **「谁是 operator」的唯一读法**(S-1050,[ref] ① 裁 A;IMAGE-API-DESIGN.md §P2.4a MUST-FIX 的那一只门)。
141
+ * · 名单为空 ⇒ **没人是**(没有任何形把「空名单」读成「人人是」—— 那条读法 `isOperator` 已删);
142
+ * · 名单非空 ⇒ 验过的主体在名单里才是;
143
+ * · 单用户形显式声明 `OPERATOR_SINGLE_USER=true` ⇒ 这台部署的**每个调用方**都是(单用户形只有一位用户,能到这台部署的就是他;边界是部署自己的
144
+ * 凭据门 —— 没配凭据的部署上,能连上端口的都是)。声明只在单用户形成立(起服不变量)。
145
+ * `principal` 必须是验过的身份(`gatedPrincipal`),绝不是自述头。全部运维面(审批队列、steer.trusted、记忆运维口、bake 门……)读这一只;
146
+ * 🔴 SINGLE mint point (E1):不要再造兄弟。
147
+ */
148
+ export declare function explicitOperatorOk(principal: string | undefined, roster: OperatorRoster): boolean;
149
+ /** 「这台部署**有没有**运维」的唯一读法(名单非空 ∨ 单用户声明在场)—— 能力事实 `operatorLane`、整理阀门的可达性告警、起服提示同读这一只。 */
150
+ export declare function operatorRosterPresent(roster: OperatorRoster): boolean;
151
+ /**
152
+ * 起服提示(S-1050):单用户形(`REQUIRE_PRINCIPAL` 未开)+ 没有运维名册 + 这台部署有审批底座 ⇒ 一行 warn。这一形在「空名单 = 人人是」那条读法退役之后
153
+ * 丢的东西就是审批队列的运维视图(跨 scope 列表 / `?owner=` / 无主体调用方的整个队列),补回它只要一行配置 —— 不拒启:升级那一拍打掉一台今天跑得好好的
154
+ * 单用户机器比一条提示重。多租形不提示(它另有拒启不变量);没有审批底座的形什么都没少。
155
+ */
156
+ export declare function buildOperatorUndeclaredNotice(config: OperatorRoster & Pick<ServiceConfig, "requirePrincipal">, approvalFaceWired: boolean): {
157
+ event: "operator_undeclared_single_user";
158
+ fields: {
159
+ detail: string;
160
+ };
161
+ } | undefined;
133
162
  /** operator 面族在「名单为空」这一形上的**唯一**拒发口:事实 `operatorLane`(`capability-facts.ts`)为假 ⇒ 这一面对任何
134
163
  * 身份都打不通 = 这台部署上不可用 ⇒ 501,不是鉴权拒(403 只留给「名单非空而你不在名单上」)。能力位读的是同一只事实。 */
135
164
  export declare function sendOperatorPrincipalsRequired(res: ServerResponse): void;
136
- /** May `principal` act as an OPERATOR on the F4 approval queue? Empty `operatorPrincipals` = legacy
137
- * behavior (the shared service token IS the operator boundary → everyone authenticated is an operator).
138
- * Otherwise only listed principals are operators. */
139
- export declare function isOperator(principal: string | undefined, operatorPrincipals: readonly string[]): boolean;
140
- /** Operator check WITHOUT the empty-list "everyone is an operator" legacy fallback — an EMPTY `operatorPrincipals`
141
- * yields `false` (no one is an operator). Use this where empty-operators-means-all would be a SECURITY hole rather
142
- * than a back-compat convenience: image VISIBILITY scoping (a tenant-scoped image must not leak to every caller
143
- * just because OPERATOR_PRINCIPALS is unset). Adversarial-review HIGH: `isOperator([], p)=true` made
144
- * `latestPublished(..., {operator:true})` bypass tenant visibility for any caller on an operator-less deployment.
145
- * (The byte-identical `explicitOperator` twin that used to live here was folded into `explicitOperatorOk` above —
146
- * one judgment, one mint point.) */
165
+ /**
166
+ * 「这台部署没有运维名册」时**唯一**的出路句 —— 与 {@link operatorRosterPresent} 同一对旋钮,所以句子不比事实少说一条出路:
167
+ * 名单 `OPERATOR_PRINCIPALS`(具名运维;多租形只有这一条),或单用户形的显式声明 `OPERATOR_SINGLE_USER=true`(只在 `REQUIRE_PRINCIPAL` 未开且名单为空时成立,
168
+ * 否则起服拒 —— 句子把它限定在单用户部署上)。拒发口 {@link sendOperatorPrincipalsRequired}、能力事实闭集 `operator_principals_empty` 的义、记忆整理阀门的起服告警
169
+ * 都说这一句,别处不再各写一份。
170
+ */
171
+ export declare function buildOperatorRosterWayOut(): string;
147
172
  //# sourceMappingURL=principal-gate.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { timingSafeEqual } from "node:crypto";
2
- import { PRINCIPAL_TOKEN_HEADER, verifiedPrincipal, ssoVerifiedPrincipal } from "../security.js";
2
+ import { PRINCIPAL_TOKEN_HEADER, verifiedPrincipal, ssoVerifiedPrincipal, ownerlessRowOpen, mintPrincipal } from "../security.js";
3
3
  import { sendError } from "./send.js";
4
4
  export function headerStr(v) {
5
5
  return Array.isArray(v) ? v[0] : v;
@@ -50,7 +50,8 @@ export function verifiedIdentitySource(req, config) {
50
50
  export function gatedPrincipal(req, config) {
51
51
  if (bearerPresentedButUnverified(req, config))
52
52
  return undefined;
53
- return verifiedPrincipal(req, config);
53
+ const verified = verifiedPrincipal(req, config);
54
+ return verified === undefined ? undefined : mintPrincipal(verified);
54
55
  }
55
56
  export function principalTokenPresentedButUnverified(req, config) {
56
57
  if (!config.directDoorActive)
@@ -86,10 +87,13 @@ export function sendUnauthorized(res, face) {
86
87
  }
87
88
  }
88
89
  }
90
+ function sameIdentity(principal, owner) {
91
+ return principal === owner;
92
+ }
89
93
  export function runOwnerReadable(principal, owner, config) {
90
94
  if (owner === null)
91
95
  return true;
92
- if (principal === owner)
96
+ if (principal !== undefined && sameIdentity(principal, owner))
93
97
  return true;
94
98
  return !config.requirePrincipal && owner === "anon:shell-live" && principal === undefined;
95
99
  }
@@ -116,18 +120,41 @@ export function wiredIdentitySources(w) {
116
120
  export function devOpenDeployment(w) {
117
121
  return wiredIdentitySources(w).length === 0 && !w.config.traceToken && !w.config.requirePrincipal;
118
122
  }
119
- export function sessionOwnerReadable(principal, owner, w) {
120
- return devOpenDeployment(w) || runOwnerReadable(principal, owner, w.config);
123
+ export function sessionCallerOf(req, w) {
124
+ const principal = gatedPrincipal(req, w.config);
125
+ return { principal, fleet: devOpenDeployment(w) || (principal === undefined && systemFor(req, w.config) !== undefined) };
126
+ }
127
+ export function sessionOwnerReadable(caller, cell, w) {
128
+ if (devOpenDeployment(w))
129
+ return true;
130
+ if (cell.owner === null)
131
+ return ownerlessRowOpen(cell, caller);
132
+ return runOwnerReadable(caller.principal, cell.owner, w.config);
133
+ }
134
+ export function explicitOperatorOk(principal, roster) {
135
+ if (roster.operatorSingleUser === true)
136
+ return true;
137
+ return roster.operatorPrincipals.length > 0 && principal !== undefined && roster.operatorPrincipals.includes(principal);
138
+ }
139
+ export function operatorRosterPresent(roster) {
140
+ return roster.operatorSingleUser === true || roster.operatorPrincipals.length > 0;
121
141
  }
122
- export function explicitOperatorOk(principal, operatorPrincipals) {
123
- return operatorPrincipals.length > 0 && principal !== undefined && operatorPrincipals.includes(principal);
142
+ export function buildOperatorUndeclaredNotice(config, approvalFaceWired) {
143
+ if (config.requirePrincipal === true || operatorRosterPresent(config) || !approvalFaceWired)
144
+ return undefined;
145
+ return {
146
+ event: "operator_undeclared_single_user",
147
+ fields: {
148
+ detail: "this single-user deployment declares no operator: OPERATOR_PRINCIPALS is empty and OPERATOR_SINGLE_USER is not set, so no caller is an operator — " +
149
+ "GET /v1/approvals, its stream, /v1/assistant/inbox and /v1/assistant/tasks show each caller only its own pending approvals (a caller that sends no principal sees none) and ignore ?owner=. " +
150
+ "To act as this deployment's operator, add one line: OPERATOR_SINGLE_USER=true",
151
+ },
152
+ };
124
153
  }
125
154
  export function sendOperatorPrincipalsRequired(res) {
126
- sendError(res, 501, "capability.operator_principals_required", "this endpoint is operator-only and OPERATOR_PRINCIPALS is empty, so no caller can be an operator on this deployment; configure OPERATOR_PRINCIPALS to enable it");
155
+ sendError(res, 501, "capability.operator_principals_required", `this endpoint is operator-only and this deployment has no operator roster (OPERATOR_PRINCIPALS is empty and OPERATOR_SINGLE_USER is not declared), so no caller can be an operator; ${buildOperatorRosterWayOut()}`);
127
156
  }
128
- export function isOperator(principal, operatorPrincipals) {
129
- if (operatorPrincipals.length === 0)
130
- return true;
131
- return principal !== undefined && operatorPrincipals.includes(principal);
157
+ export function buildOperatorRosterWayOut() {
158
+ return "configure OPERATOR_PRINCIPALS with the named operator principals, or — on a single-user deployment (REQUIRE_PRINCIPAL off) — declare OPERATOR_SINGLE_USER=true";
132
159
  }
133
160
  //# sourceMappingURL=principal-gate.js.map
@@ -23,6 +23,7 @@
23
23
  */
24
24
  import type { ServerResponse } from "node:http";
25
25
  import type { ServingLeaseView } from "../fleet/engine-signal.js";
26
+ import { type RefusalBody } from "./refusal-body.js";
26
27
  /**
27
28
  * 就绪原因闭集,**数组序 = 优先序**:`/health.readyReason` 取首条的句、计费门取首条的拒绝码。
28
29
  * draining 在前 —— 一台正在下线的副本「去找替补」(Retry-After 15)比「稍后重试本机」(5)更对。
@@ -86,11 +87,8 @@ export declare function admissionRefusalOf(verdict: ReadinessVerdict, lease: Ser
86
87
  * `{status, body}` 的赎回席只取体(它的路由刻意不带头,见 `approval-sweeps.ts` 该臂注)。 */
87
88
  export interface BillableRefusal {
88
89
  readonly status: 503;
89
- readonly body: {
90
- readonly error: string;
91
- readonly errorCode: string;
92
- readonly message: string;
93
- };
90
+ /** `error` 是冻结的词(`draining` / `model_roster_pending` / `lease_expired`),`message` 是给人读的句子(机读事实里的一键)。 */
91
+ readonly body: RefusalBody;
94
92
  readonly retryAfterSec: 15 | 5 | 60;
95
93
  }
96
94
  /**
@@ -100,6 +98,14 @@ export interface BillableRefusal {
100
98
  * 停机因由声明口一起退役(排空口的体改成契约 `DrainRequest`,没有因由字段);`lease_expired` 一臂新增。
101
99
  */
102
100
  export declare function billableRefusalOf(reason: AdmissionRefusal): BillableRefusal;
101
+ /**
102
+ * 订阅面终帧要的 `{ errorCode, message }` —— 与提交面 503 体**同一只铸点**的产物:`errorCode` 是体的码,`message` 是体里 `message` 键的那句话(`error` 是冻结的词,
103
+ * 句子在 `message`)。从体里读,不另抄一份;体里没有那句话是铸点坏了 ⇒ 响亮抛(不静默发一帧没有句子的终帧)。
104
+ */
105
+ export declare function billableRefusalFrame(refusal: BillableRefusal): {
106
+ readonly errorCode: string;
107
+ readonly message: string;
108
+ };
103
109
  /** 发送形:`Retry-After` + 体(`dispatch.ts` 的 billable 门与 a2a `message/send` 同调)。走 `sendError` —— errorCode
104
110
  * 是它的必填位置参(对抗复审 #3:修前这里是 `sendJson(res, r.status, r.body)`,与 leader 口的豁免行逐字同文,
105
111
  * 在 error-code-key-gate 上借了别人的豁免过门);体键序与修前逐字同(`error, errorCode, message, reason?`)。 */
@@ -1,4 +1,5 @@
1
1
  import { sendJson, sendError } from "./send.js";
2
+ import { refusalBody } from "./refusal-body.js";
2
3
  export const READINESS_REASONS = ["draining", "model_roster_pending"];
3
4
  export function readinessOf(deps) {
4
5
  const [first, ...rest] = READINESS_REASONS.filter((r) => holds(r, deps));
@@ -60,17 +61,23 @@ export function admissionRefusalOf(verdict, lease, kind) {
60
61
  export function billableRefusalOf(reason) {
61
62
  switch (reason) {
62
63
  case "draining":
63
- return { status: 503, body: { error: "draining", errorCode: "draining", message: "this instance is draining for shutdown/upgrade — retry against the replacement instance" }, retryAfterSec: 15 };
64
+ return { status: 503, body: refusalBody({ errorCode: "draining", message: "draining", facts: { message: "this instance is draining for shutdown/upgrade — retry against the replacement instance" } }), retryAfterSec: 15 };
64
65
  case "model_roster_pending":
65
- return { status: 503, body: { error: "model_roster_pending", errorCode: "state.model_roster_pending", message: "this worker has no model yet (waiting for the first effective-config pull to land the roster) — retry shortly" }, retryAfterSec: 5 };
66
+ return { status: 503, body: refusalBody({ errorCode: "state.model_roster_pending", message: "model_roster_pending", facts: { message: "this worker has no model yet (waiting for the first effective-config pull to land the roster) — retry shortly" } }), retryAfterSec: 5 };
66
67
  case "lease_expired":
67
- return { status: 503, body: { error: "lease_expired", errorCode: "state.lease_expired", message: "this instance has not renewed its serving lease with the fleet center for longer than its outage limit — it takes no new work; cancel, approvals, resume and reads stay open here — retry new work against another instance" }, retryAfterSec: 60 };
68
+ return { status: 503, body: refusalBody({ errorCode: "state.lease_expired", message: "lease_expired", facts: { message: "this instance has not renewed its serving lease with the fleet center for longer than its outage limit — it takes no new work; cancel, approvals, resume and reads stay open here — retry new work against another instance" } }), retryAfterSec: 60 };
68
69
  default: {
69
70
  const unhandled = reason;
70
71
  throw new Error(`admission refusal without a billable refusal: ${String(unhandled)}`);
71
72
  }
72
73
  }
73
74
  }
75
+ export function billableRefusalFrame(refusal) {
76
+ const message = refusal.body["message"];
77
+ if (typeof message !== "string")
78
+ throw new Error(`billable refusal body without its message sentence: ${refusal.body.errorCode}`);
79
+ return { errorCode: refusal.body.errorCode, message };
80
+ }
74
81
  export function sendBillableRefusal(res, refusal) {
75
82
  const { error, errorCode, ...extra } = refusal.body;
76
83
  res.setHeader("retry-after", String(refusal.retryAfterSec));
@@ -4,6 +4,8 @@
4
4
  * ctx 字段 1 只(`deps`):本族对外围的耦合就这一格,它也是这一族的可测性读数。
5
5
  */
6
6
  import type { IncomingMessage, ServerResponse } from "node:http";
7
+ import { type SessionOwnerCell } from "../security.js";
8
+ import { type RefusalBody } from "./refusal-body.js";
7
9
  import type { FlatServiceDeps } from "./server.js";
8
10
  import type { LeaseRefusalAdmission } from "../fleet-lease.js";
9
11
  import { type QuotaDecision } from "../observability/cost-quota.js";
@@ -18,6 +20,13 @@ import { type SqlWallSource, type SqlWallsReading } from "../plugins/sql-driver.
18
20
  * 两腿的折算与 SQL 快照 blob 的缺省帽读同一份(`plugins/sql-driver.ts`,余量各只在那里定义一次)。两腿都拒时报行墙那条(行墙句逐字沿用)。
19
21
  * 入参是**已判好的墙**(`sql-driver.ts` `sqlWallsOf` —— 事实缺席时的落点只在那里定义,与 blob 帽同一只判官、同一个方向);本函数不读事实、不判缺席,
20
22
  * 墙从哪来(`sources` 里**被拒的那一堵**的出处)原样带进判决(`wallSource`),拒句按它分两形:库报的墙点名库的上限与旋钮,假定的墙(事实还没建立,或事实在场但这一堵读失败、退了文档缺省)只说「库的这个上限没读到、按最保守的墙拒」。
23
+ *
24
+ * 🔴 **包墙腿是同一只规则的提前一跑,不是第二套规则**(S-1746 主会话裁定):包墙是物理连接的事实(会话值,连接建立时从全局拷一份、此后不变),
25
+ * 终判在将要执行写入的那条连接上 —— 那是库自己(续跑输入那一笔被拒 ⇒ 店的带码拒 ⇒ 同一个 413;会话日志那一笔走它既有的失败面);SQL 快照 blob 则由
26
+ * `blob-backend.ts` 经 `connectJudged` 在写入连接上判。受理时手里没有那条连接,判官 `sqlWallsOf` 读的 `facts` 是池里**最近一次初始化**的那条连接的读数
27
+ * (`SqlDriver.facts`:池见过的最新读数,只在有连接初始化时才更新),同一个折算 `packetWallStatementBytes`:拒 ⇒ 413 在任何副作用之前;收 ⇒ 写入那条连接上的判决仍是终判。
28
+ * 这个读数是**缓存**,DBA 调了全局值之后、下一条连接初始化之前,它与库此刻的值两个方向都可能不一致:调大 ⇒ 受理口仍按旧的小值拒(新墙本装得下的体也 413,
29
+ * 直到有连接按新值初始化 —— 快照 blob 的旧连接自换就会带来一条);调小 ⇒ 受理口按旧的大值放行,写入那条连接上的库再拒(续跑输入那一笔 = 同一个 413)。
21
30
  * 两堵墙都不存在(PG / 没有 SQL 后端)⇒ `null`。一堵墙「够不着」(行墙门限 ≥ 请求体上限;包墙门限 ≥ 请求体上限的 2×,即转义上界也到不了)时那一腿不量,
22
31
  * 两腿都够不着就连 `JSON.stringify` 都不调 —— 默认的 PG / MySQL 8(包墙 64 MiB)/ 无 SQL 后端部署零额外开销。
23
32
  * 受理口(`admission.ts` `prepareSpec`)按它答 413 `request.payload_too_large`,句子按 `by` × `wallSource` 点名真正拒绝的那堵墙;两位都是闭集,加一臂 = 编译红。
@@ -36,16 +45,11 @@ export interface SubmitBodyRefusal {
36
45
  export declare function refuseSubmitBody(walls: SqlWallsReading, body: unknown): SubmitBodyRefusal | null;
37
46
  /**
38
47
  * S-866 ③ —— 租约拒绝的应答形({@link leaseRefusalOf} 的产物;`{status, body}` 交回形 + 镜像头用的 `retryAfterSec`)。
39
- * 体键序固定 `error, errorCode, …`(冻结门 `FROZEN_RESULT_BODY` 按这个字面形锚)。
48
+ * 体经唯一组句器铸(键序 `error, errorCode, …机读位`;冻结门 `FROZEN_RESULT_BODY` 读 `refusalBody({ errorCode, message, … })` 这一形)。
40
49
  */
41
50
  export interface LeaseRefusal {
42
51
  readonly status: 429 | 503;
43
- readonly body: {
44
- readonly error: string;
45
- readonly errorCode: string;
46
- readonly retryAfterSec: number;
47
- readonly [extra: string]: unknown;
48
- };
52
+ readonly body: RefusalBody;
49
53
  readonly retryAfterSec: number;
50
54
  }
51
55
  /**
@@ -60,20 +64,13 @@ export interface LeaseRefusal {
60
64
  export declare function leaseRefusalOf(adm: LeaseRefusalAdmission): LeaseRefusal;
61
65
  /**
62
66
  * 成本配额越顶 → 429 `limit.cost_quota_exceeded` 的**唯一**体铸点(三只门同调:路由门 `quotaExceeded`、属主门
63
- * `ownerCostQuotaDenied`、赎回席 `approval-sweeps.ts`;修前三份手抄)。体键序 `error, errorCode, …`(冻结门
64
- * `FROZEN_RESULT_BODY` 按这个字面形锚)。`retryAfterSec` 与 `windowResetsAt` 同源于决策的 `windowEndsAtMs`
67
+ * `ownerCostQuotaDenied`、赎回席 `approval-sweeps.ts`;修前三份手抄)。体经唯一组句器铸(键序 `error, errorCode, …机读位`;冻结门
68
+ * `FROZEN_RESULT_BODY` 读 `refusalBody({ errorCode, message, … })` 这一形)。`retryAfterSec` 与 `windowResetsAt` 同源于决策的 `windowEndsAtMs`
65
69
  * (同一次读钟),`Retry-After` 头由各发送点从体里的 `retryAfterSec` 镜像。
66
70
  */
67
71
  export declare function costQuotaRefusalOf(d: QuotaDecision): {
68
72
  status: 429;
69
- body: {
70
- error: string;
71
- errorCode: string;
72
- usedMicroUsd: number;
73
- limitMicroUsd: number;
74
- retryAfterSec: number;
75
- windowResetsAt: string;
76
- };
73
+ body: RefusalBody;
77
74
  };
78
75
  export declare function createRequestGuards(ctx: {
79
76
  deps: FlatServiceDeps;
@@ -92,11 +89,7 @@ export declare function createRequestGuards(ctx: {
92
89
  leaseDenied: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
93
90
  usageWindowDenialFor: (key: string) => Promise<{
94
91
  status: number;
95
- body: {
96
- error: string;
97
- errorCode: string;
98
- retryAfterSec: number;
99
- };
92
+ body: RefusalBody;
100
93
  retryAfterSec: number;
101
94
  } | undefined>;
102
95
  usageWindowDenied: (req: IncomingMessage, res: ServerResponse) => Promise<boolean>;
@@ -112,7 +105,7 @@ export declare function createRequestGuards(ctx: {
112
105
  gateOwner: string | null;
113
106
  };
114
107
  runOwnerOk: (req: IncomingMessage, res: ServerResponse, owner: string | null) => boolean;
115
- sessionOwnerOk: (req: IncomingMessage, res: ServerResponse, owner: string | null) => boolean;
108
+ sessionOwnerOk: (req: IncomingMessage, res: ServerResponse, cell: SessionOwnerCell) => boolean;
116
109
  runSessionAcceptOk: (req: IncomingMessage, res: ServerResponse, run: {
117
110
  sessionId: string | null;
118
111
  }, face: string, notFoundError?: string) => boolean;