@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
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 请求面**来源**闭集 —— 一个 hook 组(`settings.hooks[event][i].source`)是谁写的。词表与「托管与否」的映射**只在这里**;
3
+ * hooks 执行器按它分车道(`hook-runner.ts` 的三相),将来请求面别处的来源键(MCP 服务器条目)读同一张词表,不另立第二份。
4
+ *
5
+ * 词表(闭集;集外词 = 形错,受理面整份拒,fail-closed;缺席 = 非托管):
6
+ * · `managedPolicy` —— 组织策略(托管设置)。**托管**,且是终判相。
7
+ * · `managedPlugin:<name>` —— 托管设置启用的插件(壳在「只许托管 hook」治理下仍保留它们)。**托管**。
8
+ * · `user` / `project` / `local` / `flag` —— 设置文件的各层与启动时给的 `--settings`。非托管。
9
+ * · `plugin:<name>` —— 用户启用的插件。非托管(来源不可信)。
10
+ * `<name>` 非空、至多 {@link MAX_HOOK_SOURCE_NAME_CHARS} 字;**名只留痕,不作判据**(相只看前缀)。
11
+ */
12
+ /** 不带名的来源词 → 它跑在哪一相。`satisfies Record<…>`:加一个词不写相 = 编译红。 */
13
+ declare const HOOK_SOURCE_WORD_PHASE: {
14
+ readonly managedPolicy: "managedPolicy";
15
+ readonly user: "unmanaged";
16
+ readonly project: "unmanaged";
17
+ readonly local: "unmanaged";
18
+ readonly flag: "unmanaged";
19
+ };
20
+ /** 带名的来源前缀(`<prefix>:<name>`)→ 它跑在哪一相。 */
21
+ declare const HOOK_SOURCE_PREFIX_PHASE: {
22
+ readonly managedPlugin: "managedPlugin";
23
+ readonly plugin: "unmanaged";
24
+ };
25
+ type HookSourceWord = keyof typeof HOOK_SOURCE_WORD_PHASE;
26
+ type HookSourcePrefix = keyof typeof HOOK_SOURCE_PREFIX_PHASE;
27
+ /** 一个 hook 组的来源(闭集;见文件头)。 */
28
+ export type HookGroupSource = HookSourceWord | `${HookSourcePrefix}:${string}`;
29
+ /**
30
+ * 执行相 —— **固定次序**就是这张数组的次序:非托管 → 托管插件 → 托管策略(终判)。后一相在前一相的最终输入上跑;结果按信任序(倒序)读。
31
+ * 托管 = 后两相;非托管与托管各一只事件墙钟(见 `hook-runner.ts`)。
32
+ */
33
+ export declare const HOOK_PHASES: readonly ["unmanaged", "managedPlugin", "managedPolicy"];
34
+ export type HookPhase = (typeof HOOK_PHASES)[number];
35
+ /** 来源的名的长度上限(`managedPlugin:` / `plugin:` 之后那一段)。 */
36
+ export declare const MAX_HOOK_SOURCE_NAME_CHARS = 128;
37
+ /** 受理面的读法:合形 ⇒ 来源;否则 `undefined`(调用方铸拒)。只认 own 词(`Object.hasOwn`:原型上的名字不是词表成员)。 */
38
+ export declare function readHookGroupSource(raw: unknown): HookGroupSource | undefined;
39
+ /** 来源 → 相。缺席 ⇒ 非托管(老包不带 `source`:行为与此前逐字同)。 */
40
+ export declare function hookPhaseOf(source: HookGroupSource | undefined): HookPhase;
41
+ /** 拒句里的词表(由两张表派生,不另写一份)。 */
42
+ export declare const HOOK_SOURCE_VOCABULARY_SENTENCE: string;
43
+ export {};
44
+ //# sourceMappingURL=hook-source.d.ts.map
@@ -0,0 +1,40 @@
1
+ const HOOK_SOURCE_WORD_PHASE = {
2
+ managedPolicy: "managedPolicy",
3
+ user: "unmanaged",
4
+ project: "unmanaged",
5
+ local: "unmanaged",
6
+ flag: "unmanaged",
7
+ };
8
+ const HOOK_SOURCE_PREFIX_PHASE = {
9
+ managedPlugin: "managedPlugin",
10
+ plugin: "unmanaged",
11
+ };
12
+ export const HOOK_PHASES = ["unmanaged", "managedPlugin", "managedPolicy"];
13
+ export const MAX_HOOK_SOURCE_NAME_CHARS = 128;
14
+ export function readHookGroupSource(raw) {
15
+ if (typeof raw !== "string")
16
+ return undefined;
17
+ if (Object.hasOwn(HOOK_SOURCE_WORD_PHASE, raw))
18
+ return raw;
19
+ const colon = raw.indexOf(":");
20
+ if (colon <= 0)
21
+ return undefined;
22
+ const prefix = raw.slice(0, colon);
23
+ const name = raw.slice(colon + 1);
24
+ if (!Object.hasOwn(HOOK_SOURCE_PREFIX_PHASE, prefix))
25
+ return undefined;
26
+ if (name.length === 0 || name.length > MAX_HOOK_SOURCE_NAME_CHARS)
27
+ return undefined;
28
+ return raw;
29
+ }
30
+ export function hookPhaseOf(source) {
31
+ if (source === undefined)
32
+ return "unmanaged";
33
+ if (Object.hasOwn(HOOK_SOURCE_WORD_PHASE, source))
34
+ return HOOK_SOURCE_WORD_PHASE[source];
35
+ return HOOK_SOURCE_PREFIX_PHASE[source.slice(0, source.indexOf(":"))];
36
+ }
37
+ export const HOOK_SOURCE_VOCABULARY_SENTENCE = `${Object.keys(HOOK_SOURCE_WORD_PHASE).join(" / ")}, or ${Object.keys(HOOK_SOURCE_PREFIX_PHASE)
38
+ .map((p) => `${p}:<name>`)
39
+ .join(" / ")} with a non-empty name of at most ${MAX_HOOK_SOURCE_NAME_CHARS} characters`;
40
+ //# sourceMappingURL=hook-source.js.map
@@ -39,16 +39,8 @@ export declare const ACTIVE_RUN_CONFLICT_BASE_TEXT = "session already has an act
39
39
  * **不是** token 摘要,理由是「最需要它的投影面恰恰是拿不到 token 的那些」——本载体就是其中之一)。
40
40
  * ⚠️ 它**不是** askId 的替代:askId 是 `approval_ask` 行的身份,checkpointId 是 checkpoint 行的身份,
41
41
  * 两者一对多且今天仍无反查读口(那条记账原样有效,别据本键认为它已销账)。
42
- * ⚠️ **辖域如实**:本批只把这一位投到**本载体**(409 体 + 其 done 帧)。另外三条 park 读面今天都**没有**
43
- * 这一格,而且它们的**代价不同**(亲核过读面真码,别按一句「都补上」估工):
44
- * · `/v1/assistant/inbox` 读 `listByScope` ⇒ core 的 `CheckpointSummary`,**值已经在手**(core
45
- * `summarizeCheckpoint` 单源投影里就带着),补它 = 那张显式白名单上加一行,零 SQL 面;
46
- * · `GET /v1/approvals` 与 `/v1/approvals/stream` 读的是**本仓自己的** `PendingCheckpoint`
47
- * (`checkpoint-store-sql.ts` 的 `listPending`,**按列 SELECT、刻意不拉 blob** —— 那是它避免读放大
48
- * 的既定姿势),所以这一位**不在手**:要补得给 checkpoint 表反范式一列(与 `gate_kind` /
49
- * `bound_input_hash` / `risk_descriptor` 同族)⇒ **SQL 面 = 双库集成门**,不是顺手一行。
50
- * 补它们是另一批的事(各自过三问:谁需要跨面 join、join 不上谁受伤、补偿是什么),别在文档或消费码里
51
- * 预支「两面能对上」。
42
+ * 7.133.0 起三条 durable 读面(运维队列行 `GET /v1/approvals` + `/v1/approvals/stream` 帧、停驻收件 `GET /v1/assistant/inbox` 行)同带这一格,
43
+ * 同一信任边界({@link trustedCheckpointId}),在那三面上它是 join 键(有可信值就带;缺它的行照列、键缺席 + 计数,见 `checkpoint-identity.ts`)。
52
44
  *
53
45
  * 🔴 **keyset 门辖域外,故在此成文**:`src/trace/core-keyset-guard.ts` 的七个面是
54
46
  * TaskNotificationPayload / BackgroundChildEvent / RosterEntry / AskRequest+ToolApprovalFrame /
@@ -62,11 +54,11 @@ export interface PendingGateMaterial {
62
54
  /** 见 {@link PendingGateMaterial} 顶注:`true` 才在场,恒不写 `false`。 */
63
55
  governanceForced?: true;
64
56
  /** [ref](core 5.42.0):这道 park 门的**非秘密稳定身份**(`Checkpoint.checkpointId`)。逐字过境,
65
- * server 绝不自铸、绝不派生(信任边界校验见 `safeCheckpointIdOf`)。
57
+ * server 绝不自铸、绝不派生(信任边界校验见 `checkpoint-identity.ts` 的 `trustedCheckpointId`)。
66
58
  * 🔴 **缺席是三成因合流的一个形**(R3-[low] 验真后改真 —— 此前这里只写了第一条,那是失真):
67
59
  * ① **legacy 行**:该键诞生前 park 的(core 明写「absent ⇒ legacy row, read compatibly, no backfill」);
68
- * ② **服务端校验拒绝**:行上有值但没过 `safeCheckpointIdOf`(空串 / 超长 / 含 token) ⇒ **数据污染**,
69
- * 而它在运维面上今天**与 ① 不可分辨**(见该函数顶注末段的残留登记);
60
+ * ② **服务端校验拒绝**:行上有值但没过 `trustedCheckpointId`(空串 / 超长 / 含 token) ⇒ **数据污染**;
61
+ * 本载体上它与 ① 同形(键缺席、不计数 —— best-effort 分诊材料),durable 读面上两者分计(`checkpoint-identity.ts` 头注);
70
62
  * ③ **材料读取失败**:整只 `pendingGate` 都不在(那时本键连位置都没有)。
71
63
  * ⇒ 读作「不知道这道门叫什么」,**不是**「这道门没有身份」,更**不是**「这一定是条老行」。
72
64
  *
@@ -2,6 +2,8 @@ import { governanceOriginOf } from "../runtime-governance.js";
2
2
  import { isParkedRunStatus } from "../plugins/store-contracts.js";
3
3
  import { LIVE_RUN_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
4
4
  import { recordFailOpen } from "../observability/fail-open.js";
5
+ import { refusalBody } from "./refusal-body.js";
6
+ import { trustedCheckpointId } from "../checkpoint-identity.js";
5
7
  export const ACTIVE_RUN_CONFLICT_BASE_TEXT = "session already has an active run — POST /v1/runs/{activeTaskId}/cancel stops it (same-instance interactive runs abort immediately)";
6
8
  export function resumeEntryForGate(kind, ids) {
7
9
  switch (kind) {
@@ -18,15 +20,6 @@ export function resumeEntryForGate(kind, ids) {
18
20
  return null;
19
21
  }
20
22
  }
21
- const MAX_CHECKPOINT_ID_CHARS = 128;
22
- function safeCheckpointIdOf(raw, token) {
23
- if (typeof raw !== "string" || raw === "" || raw.length > MAX_CHECKPOINT_ID_CHARS)
24
- return undefined;
25
- const secret = token === undefined || token === null ? "" : String(token).toLowerCase();
26
- if (secret !== "" && raw.toLowerCase().includes(secret))
27
- return undefined;
28
- return raw;
29
- }
30
23
  export function isActiveRunConflictBody(body) {
31
24
  return typeof body === "object" && body !== null && "activeTaskId" in body && body.errorCode === "conflict.session_active_run";
32
25
  }
@@ -42,12 +35,15 @@ export function toDoneFrameResult(body) {
42
35
  ...(body.pendingGate !== undefined ? { pendingGate: body.pendingGate } : {}),
43
36
  };
44
37
  }
38
+ function activeRunConflictBody(message, facts) {
39
+ const body = refusalBody({ errorCode: "conflict.session_active_run", message, facts });
40
+ if (!isActiveRunConflictBody(body))
41
+ throw new Error("session-active-run conflict body lost its identity keys (errorCode / activeTaskId)");
42
+ return body;
43
+ }
45
44
  export async function buildActiveRunConflict(deps, sessionId, activeTaskId) {
46
- const base = {
47
- error: ACTIVE_RUN_CONFLICT_BASE_TEXT,
48
- errorCode: "conflict.session_active_run",
49
- activeTaskId: activeTaskId ?? null,
50
- };
45
+ const baseFacts = { activeTaskId: activeTaskId ?? null };
46
+ const base = activeRunConflictBody(ACTIVE_RUN_CONFLICT_BASE_TEXT, baseFacts);
51
47
  if (!activeTaskId)
52
48
  return base;
53
49
  try {
@@ -60,13 +56,12 @@ export async function buildActiveRunConflict(deps, sessionId, activeTaskId) {
60
56
  const updatedAtMs = row?.updatedAt !== undefined ? new Date(row.updatedAt).getTime() : Number.NaN;
61
57
  const staleMs = (deps.runStaleSec ?? 0) * 1000;
62
58
  const stale = staleMs > 0 && Number.isFinite(updatedAtMs) && Date.now() - updatedAtMs > staleMs;
63
- return {
64
- ...base,
65
- error: "session already has an active run — POST /v1/runs/{activeTaskId}/steer injects a message into the running turn (queued, applied at the next turn boundary), or POST /v1/runs/{activeTaskId}/cancel stops it (same-instance interactive runs abort immediately)",
59
+ return activeRunConflictBody("session already has an active run — POST /v1/runs/{activeTaskId}/steer injects a message into the running turn (queued, applied at the next turn boundary), or POST /v1/runs/{activeTaskId}/cancel stops it (same-instance interactive runs abort immediately)", {
60
+ ...baseFacts,
66
61
  activeTaskStatus: status,
67
62
  ...(lastAt !== undefined ? { msSinceLastActivity: Math.max(0, Date.now() - lastAt) } : {}),
68
63
  ...(stale ? { stale: true } : {}),
69
- };
64
+ });
70
65
  }
71
66
  const cs = deps.checkpointStore;
72
67
  let pendingGate;
@@ -79,7 +74,7 @@ export async function buildActiveRunConflict(deps, sessionId, activeTaskId) {
79
74
  const kind = gate?.kind;
80
75
  const decidePath = kind ? resumeEntryForGate(kind, { sessionId, taskId: activeTaskId }) : null;
81
76
  const governanceForced = governanceOriginOf(gate, deps.governance);
82
- const checkpointId = safeCheckpointIdOf(row?.checkpointId, token);
77
+ const checkpointId = trustedCheckpointId(row?.checkpointId, token);
83
78
  if (kind && decidePath) {
84
79
  pendingGate = {
85
80
  kind,
@@ -89,14 +84,9 @@ export async function buildActiveRunConflict(deps, sessionId, activeTaskId) {
89
84
  };
90
85
  }
91
86
  }
92
- return {
93
- ...base,
94
- error: pendingGate
95
- ? `session already has an active run — it is parked waiting for a decision (${status}); resolve it via POST ${pendingGate.decidePath}, or POST /v1/runs/{activeTaskId}/cancel abandons it`
96
- : `session already has an active run — it is parked (${status}); POST /v1/runs/{activeTaskId}/cancel abandons it (its pending decision could not be located on this instance)`,
97
- activeTaskStatus: status,
98
- ...(pendingGate ? { pendingGate } : {}),
99
- };
87
+ return activeRunConflictBody(pendingGate
88
+ ? `session already has an active run — it is parked waiting for a decision (${status}); resolve it via POST ${pendingGate.decidePath}, or POST /v1/runs/{activeTaskId}/cancel abandons it`
89
+ : `session already has an active run — it is parked (${status}); POST /v1/runs/{activeTaskId}/cancel abandons it (its pending decision could not be located on this instance)`, { ...baseFacts, activeTaskStatus: status, ...(pendingGate ? { pendingGate } : {}) });
100
90
  }
101
91
  catch {
102
92
  recordFailOpen("server.approvals.active-run-conflict-material-unavailable");
@@ -1,23 +1,26 @@
1
1
  import { APPROVAL_REACH, MCP_IMAGE_MAX_BASE64, isApprovalReach, isPermissionMode, isThinkingLevel, expandTiers } from "@sema-agent/core";
2
2
  import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../model-select.js";
3
- import { modelUnavailableMessage } from "../model-entry-refusal.js";
3
+ import { echoModelRef, modelUnavailableMessage } from "../model-entry-refusal.js";
4
4
  import { HttpError, sessionIdShapeIssue } from "../security.js";
5
5
  import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, PERMISSION_MODE_WORDS, parseTaskSettings, settingsPermissionBucketIssue, skillListingBudgetFractionOk, skillListingMaxDescOk, stampEntryPermissionMode, taskHooksHonored, taskSettingsKeyIssue } from "../task-settings.js";
6
+ import { tightenOnlySeatIssue } from "../tighten-only-seats.js";
6
7
  import { buildEntryModeClampNotice } from "../entry-mode-clamp-notice.js";
7
8
  import { autonomyModeFloor } from "../runtime-governance.js";
8
9
  import { taskBodyKeyIssue } from "../request-key-closure.js";
9
- import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
10
+ import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, fileHistoryRestoreKeysInBody, inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
10
11
  import * as facts from "../capability-facts.js";
11
12
  import { buildHooksRefusal, parseHooksConfig, hooksMatcherIssue } from "../hooks/hook-runner.js";
12
13
  import { judgeRequestWebSearch } from "../plugins/web-search.js";
13
14
  import { normalizeApproachNotice, skillToolListOk, unknownTaskAgentModelRef, validateTaskAgents } from "../spec-fields.js";
14
- import { skillToolListsOverCap } from "../rule-entry-list.js";
15
- import { limitRefusal } from "../limit-refusal.js";
16
- import { quotedCallerText } from "../caller-text-echo.js";
15
+ import { allowSelectorWarningsOf, skillToolListsOverCap } from "../rule-entry-list.js";
16
+ import { limitFacts, limitRefusal } from "../limit-refusal.js";
17
+ import { fieldConflictFacts } from "../field-conflict.js";
18
+ import { echoCallerText, quotedCallerText } from "../caller-text-echo.js";
17
19
  import { MAX_ADDITIONAL_DIRS } from "../task-cwd.js";
18
20
  import { verifyRoundsFromBody } from "./verify-rounds.js";
19
21
  import { skillCarriesRetiredManifest } from "./retired-skill-manifest.js";
20
22
  import { resolveDevicePlacementIntent } from "./device-placement.js";
23
+ import { callerTrust, requestTrustRefusal } from "./caller-trust.js";
21
24
  import { createRunAcceptance } from "../run-acceptance.js";
22
25
  import { sendError, sendHttpError } from "./send.js";
23
26
  import { refuseSubmitBody } from "./request-guards.js";
@@ -27,38 +30,56 @@ const APPROVAL_REACH_WORDS = APPROVAL_REACH.map((word) => JSON.stringify(word)).
27
30
  export const MAX_SKILL_CONTENT_CHARS = 1_048_576;
28
31
  export const MAX_SYSTEM_PROMPT_CHARS = 16_384;
29
32
  export const MAX_OUTPUT_SCHEMA_CHARS = 32_768;
33
+ const MAX_JOB_ID_CHARS = 64;
34
+ const MAX_CLIENT_TIMEZONE_CHARS = 64;
35
+ const MAX_CLIENT_USER_EMAIL_CHARS = 256;
36
+ const MAX_SANDBOX_IMAGE_PROFILE_CHARS = 128;
37
+ const MAX_RESUME_AT_CHARS = 64;
38
+ const overCharsFacts = (field, v, cap) => (typeof v === "string" && v.length > cap ? limitFacts(field, "characters", v.length, cap) : undefined);
39
+ const multiAttemptFields = (body) => {
40
+ const out = [];
41
+ if (body.verify === true)
42
+ out.push("verify");
43
+ if (body.cascade === true)
44
+ out.push("cascade");
45
+ return out;
46
+ };
30
47
  export function validateUserSkills(skills) {
31
48
  if (skills === undefined)
32
49
  return null;
33
50
  if (!Array.isArray(skills))
34
51
  return { message: "skills must be an array of { name, description, content }" };
35
52
  for (let k = 0; k < skills.length; k++) {
36
- const shape = userSkillShapeIssue(skills[k]);
53
+ const shape = userSkillShapeIssue(skills[k], k);
37
54
  if (shape !== null)
38
- return { message: shape };
55
+ return shape;
39
56
  const over = skillToolListsOverCap(skills[k], `skills[${k}]`, "top");
40
57
  if (over !== undefined)
41
58
  return { message: over.message, ...(over.extra !== undefined ? { facts: over.extra } : {}) };
42
59
  }
43
60
  return null;
44
61
  }
45
- function userSkillShapeIssue(item) {
62
+ function userSkillShapeIssue(item, index) {
46
63
  if (item === null || typeof item !== "object")
47
- return "each skill must be an object { name, description, content }";
64
+ return { message: "each skill must be an object { name, description, content }" };
48
65
  const s = item;
49
66
  if (typeof s["name"] !== "string" || s["name"].length === 0)
50
- return "skill.name must be a non-empty string";
67
+ return { message: "skill.name must be a non-empty string" };
51
68
  if (typeof s["description"] !== "string")
52
- return "skill.description must be a string";
53
- if (typeof s["content"] !== "string" || s["content"].length === 0 || s["content"].length > MAX_SKILL_CONTENT_CHARS)
54
- return `skill.content must be a non-empty string of at most ${MAX_SKILL_CONTENT_CHARS} characters (the engine rejects skills over this size outright at load time)`;
55
- if (s["baseDir"] !== undefined && !isAbsoluteHostPath(s["baseDir"]))
56
- return `skill.baseDir must be an absolute path string of at most ${ABSOLUTE_HOST_PATH_MAX} characters with no ".." segment and no NUL byte (it is not resolved or read here — the engine only substitutes it into the skill's own text)`;
69
+ return { message: "skill.description must be a string" };
70
+ if (typeof s["content"] !== "string" || s["content"].length === 0 || s["content"].length > MAX_SKILL_CONTENT_CHARS) {
71
+ const facts = overCharsFacts(`skills[${index}].content`, s["content"], MAX_SKILL_CONTENT_CHARS);
72
+ return { message: `skill.content must be a non-empty string of at most ${MAX_SKILL_CONTENT_CHARS} characters (the engine rejects skills over this size outright at load time)`, ...(facts === undefined ? {} : { facts }) };
73
+ }
74
+ if (s["baseDir"] !== undefined && !isAbsoluteHostPath(s["baseDir"])) {
75
+ const facts = overCharsFacts(`skills[${index}].baseDir`, s["baseDir"], ABSOLUTE_HOST_PATH_MAX);
76
+ return { message: `skill.baseDir must be an absolute path string of at most ${ABSOLUTE_HOST_PATH_MAX} characters with no ".." segment and no NUL byte (it is not resolved or read here — the engine only substitutes it into the skill's own text)`, ...(facts === undefined ? {} : { facts }) };
77
+ }
57
78
  if (skillCarriesRetiredManifest(s))
58
- return SKILL_MANIFEST_RETIRED;
79
+ return { message: SKILL_MANIFEST_RETIRED };
59
80
  for (const key of ["allowedTools", "disallowedTools"]) {
60
81
  if (s[key] !== undefined && !skillToolListOk(s[key]))
61
- return `skill.${key} must be an array of strings (permission-rule spellings: a tool name, mcp__<server> / mcp__<server>__*, Bash(<prefix>:*), Read(//<dir>/**))`;
82
+ return { message: `skill.${key} must be an array of strings (permission-rule spellings: a tool name, mcp__<server> / mcp__<server>__*, Bash(<prefix>:*), Read(//<dir>/**))` };
62
83
  }
63
84
  return null;
64
85
  }
@@ -72,7 +93,7 @@ function refuseUnavailableModel(res, config, field, ref) {
72
93
  const hit = refusedCatalogEntry(ref, config.modelRefusals);
73
94
  if (hit === undefined)
74
95
  return false;
75
- const message = modelUnavailableMessage(field, ref.slice(0, 120), hit.name, hit.refusal);
96
+ const message = modelUnavailableMessage(field, ref, hit.name, hit.refusal);
76
97
  sendError(res, 400, "request.model_unavailable", message);
77
98
  return true;
78
99
  }
@@ -96,7 +117,7 @@ export function createAdmission(ctx) {
96
117
  {
97
118
  const refusal = refuseSubmitBody(sqlWallsOf(deps.sqlEngine), body);
98
119
  if (refusal !== null) {
99
- const facts = { wallSource: refusal.wallSource };
120
+ const facts = { ...limitFacts("body", "bytes", refusal.bytes, refusal.limitBytes), wallSource: refusal.wallSource };
100
121
  switch (refusal.wallSource) {
101
122
  case "reported":
102
123
  switch (refusal.by) {
@@ -149,8 +170,8 @@ export function createAdmission(ctx) {
149
170
  return null;
150
171
  }
151
172
  }
152
- if (body.jobId !== undefined && (typeof body.jobId !== "string" || body.jobId.length === 0 || body.jobId.length > 64)) {
153
- sendError(res, 400, "request.field_invalid", "jobId must be a non-empty string of at most 64 characters");
173
+ if (body.jobId !== undefined && (typeof body.jobId !== "string" || body.jobId.length === 0 || body.jobId.length > MAX_JOB_ID_CHARS)) {
174
+ sendError(res, 400, "request.field_invalid", "jobId must be a non-empty string of at most 64 characters", overCharsFacts("jobId", body.jobId, MAX_JOB_ID_CHARS));
154
175
  return null;
155
176
  }
156
177
  {
@@ -161,7 +182,7 @@ export function createAdmission(ctx) {
161
182
  }
162
183
  }
163
184
  if (typeof body.systemPrompt === "string" && body.systemPrompt.length > MAX_SYSTEM_PROMPT_CHARS) {
164
- sendError(res, 400, "request.field_invalid", `systemPrompt must be at most ${MAX_SYSTEM_PROMPT_CHARS} characters`);
185
+ sendError(res, 400, "request.field_invalid", `systemPrompt must be at most ${MAX_SYSTEM_PROMPT_CHARS} characters`, limitFacts("systemPrompt", "characters", body.systemPrompt.length, MAX_SYSTEM_PROMPT_CHARS));
165
186
  return null;
166
187
  }
167
188
  if (body.appendSystemPrompt !== undefined) {
@@ -170,7 +191,7 @@ export function createAdmission(ctx) {
170
191
  return null;
171
192
  }
172
193
  if (body.appendSystemPrompt.length > MAX_SYSTEM_PROMPT_CHARS) {
173
- sendError(res, 400, "request.field_invalid", `appendSystemPrompt must be at most ${MAX_SYSTEM_PROMPT_CHARS} characters`);
194
+ sendError(res, 400, "request.field_invalid", `appendSystemPrompt must be at most ${MAX_SYSTEM_PROMPT_CHARS} characters`, limitFacts("appendSystemPrompt", "characters", body.appendSystemPrompt.length, MAX_SYSTEM_PROMPT_CHARS));
174
195
  return null;
175
196
  }
176
197
  }
@@ -203,14 +224,12 @@ export function createAdmission(ctx) {
203
224
  }
204
225
  }
205
226
  if (typeof st.outputStyle === "string" && st.outputStyle.length > MAX_SETTINGS_OUTPUT_STYLE_CHARS) {
206
- sendError(res, 400, "request.field_invalid", `settings.outputStyle must be at most ${MAX_SETTINGS_OUTPUT_STYLE_CHARS} characters`);
227
+ sendError(res, 400, "request.field_invalid", `settings.outputStyle must be at most ${MAX_SETTINGS_OUTPUT_STYLE_CHARS} characters`, limitFacts("settings.outputStyle", "characters", st.outputStyle.length, MAX_SETTINGS_OUTPUT_STYLE_CHARS));
207
228
  return null;
208
229
  }
209
- if (typeof st.outputStyle === "string" &&
210
- st.outputStyle.length > 0 &&
211
- typeof body.appendSystemPrompt === "string" &&
212
- body.appendSystemPrompt.length + 2 + st.outputStyle.length > MAX_SYSTEM_PROMPT_CHARS) {
213
- sendError(res, 400, "request.field_conflict", `appendSystemPrompt + settings.outputStyle fold into one system-prompt block — combined they must be at most ${MAX_SYSTEM_PROMPT_CHARS} characters (including the 2-char joiner)`);
230
+ const foldedChars = typeof st.outputStyle === "string" && st.outputStyle.length > 0 && typeof body.appendSystemPrompt === "string" ? body.appendSystemPrompt.length + 2 + st.outputStyle.length : 0;
231
+ if (foldedChars > MAX_SYSTEM_PROMPT_CHARS) {
232
+ sendError(res, 400, "request.field_conflict", `appendSystemPrompt + settings.outputStyle fold into one system-prompt block — combined they must be at most ${MAX_SYSTEM_PROMPT_CHARS} characters (including the 2-char joiner)`, fieldConflictFacts("system_prompt_fold", ["appendSystemPrompt", "settings.outputStyle"], { count: foldedChars, cap: MAX_SYSTEM_PROMPT_CHARS }));
214
233
  return null;
215
234
  }
216
235
  const perms = st.permissions;
@@ -225,6 +244,13 @@ export function createAdmission(ctx) {
225
244
  return null;
226
245
  }
227
246
  }
247
+ {
248
+ const seatIssue = tightenOnlySeatIssue(st);
249
+ if (seatIssue) {
250
+ sendError(res, 400, "request.field_invalid", seatIssue.message, seatIssue.fields);
251
+ return null;
252
+ }
253
+ }
228
254
  const envRaw = st.env;
229
255
  if (envRaw !== undefined && envRaw !== null) {
230
256
  if (typeof envRaw !== "object" || Array.isArray(envRaw)) {
@@ -233,12 +259,16 @@ export function createAdmission(ctx) {
233
259
  }
234
260
  const entries = Object.entries(envRaw);
235
261
  if (entries.length > MAX_SETTINGS_ENV_VARS) {
236
- sendError(res, 400, "request.field_invalid", `settings.env must have at most ${MAX_SETTINGS_ENV_VARS} variables`);
262
+ sendError(res, 400, "request.field_invalid", `settings.env must have at most ${MAX_SETTINGS_ENV_VARS} variables`, limitFacts("settings.env", "entries", entries.length, MAX_SETTINGS_ENV_VARS));
237
263
  return null;
238
264
  }
239
265
  for (const [k, val] of entries) {
240
- if (k.length > MAX_SETTINGS_ENV_KEY_CHARS || (typeof val === "string" && val.length > MAX_SETTINGS_ENV_VALUE_CHARS)) {
241
- sendError(res, 400, "request.field_invalid", `settings.env keys must be ≤${MAX_SETTINGS_ENV_KEY_CHARS} chars and values ≤${MAX_SETTINGS_ENV_VALUE_CHARS} chars`);
266
+ const keyOver = k.length > MAX_SETTINGS_ENV_KEY_CHARS;
267
+ const valueOver = typeof val === "string" && val.length > MAX_SETTINGS_ENV_VALUE_CHARS;
268
+ if (keyOver || valueOver) {
269
+ const field = `settings.env.${echoCallerText(k, { maxChars: MAX_SETTINGS_ENV_KEY_CHARS })}`;
270
+ const facts = keyOver ? limitFacts(field, "characters", k.length, MAX_SETTINGS_ENV_KEY_CHARS) : limitFacts(field, "characters", val.length, MAX_SETTINGS_ENV_VALUE_CHARS);
271
+ sendError(res, 400, "request.field_invalid", `settings.env keys must be ≤${MAX_SETTINGS_ENV_KEY_CHARS} chars and values ≤${MAX_SETTINGS_ENV_VALUE_CHARS} chars`, facts);
242
272
  return null;
243
273
  }
244
274
  }
@@ -284,13 +314,14 @@ export function createAdmission(ctx) {
284
314
  }
285
315
  if (Array.isArray(body.images)) {
286
316
  if (body.images.length > MAX_IMAGES_PER_REQUEST) {
287
- sendError(res, 413, "request.payload_too_large", `at most ${MAX_IMAGES_PER_REQUEST} images per request`);
317
+ sendError(res, 413, "request.payload_too_large", `at most ${MAX_IMAGES_PER_REQUEST} images per request`, limitFacts("images", "entries", body.images.length, MAX_IMAGES_PER_REQUEST));
288
318
  return null;
289
319
  }
290
- for (const img of body.images) {
320
+ for (const [i, img] of body.images.entries()) {
291
321
  const data = img.data;
292
- if (typeof data === "string" && Buffer.byteLength(data, "utf8") > MAX_IMAGE_BASE64_BYTES) {
293
- sendError(res, 413, "request.payload_too_large", `each inline image must be at most ${MAX_IMAGE_BASE64_BYTES} base64 bytes (the engine's inline-image limit — the model API's 5 MB image cap)`);
322
+ const dataBytes = typeof data === "string" ? Buffer.byteLength(data, "utf8") : 0;
323
+ if (dataBytes > MAX_IMAGE_BASE64_BYTES) {
324
+ sendError(res, 413, "request.payload_too_large", `each inline image must be at most ${MAX_IMAGE_BASE64_BYTES} base64 bytes (the engine's inline-image limit — the model API's 5 MB image cap)`, limitFacts(`images[${i}].data`, "bytes", dataBytes, MAX_IMAGE_BASE64_BYTES));
294
325
  return null;
295
326
  }
296
327
  }
@@ -301,17 +332,17 @@ export function createAdmission(ctx) {
301
332
  sendError(res, 400, "request.field_invalid", "clientContext must be an object");
302
333
  return null;
303
334
  }
304
- if (cc.timeZone !== undefined && (typeof cc.timeZone !== "string" || cc.timeZone.length > 64)) {
305
- sendError(res, 400, "request.field_invalid", "clientContext.timeZone must be a string of at most 64 characters");
335
+ if (cc.timeZone !== undefined && (typeof cc.timeZone !== "string" || cc.timeZone.length > MAX_CLIENT_TIMEZONE_CHARS)) {
336
+ sendError(res, 400, "request.field_invalid", "clientContext.timeZone must be a string of at most 64 characters", overCharsFacts("clientContext.timeZone", cc.timeZone, MAX_CLIENT_TIMEZONE_CHARS));
306
337
  return null;
307
338
  }
308
- if (cc.userEmail !== undefined && (typeof cc.userEmail !== "string" || cc.userEmail.length > 256)) {
309
- sendError(res, 400, "request.field_invalid", "clientContext.userEmail must be a string of at most 256 characters");
339
+ if (cc.userEmail !== undefined && (typeof cc.userEmail !== "string" || cc.userEmail.length > MAX_CLIENT_USER_EMAIL_CHARS)) {
340
+ sendError(res, 400, "request.field_invalid", "clientContext.userEmail must be a string of at most 256 characters", overCharsFacts("clientContext.userEmail", cc.userEmail, MAX_CLIENT_USER_EMAIL_CHARS));
310
341
  return null;
311
342
  }
312
343
  }
313
- if (body.sandboxImageProfile !== undefined && (typeof body.sandboxImageProfile !== "string" || body.sandboxImageProfile.length === 0 || body.sandboxImageProfile.length > 128)) {
314
- sendError(res, 400, "request.field_invalid", "sandboxImageProfile must be a non-empty string of at most 128 characters");
344
+ if (body.sandboxImageProfile !== undefined && (typeof body.sandboxImageProfile !== "string" || body.sandboxImageProfile.length === 0 || body.sandboxImageProfile.length > MAX_SANDBOX_IMAGE_PROFILE_CHARS)) {
345
+ sendError(res, 400, "request.field_invalid", "sandboxImageProfile must be a non-empty string of at most 128 characters", overCharsFacts("sandboxImageProfile", body.sandboxImageProfile, MAX_SANDBOX_IMAGE_PROFILE_CHARS));
315
346
  return null;
316
347
  }
317
348
  if (body.capabilitiesNeeded !== undefined && (!Array.isArray(body.capabilitiesNeeded) || body.capabilitiesNeeded.some((c) => typeof c !== "string"))) {
@@ -319,11 +350,11 @@ export function createAdmission(ctx) {
319
350
  return null;
320
351
  }
321
352
  if (Array.isArray(body.capabilitiesNeeded) && body.capabilitiesNeeded.length > 0 && (typeof body.sandboxImageProfile !== "string" || body.sandboxImageProfile.length === 0)) {
322
- sendError(res, 400, "request.field_conflict", "capabilitiesNeeded requires sandboxImageProfile (it constrains the selected image)");
353
+ sendError(res, 400, "request.field_conflict", "capabilitiesNeeded requires sandboxImageProfile (it constrains the selected image)", fieldConflictFacts("image_profile", ["capabilitiesNeeded", "sandboxImageProfile"]));
323
354
  return null;
324
355
  }
325
356
  if (typeof body.sandboxImageProfile === "string" && body.sandboxImageProfile.length > 0 && (body.cascade === true || body.verify === true)) {
326
- sendError(res, 400, "request.field_conflict", "sandboxImageProfile is not supported together with cascade/verify in v1 (the per-task image would not bind to the cascade rungs / verifier sub-run)");
357
+ sendError(res, 400, "request.field_conflict", "sandboxImageProfile is not supported together with cascade/verify in v1 (the per-task image would not bind to the cascade rungs / verifier sub-run)", fieldConflictFacts("cascade_verify", ["sandboxImageProfile", ...multiAttemptFields(body)]));
327
358
  return null;
328
359
  }
329
360
  if (body.outputSchema !== undefined) {
@@ -331,8 +362,9 @@ export function createAdmission(ctx) {
331
362
  sendError(res, 400, "request.field_invalid", "outputSchema must be a JSON Schema object");
332
363
  return null;
333
364
  }
334
- if (JSON.stringify(body.outputSchema).length > MAX_OUTPUT_SCHEMA_CHARS) {
335
- sendError(res, 400, "request.field_invalid", `outputSchema must serialize to at most ${MAX_OUTPUT_SCHEMA_CHARS} characters`);
365
+ const schemaChars = JSON.stringify(body.outputSchema).length;
366
+ if (schemaChars > MAX_OUTPUT_SCHEMA_CHARS) {
367
+ sendError(res, 400, "request.field_invalid", `outputSchema must serialize to at most ${MAX_OUTPUT_SCHEMA_CHARS} characters`, limitFacts("outputSchema", "characters", schemaChars, MAX_OUTPUT_SCHEMA_CHARS));
336
368
  return null;
337
369
  }
338
370
  }
@@ -341,12 +373,12 @@ export function createAdmission(ctx) {
341
373
  return null;
342
374
  }
343
375
  if (body.resumeAt !== undefined) {
344
- if (typeof body.resumeAt !== "string" || body.resumeAt.length === 0 || body.resumeAt.length > 64) {
345
- sendError(res, 400, "request.field_invalid", "resumeAt must be a non-empty message eventId string (≤64 chars)");
376
+ if (typeof body.resumeAt !== "string" || body.resumeAt.length === 0 || body.resumeAt.length > MAX_RESUME_AT_CHARS) {
377
+ sendError(res, 400, "request.field_invalid", "resumeAt must be a non-empty message eventId string (≤64 chars)", overCharsFacts("resumeAt", body.resumeAt, MAX_RESUME_AT_CHARS));
346
378
  return null;
347
379
  }
348
380
  if (body.verify === true || body.cascade === true) {
349
- sendError(res, 400, "request.field_conflict", "resumeAt is not supported with verify or cascade (those start a fresh/replaced session)");
381
+ sendError(res, 400, "request.field_conflict", "resumeAt is not supported with verify or cascade (those start a fresh/replaced session)", fieldConflictFacts("cascade_verify", ["resumeAt", ...multiAttemptFields(body)]));
350
382
  return null;
351
383
  }
352
384
  }
@@ -356,7 +388,7 @@ export function createAdmission(ctx) {
356
388
  return null;
357
389
  }
358
390
  if (body.resumeAt === undefined) {
359
- sendError(res, 400, "request.field_conflict", "resumeAtMode requires resumeAt (it qualifies the rewind target)");
391
+ sendError(res, 400, "request.field_conflict", "resumeAtMode requires resumeAt (it qualifies the rewind target)", fieldConflictFacts("resume_anchor", ["resumeAtMode", "resumeAt"]));
360
392
  return null;
361
393
  }
362
394
  }
@@ -407,7 +439,7 @@ export function createAdmission(ctx) {
407
439
  }
408
440
  }
409
441
  if (fileHistoryRestoreConflict(body)) {
410
- sendError(res, 400, "request.field_conflict", FILE_HISTORY_DISABLED_NO_RESTORE);
442
+ sendError(res, 400, "request.field_conflict", FILE_HISTORY_DISABLED_NO_RESTORE, fieldConflictFacts("file_history_restore", ["fileHistory", ...fileHistoryRestoreKeysInBody(body)]));
411
443
  return null;
412
444
  }
413
445
  if (body.requireExistingSession !== undefined && typeof body.requireExistingSession !== "boolean") {
@@ -514,7 +546,7 @@ export function createAdmission(ctx) {
514
546
  if (um !== undefined && refuseUnavailableModel(res, deps.config, `agents[${um.index}].model`, um.ref))
515
547
  return null;
516
548
  if (um !== undefined) {
517
- sendError(res, 400, "request.unknown_reference", `unknown agents[${um.index}].model "${um.ref.slice(0, 120)}" — not in the configured catalog (a catalog name or tier word)`, { available: mentionableNames(deps.config, agentsCatalog) });
549
+ sendError(res, 400, "request.unknown_reference", `unknown agents[${um.index}].model "${echoModelRef(um.ref)}" — not in the configured catalog (a catalog name or tier word)`, { available: mentionableNames(deps.config, agentsCatalog) });
518
550
  return null;
519
551
  }
520
552
  }
@@ -541,9 +573,12 @@ export function createAdmission(ctx) {
541
573
  return null;
542
574
  }
543
575
  }
544
- if (body.disableSkills === true && ((body.skills?.length ?? 0) > 0 || (body.agents ?? []).some((a) => (a.skills?.length ?? 0) > 0))) {
545
- sendError(res, 400, "request.field_conflict", "disableSkills: true means this run installs no skill at all (not the deployment's, not the request's), so it cannot come with a non-empty skills or agents[].skills — send one or the other");
546
- return null;
576
+ if (body.disableSkills === true) {
577
+ const skillSeats = [...((body.skills?.length ?? 0) > 0 ? ["skills"] : []), ...(body.agents ?? []).flatMap((a, i) => ((a.skills?.length ?? 0) > 0 ? [`agents[${i}].skills`] : []))];
578
+ if (skillSeats.length > 0) {
579
+ sendError(res, 400, "request.field_conflict", "disableSkills: true means this run installs no skill at all (not the deployment's, not the request's), so it cannot come with a non-empty skills or agents[].skills — send one or the other", fieldConflictFacts("skills_disabled", ["disableSkills", ...skillSeats]));
580
+ return null;
581
+ }
547
582
  }
548
583
  {
549
584
  const v = body.userInvokedSkills;
@@ -608,7 +643,7 @@ export function createAdmission(ctx) {
608
643
  if (matchCatalogModel(body.model, catalog, deps.config.modelRefusals) === undefined) {
609
644
  if (refuseUnavailableModel(res, deps.config, "model", body.model))
610
645
  return null;
611
- sendError(res, 400, "request.unknown_reference", `unknown model "${body.model.slice(0, 120)}" — not in the configured catalog (name, tier word, or id)`, { available: mentionableNames(deps.config, catalog) });
646
+ sendError(res, 400, "request.unknown_reference", `unknown model "${echoModelRef(body.model)}" — not in the configured catalog (name, tier word, or id)`, { available: mentionableNames(deps.config, catalog) });
612
647
  return null;
613
648
  }
614
649
  }
@@ -622,7 +657,7 @@ export function createAdmission(ctx) {
622
657
  if (matchCatalogModel(body.compactionModel, catalog, deps.config.modelRefusals) === undefined) {
623
658
  if (refuseUnavailableModel(res, deps.config, "compactionModel", body.compactionModel))
624
659
  return null;
625
- sendError(res, 400, "request.unknown_reference", `unknown compactionModel "${body.compactionModel.slice(0, 120)}" — not in the configured catalog (name, tier word, or id)`, { available: mentionableNames(deps.config, catalog) });
660
+ sendError(res, 400, "request.unknown_reference", `unknown compactionModel "${echoModelRef(body.compactionModel)}" — not in the configured catalog (name, tier word, or id)`, { available: mentionableNames(deps.config, catalog) });
626
661
  return null;
627
662
  }
628
663
  }
@@ -663,17 +698,24 @@ export function createAdmission(ctx) {
663
698
  const verify = verifyRoundsFromBody(body);
664
699
  const cascade = body.cascade === true;
665
700
  if (cascade && verify) {
666
- sendError(res, 400, "request.field_conflict", "verify and cascade are mutually exclusive (verify = adversarial gate; cascade = cheap→strong ladder)");
701
+ sendError(res, 400, "request.field_conflict", "verify and cascade are mutually exclusive (verify = adversarial gate; cascade = cheap→strong ladder)", fieldConflictFacts("cascade_verify", ["verify", "cascade"]));
667
702
  return null;
668
703
  }
669
704
  if (body.suggestNextPrompts && (verify || cascade)) {
670
- sendError(res, 400, "request.field_conflict", "suggestNextPrompts is not supported with verify or cascade (those return a result, not a streamed run)");
705
+ sendError(res, 400, "request.field_conflict", "suggestNextPrompts is not supported with verify or cascade (those return a result, not a streamed run)", fieldConflictFacts("cascade_verify", ["suggestNextPrompts", ...multiAttemptFields(body)]));
671
706
  return null;
672
707
  }
673
708
  if (cascade && deps.config.cascadeLadder.length === 0) {
674
709
  sendError(res, 400, "request.precondition_unmet", "cascade requested but no ladder configured — set MODEL_CASCADE_LADDER (catalog model names, cheapest→strongest)");
675
710
  return null;
676
711
  }
712
+ {
713
+ const refusal = requestTrustRefusal({ permissions: parseTaskSettings(body.settings).settings?.permissions, skills: body.skills, userInvokedSkills: body.userInvokedSkills }, callerTrust(req, deps.config));
714
+ if (refusal !== undefined) {
715
+ sendError(res, 400, "request.field_invalid", refusal.message, refusal.facts);
716
+ return null;
717
+ }
718
+ }
677
719
  const { body: admitted, entry: entryMode } = stampEntryPermissionMode(body, autonomyModeFloor(deps.config.autonomy));
678
720
  try {
679
721
  const auth = deps.authorize ? await deps.authorize({ req, body: admitted }) : undefined;
@@ -691,7 +733,8 @@ export function createAdmission(ctx) {
691
733
  const acceptance = createRunAcceptance({ sessionTitler: deps.sessionTitler }, { ...(spec.sessionId ? { sessionId: spec.sessionId } : {}), objective: spec.objective, ...(model !== undefined ? { model } : {}) });
692
734
  const clampNotice = buildEntryModeClampNotice(entryMode, spec.sessionId);
693
735
  const pendingNotices = clampNotice !== undefined ? [clampNotice, ...engineNotices] : engineNotices;
694
- return { spec, runner: runnerFor(spec), auth, verify, cascade, jobId: admitted.jobId, body: admitted, acceptance, ...(bindingIntent === undefined ? {} : { bindingIntent }), ...(pendingNotices.length > 0 ? { engineNotices: pendingNotices } : {}) };
736
+ const warnings = allowSelectorWarningsOf(admitted, spec.tools ?? []);
737
+ return { spec, runner: runnerFor(spec), auth, verify, cascade, jobId: admitted.jobId, body: admitted, acceptance, ...(bindingIntent === undefined ? {} : { bindingIntent }), ...(pendingNotices.length > 0 ? { engineNotices: pendingNotices } : {}), ...(warnings.length > 0 ? { warnings } : {}) };
695
738
  }
696
739
  catch (err) {
697
740
  if (err instanceof HttpError) {