@sema-agent/server 7.131.0 → 7.133.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (509) hide show
  1. package/MIGRATION.md +575 -0
  2. package/README.md +12 -9
  3. package/README.zh-CN.md +9 -6
  4. package/USAGE.md +326 -98
  5. package/deploy/sema-up/README.md +19 -1
  6. package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -2
  7. package/deploy/sema-up/chart/templates/server.yaml +5 -2
  8. package/deploy/sema-up/stack/compose.yaml +3 -0
  9. package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
  10. package/dist/adoption/plan.d.ts +3 -2
  11. package/dist/adoption/plan.js +7 -5
  12. package/dist/adoption/runner.js +3 -10
  13. package/dist/adoption/sql.d.ts +3 -2
  14. package/dist/adoption/sql.js +3 -5
  15. package/dist/adoption/wire.d.ts +4 -1
  16. package/dist/adoption/wire.js +1 -0
  17. package/dist/approval-ask-audit-store.d.ts +31 -13
  18. package/dist/approval-ask-audit-store.js +272 -74
  19. package/dist/approval-ask-machine.d.ts +15 -0
  20. package/dist/approval-ask-machine.js +6 -0
  21. package/dist/approval-ask-retention.d.ts +136 -0
  22. package/dist/approval-ask-retention.js +102 -0
  23. package/dist/approval-card.d.ts +82 -20
  24. package/dist/approval-card.js +46 -16
  25. package/dist/approval-deny-reasons.d.ts +1 -1
  26. package/dist/approval.d.ts +9 -0
  27. package/dist/approval.js +24 -3
  28. package/dist/audit.d.ts +33 -19
  29. package/dist/audit.js +12 -16
  30. package/dist/auth-bridge.d.ts +4 -0
  31. package/dist/auth-bridge.js +10 -11
  32. package/dist/bake-runner/main.js +3 -2
  33. package/dist/boot/deferred-sandbox-path-env.js +1 -1
  34. package/dist/boot/deployment-mods.d.ts +31 -0
  35. package/dist/boot/deployment-mods.js +26 -0
  36. package/dist/boot/execution-env.d.ts +1 -1
  37. package/dist/boot/execution-env.js +27 -12
  38. package/dist/boot/execution-lane.js +11 -0
  39. package/dist/boot/leader.js +2 -0
  40. package/dist/boot/memory-boundary.d.ts +2 -2
  41. package/dist/boot/memory-boundary.js +2 -2
  42. package/dist/boot/memory-consolidation.d.ts +5 -4
  43. package/dist/boot/memory-consolidation.js +7 -6
  44. package/dist/boot/org-delivery.d.ts +28 -0
  45. package/dist/boot/org-delivery.js +20 -0
  46. package/dist/boot/read-deny-anchor-audit.d.ts +17 -0
  47. package/dist/boot/read-deny-anchor-audit.js +36 -0
  48. package/dist/boot/reapers.d.ts +3 -0
  49. package/dist/boot/reapers.js +21 -1
  50. package/dist/boot/resolve-spec.d.ts +4 -5
  51. package/dist/boot/resolve-spec.js +74 -123
  52. package/dist/boot/runner-deps.d.ts +19 -8
  53. package/dist/boot/runner-deps.js +15 -2
  54. package/dist/boot/session-faces.d.ts +2 -7
  55. package/dist/boot/session-faces.js +4 -2
  56. package/dist/boot/stage-02-web-admin-pull.js +4 -1
  57. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  58. package/dist/boot/stage-03-store-backend.js +2 -2
  59. package/dist/boot/stage-04-stores.d.ts +1 -1
  60. package/dist/boot/stage-05-execution-env.d.ts +4 -4
  61. package/dist/boot/stage-05-execution-env.js +7 -10
  62. package/dist/boot/stage-06-runners.d.ts +4 -2
  63. package/dist/boot/stage-06-runners.js +11 -7
  64. package/dist/boot/stage-07-capability-layer.d.ts +4 -2
  65. package/dist/boot/stage-07-capability-layer.js +27 -6
  66. package/dist/boot/stage-08-reapers.d.ts +4 -2
  67. package/dist/boot/stage-08-reapers.js +2 -2
  68. package/dist/boot/stage-09-leader.d.ts +6 -9
  69. package/dist/boot/stage-10-http-server.d.ts +6 -9
  70. package/dist/boot/stage-10-http-server.js +7 -9
  71. package/dist/boot/stores.d.ts +1 -1
  72. package/dist/boot/stores.js +7 -8
  73. package/dist/boot/web-admin.d.ts +4 -3
  74. package/dist/boot/web-admin.js +186 -98
  75. package/dist/boot/workflow-orchestration.d.ts +2 -0
  76. package/dist/boot/workflow-orchestration.js +2 -1
  77. package/dist/brain.js +25 -25
  78. package/dist/breaker-key.d.ts +6 -0
  79. package/dist/breaker-key.js +29 -0
  80. package/dist/budget.js +5 -4
  81. package/dist/caller-text-echo.d.ts +23 -0
  82. package/dist/caller-text-echo.js +21 -0
  83. package/dist/capabilities/brand-identity.d.ts +26 -0
  84. package/dist/capabilities/brand-identity.js +27 -0
  85. package/dist/capabilities/center-plugins.d.ts +7 -0
  86. package/dist/capabilities/center-plugins.js +1 -1
  87. package/dist/capabilities/center-prompts.d.ts +32 -59
  88. package/dist/capabilities/center-prompts.js +18 -32
  89. package/dist/capabilities/composite-members.d.ts +1 -1
  90. package/dist/capabilities/composite-members.js +1 -1
  91. package/dist/capabilities/engine-capabilities.js +2 -1
  92. package/dist/capabilities/execution-lane.js +2 -2
  93. package/dist/capabilities/hands-lane.d.ts +30 -6
  94. package/dist/capabilities/hands-lane.js +11 -1
  95. package/dist/capabilities/mcp-probe.d.ts +48 -3
  96. package/dist/capabilities/mcp-probe.js +47 -135
  97. package/dist/capabilities/scenarios.d.ts +95 -45
  98. package/dist/capabilities/scenarios.js +53 -38
  99. package/dist/capabilities/skills.d.ts +19 -4
  100. package/dist/capabilities/skills.js +30 -10
  101. package/dist/capability-facts.d.ts +34 -4
  102. package/dist/capability-facts.js +28 -2
  103. package/dist/center-config-tier.d.ts +69 -0
  104. package/dist/center-config-tier.js +42 -0
  105. package/dist/checkpoint-identity.d.ts +16 -0
  106. package/dist/checkpoint-identity.js +19 -0
  107. package/dist/config-catalog.d.ts +17 -10
  108. package/dist/config-catalog.js +47 -32
  109. package/dist/config-d-migration.d.ts +3 -1
  110. package/dist/config-d-migration.js +2 -2
  111. package/dist/config-dir-trust.d.ts +114 -0
  112. package/dist/config-dir-trust.js +388 -0
  113. package/dist/config-invariants.d.ts +18 -5
  114. package/dist/config-invariants.js +35 -3
  115. package/dist/config-lkg.d.ts +9 -9
  116. package/dist/config-lkg.js +20 -4
  117. package/dist/config-provider.d.ts +9 -2
  118. package/dist/config-provider.js +18 -6
  119. package/dist/config-types.d.ts +75 -20
  120. package/dist/config-types.js +8 -0
  121. package/dist/config.d.ts +13 -0
  122. package/dist/config.js +137 -62
  123. package/dist/delegated-session-owner.d.ts +185 -0
  124. package/dist/delegated-session-owner.js +119 -0
  125. package/dist/device-protocol.d.ts +5 -3
  126. package/dist/device-protocol.js +1 -1
  127. package/dist/device-ws-hub.js +21 -2
  128. package/dist/device-ws-protocol.d.ts +70 -6
  129. package/dist/device-ws-protocol.js +50 -22
  130. package/dist/elicitation.js +3 -2
  131. package/dist/entry-problems.d.ts +34 -0
  132. package/dist/entry-problems.js +14 -0
  133. package/dist/env-name-allowlist-knobs.d.ts +12 -2
  134. package/dist/env-name-allowlist-knobs.js +5 -4
  135. package/dist/exec-bounds.d.ts +15 -3
  136. package/dist/exec-bounds.js +4 -0
  137. package/dist/execution-lane-caps.d.ts +56 -10
  138. package/dist/execution-lane-caps.js +50 -0
  139. package/dist/field-conflict.d.ts +43 -0
  140. package/dist/field-conflict.js +20 -0
  141. package/dist/fleet/fleet-bus.d.ts +80 -6
  142. package/dist/fleet/fleet-bus.js +41 -9
  143. package/dist/hooks/classic-stations.d.ts +91 -0
  144. package/dist/hooks/classic-stations.js +27 -0
  145. package/dist/hooks/command-spawn.d.ts +57 -0
  146. package/dist/hooks/command-spawn.js +139 -0
  147. package/dist/hooks/hook-llm.d.ts +8 -5
  148. package/dist/hooks/hook-llm.js +79 -36
  149. package/dist/hooks/hook-runner.d.ts +349 -95
  150. package/dist/hooks/hook-runner.js +855 -568
  151. package/dist/hooks/hook-source.d.ts +44 -0
  152. package/dist/hooks/hook-source.js +40 -0
  153. package/dist/http/active-run-conflict.d.ts +5 -13
  154. package/dist/http/active-run-conflict.js +17 -27
  155. package/dist/http/admission.d.ts +8 -2
  156. package/dist/http/admission.js +188 -101
  157. package/dist/http/approval-sweeps.js +99 -21
  158. package/dist/http/caller-trust.d.ts +77 -0
  159. package/dist/http/caller-trust.js +143 -0
  160. package/dist/http/checkpoint-ctx.d.ts +41 -24
  161. package/dist/http/checkpoint-ctx.js +23 -16
  162. package/dist/http/claimed-run.d.ts +14 -0
  163. package/dist/http/claimed-run.js +15 -0
  164. package/dist/http/device-placement.d.ts +2 -4
  165. package/dist/http/device-placement.js +7 -11
  166. package/dist/http/dispatch.d.ts +0 -4
  167. package/dist/http/dispatch.js +15 -13
  168. package/dist/http/durable-run-leg.d.ts +6 -33
  169. package/dist/http/durable-run-leg.js +36 -10
  170. package/dist/http/health-self-report.d.ts +7 -5
  171. package/dist/http/health-self-report.js +4 -18
  172. package/dist/http/owner-access.d.ts +56 -0
  173. package/dist/http/owner-access.js +42 -0
  174. package/dist/http/principal-gate.d.ts +70 -28
  175. package/dist/http/principal-gate.js +61 -15
  176. package/dist/http/readiness.d.ts +11 -5
  177. package/dist/http/readiness.js +10 -3
  178. package/dist/http/refusal-body.d.ts +21 -0
  179. package/dist/http/refusal-body.js +4 -0
  180. package/dist/http/request-guards.d.ts +43 -23
  181. package/dist/http/request-guards.js +53 -17
  182. package/dist/http/resume-ctx-read.d.ts +2 -5
  183. package/dist/http/resume-ctx-read.js +7 -4
  184. package/dist/http/resume-legs.d.ts +28 -4
  185. package/dist/http/resume-legs.js +169 -91
  186. package/dist/http/retired-service-deps.d.ts +43 -0
  187. package/dist/http/retired-service-deps.js +18 -0
  188. package/dist/http/route-ctx.d.ts +44 -28
  189. package/dist/http/route-ctx.js +4 -0
  190. package/dist/http/route-table.js +2 -0
  191. package/dist/http/routes/a2a-serve.js +3 -4
  192. package/dist/http/routes/admin-config-refresh.js +1 -1
  193. package/dist/http/routes/admin-drain.js +1 -1
  194. package/dist/http/routes/admin-memory-optout.js +5 -1
  195. package/dist/http/routes/adoption.js +2 -9
  196. package/dist/http/routes/approvals-assistant.js +49 -48
  197. package/dist/http/routes/background.js +3 -2
  198. package/dist/http/routes/capabilities.js +21 -5
  199. package/dist/http/routes/config-catalog.d.ts +3 -3
  200. package/dist/http/routes/config-catalog.js +1 -1
  201. package/dist/http/routes/devices.js +3 -17
  202. package/dist/http/routes/diagnostics.js +6 -2
  203. package/dist/http/routes/images.js +6 -7
  204. package/dist/http/routes/leader.js +1 -8
  205. package/dist/http/routes/mcp-reconnect.d.ts +1 -1
  206. package/dist/http/routes/mcp-reconnect.js +3 -2
  207. package/dist/http/routes/memory-bundle.js +1 -1
  208. package/dist/http/routes/memory-compliance.js +1 -1
  209. package/dist/http/routes/memory-consolidation.js +1 -1
  210. package/dist/http/routes/memory-origin.js +1 -1
  211. package/dist/http/routes/memory-policy.d.ts +1 -1
  212. package/dist/http/routes/memory-policy.js +3 -2
  213. package/dist/http/routes/notify-wake.js +8 -21
  214. package/dist/http/routes/observability.js +2 -2
  215. package/dist/http/routes/principal-inbox.d.ts +39 -0
  216. package/dist/http/routes/principal-inbox.js +76 -0
  217. package/dist/http/routes/retention-ops.js +2 -9
  218. package/dist/http/routes/rules.js +5 -18
  219. package/dist/http/routes/runs.d.ts +5 -0
  220. package/dist/http/routes/runs.js +34 -107
  221. package/dist/http/routes/session-sync.js +15 -26
  222. package/dist/http/routes/sessions.js +30 -40
  223. package/dist/http/routes/shared-memory.js +2 -13
  224. package/dist/http/routes/side-query.js +8 -7
  225. package/dist/http/routes/tasks.js +67 -21
  226. package/dist/http/routes/workflows.d.ts +351 -0
  227. package/dist/http/routes/workflows.js +153 -119
  228. package/dist/http/send.d.ts +33 -10
  229. package/dist/http/send.js +21 -4
  230. package/dist/http/server.d.ts +27 -18
  231. package/dist/http/server.js +34 -6
  232. package/dist/http/stored-body-replay.js +17 -4
  233. package/dist/http/submit-ctx.d.ts +25 -0
  234. package/dist/http/submit-ctx.js +40 -0
  235. package/dist/http/submit-replay.d.ts +4 -3
  236. package/dist/http/submit-replay.js +5 -4
  237. package/dist/http/wire-types.d.ts +11 -8
  238. package/dist/index.d.ts +2 -2
  239. package/dist/index.js +1 -2
  240. package/dist/lane-home-probe.d.ts +112 -0
  241. package/dist/lane-home-probe.js +101 -0
  242. package/dist/leader/endpoint.d.ts +4 -1
  243. package/dist/leader/endpoint.js +11 -10
  244. package/dist/limit-refusal.d.ts +59 -0
  245. package/dist/limit-refusal.js +15 -0
  246. package/dist/main.js +1 -1
  247. package/dist/mcp-assertion-header.d.ts +32 -19
  248. package/dist/mcp-assertion-header.js +10 -9
  249. package/dist/mcp-tool-faces.d.ts +5 -0
  250. package/dist/mcp-tool-faces.js +6 -4
  251. package/dist/memory-project-layer.d.ts +37 -0
  252. package/dist/memory-project-layer.js +46 -0
  253. package/dist/memory-scope.d.ts +24 -17
  254. package/dist/memory-scope.js +24 -8
  255. package/dist/memory-sync-client.d.ts +27 -13
  256. package/dist/memory-sync-client.js +107 -18
  257. package/dist/model-entry-refusal.d.ts +9 -1
  258. package/dist/model-entry-refusal.js +6 -1
  259. package/dist/model-plane-face.d.ts +51 -0
  260. package/dist/model-plane-face.js +24 -0
  261. package/dist/model-provider.d.ts +44 -12
  262. package/dist/model-provider.js +21 -2
  263. package/dist/model-route-endpoint.d.ts +9 -12
  264. package/dist/model-route-endpoint.js +8 -11
  265. package/dist/model-route-env.d.ts +22 -0
  266. package/dist/model-route-env.js +2 -0
  267. package/dist/model-route-gap.d.ts +60 -30
  268. package/dist/model-route-gap.js +21 -15
  269. package/dist/observability/corrupt-read-seat.d.ts +7 -0
  270. package/dist/observability/corrupt-read-seat.js +9 -0
  271. package/dist/observability/cost-taxonomy.d.ts +7 -7
  272. package/dist/observability/err-text.d.ts +6 -0
  273. package/dist/observability/err-text.js +18 -4
  274. package/dist/observability/fail-open.d.ts +76 -14
  275. package/dist/observability/fail-open.js +76 -14
  276. package/dist/observability/metrics.js +4 -1
  277. package/dist/observability/prompt-manifest.d.ts +33 -82
  278. package/dist/observability/prompt-manifest.js +122 -17
  279. package/dist/observability/run-terminal.js +14 -6
  280. package/dist/observability/secret-env-scrub.d.ts +13 -1
  281. package/dist/observability/secret-env-scrub.js +13 -1
  282. package/dist/observability/tool-trace.d.ts +16 -21
  283. package/dist/observability/tool-trace.js +58 -49
  284. package/dist/operator-ask.d.ts +10 -0
  285. package/dist/operator-ask.js +3 -0
  286. package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
  287. package/dist/orchestration/workflow-agent-session-index.js +9 -13
  288. package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
  289. package/dist/orchestration/workflow-completion-inbox.js +13 -0
  290. package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
  291. package/dist/orchestration/workflow-notify-journal.js +27 -34
  292. package/dist/orchestration/workflow-park-decision.d.ts +89 -0
  293. package/dist/orchestration/workflow-park-decision.js +118 -0
  294. package/dist/org-delivery-gate.d.ts +81 -0
  295. package/dist/org-delivery-gate.js +59 -0
  296. package/dist/panel-tool-names.d.ts +23 -0
  297. package/dist/panel-tool-names.js +8 -0
  298. package/dist/parked-decide.d.ts +39 -6
  299. package/dist/parked-decide.js +98 -51
  300. package/dist/parked-revive-surface.d.ts +9 -2
  301. package/dist/parked-revive-surface.js +11 -8
  302. package/dist/per-task-image.d.ts +7 -1
  303. package/dist/per-task-image.js +3 -3
  304. package/dist/permission-rule-list.d.ts +1 -1
  305. package/dist/permission-rule-list.js +2 -2
  306. package/dist/plain-string-record.d.ts +25 -0
  307. package/dist/plain-string-record.js +32 -0
  308. package/dist/plugins/approval-ask-store-file.d.ts +16 -1
  309. package/dist/plugins/approval-ask-store-file.js +21 -0
  310. package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
  311. package/dist/plugins/approval-ask-store-memory.js +34 -2
  312. package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
  313. package/dist/plugins/approval-ask-store-sql.js +37 -1
  314. package/dist/plugins/blob-backend.d.ts +83 -15
  315. package/dist/plugins/blob-backend.js +68 -15
  316. package/dist/plugins/breaker-state-sql.d.ts +2 -2
  317. package/dist/plugins/breaker-state-sql.js +4 -4
  318. package/dist/plugins/caching-session-store.d.ts +3 -0
  319. package/dist/plugins/caching-session-store.js +3 -0
  320. package/dist/plugins/checkpoint-store-sql.d.ts +28 -4
  321. package/dist/plugins/checkpoint-store-sql.js +59 -28
  322. package/dist/plugins/file-history-store-sql.d.ts +5 -2
  323. package/dist/plugins/file-history-store-sql.js +3 -1
  324. package/dist/plugins/file-run-store.d.ts +5 -13
  325. package/dist/plugins/file-run-store.js +9 -0
  326. package/dist/plugins/fork-routing-session-store.d.ts +9 -1
  327. package/dist/plugins/fork-routing-session-store.js +12 -1
  328. package/dist/plugins/host-platform.d.ts +2 -0
  329. package/dist/plugins/host-platform.js +2 -1
  330. package/dist/plugins/k8s-bg-scripts.d.ts +11 -0
  331. package/dist/plugins/k8s-bg-scripts.js +22 -0
  332. package/dist/plugins/local-checkpoint-store.d.ts +2 -0
  333. package/dist/plugins/local-checkpoint-store.js +11 -3
  334. package/dist/plugins/local-session-store.d.ts +1 -1
  335. package/dist/plugins/local-session-store.js +1 -1
  336. package/dist/plugins/local-session-window.d.ts +39 -0
  337. package/dist/plugins/local-session-window.js +54 -0
  338. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
  339. package/dist/plugins/mailbox-store-sql.d.ts +30 -48
  340. package/dist/plugins/mailbox-store-sql.js +77 -52
  341. package/dist/plugins/memory-embedder-fingerprint.js +2 -1
  342. package/dist/plugins/memory-run-store.d.ts +5 -13
  343. package/dist/plugins/memory-run-store.js +8 -0
  344. package/dist/plugins/one-time-migrations.d.ts +245 -93
  345. package/dist/plugins/one-time-migrations.js +668 -207
  346. package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
  347. package/dist/plugins/org-delivery-quota-store.js +228 -0
  348. package/dist/plugins/permission-rule-store-sql.d.ts +13 -7
  349. package/dist/plugins/permission-rule-store-sql.js +4 -18
  350. package/dist/plugins/pg-pool.js +39 -7
  351. package/dist/plugins/pg-session-storage.d.ts +20 -8
  352. package/dist/plugins/pg-session-storage.js +34 -21
  353. package/dist/plugins/remote-env-adb.d.ts +12 -12
  354. package/dist/plugins/remote-env-adb.js +21 -6
  355. package/dist/plugins/remote-env-device.js +3 -0
  356. package/dist/plugins/remote-env-e2b.d.ts +1 -1
  357. package/dist/plugins/remote-env-e2b.js +21 -9
  358. package/dist/plugins/remote-env-host.d.ts +9 -9
  359. package/dist/plugins/remote-env-host.js +10 -32
  360. package/dist/plugins/remote-env-k8s.d.ts +103 -38
  361. package/dist/plugins/remote-env-k8s.js +288 -95
  362. package/dist/plugins/remote-env-local-docker.d.ts +27 -13
  363. package/dist/plugins/remote-env-local-docker.js +82 -11
  364. package/dist/plugins/remote-env-ssh.d.ts +21 -12
  365. package/dist/plugins/remote-env-ssh.js +117 -5
  366. package/dist/plugins/remote-shell.d.ts +0 -1
  367. package/dist/plugins/remote-shell.js +1 -2
  368. package/dist/plugins/retention-store-sql.js +6 -2
  369. package/dist/plugins/run-store-sql.d.ts +16 -14
  370. package/dist/plugins/run-store-sql.js +22 -2
  371. package/dist/plugins/schema-consistency.d.ts +54 -0
  372. package/dist/plugins/schema-consistency.js +52 -0
  373. package/dist/plugins/schema-indexes.js +2 -0
  374. package/dist/plugins/send-user-file.js +2 -2
  375. package/dist/plugins/session-epoch-anchor.d.ts +39 -0
  376. package/dist/plugins/session-epoch-anchor.js +22 -0
  377. package/dist/plugins/session-store.d.ts +18 -8
  378. package/dist/plugins/session-store.js +15 -4
  379. package/dist/plugins/shared-memory-store-sql.d.ts +9 -3
  380. package/dist/plugins/shared-memory-store-sql.js +5 -16
  381. package/dist/plugins/sql-driver.d.ts +205 -1
  382. package/dist/plugins/sql-driver.js +325 -13
  383. package/dist/plugins/sql-errors.d.ts +64 -0
  384. package/dist/plugins/sql-errors.js +57 -0
  385. package/dist/plugins/store-backend.d.ts +36 -1
  386. package/dist/plugins/store-backend.js +42 -15
  387. package/dist/plugins/store-contracts.d.ts +42 -0
  388. package/dist/plugins/store-contracts.js +1 -0
  389. package/dist/plugins/tidb-pool.d.ts +6 -1
  390. package/dist/plugins/tidb-pool.js +44 -10
  391. package/dist/plugins/tidb-session-storage.d.ts +8 -11
  392. package/dist/plugins/tidb-session-storage.js +6 -16
  393. package/dist/plugins/tidb-session-store.d.ts +24 -4
  394. package/dist/plugins/tidb-session-store.js +29 -5
  395. package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
  396. package/dist/plugins/workflow-journal-store-sql.js +19 -6
  397. package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
  398. package/dist/plugins/workflow-run-store-sql.js +50 -9
  399. package/dist/posture-source.d.ts +18 -10
  400. package/dist/posture-source.js +4 -1
  401. package/dist/project-identity.d.ts +45 -6
  402. package/dist/project-identity.js +162 -8
  403. package/dist/project-layer-key.d.ts +28 -0
  404. package/dist/project-layer-key.js +29 -0
  405. package/dist/prompts-domain-validate.d.ts +17 -61
  406. package/dist/prompts-domain-validate.js +9 -119
  407. package/dist/question.js +3 -2
  408. package/dist/read-deny-entry.d.ts +25 -0
  409. package/dist/read-deny-entry.js +12 -0
  410. package/dist/read-face-posture.d.ts +37 -11
  411. package/dist/read-face-posture.js +32 -3
  412. package/dist/request-key-closure.d.ts +9 -2
  413. package/dist/request-key-closure.js +11 -5
  414. package/dist/request-value-closure.d.ts +5 -0
  415. package/dist/request-value-closure.js +11 -3
  416. package/dist/rule-entry-list.d.ts +38 -11
  417. package/dist/rule-entry-list.js +106 -30
  418. package/dist/rules-consent.js +3 -4
  419. package/dist/run-acceptance.d.ts +19 -3
  420. package/dist/run-acceptance.js +13 -1
  421. package/dist/run-local.js +54 -24
  422. package/dist/runs.d.ts +15 -8
  423. package/dist/runs.js +35 -1
  424. package/dist/runtime-caps-resolver.d.ts +16 -6
  425. package/dist/runtime-caps-resolver.js +18 -5
  426. package/dist/sandbox-pkg-source.d.ts +19 -1
  427. package/dist/sandbox-pkg-source.js +29 -7
  428. package/dist/security.d.ts +87 -9
  429. package/dist/security.js +52 -9
  430. package/dist/server-secret-env.d.ts +44 -18
  431. package/dist/server-secret-env.js +49 -8
  432. package/dist/session-error-wire.d.ts +19 -2
  433. package/dist/session-error-wire.js +5 -1
  434. package/dist/session-incarnation.d.ts +15 -5
  435. package/dist/session-incarnation.js +16 -9
  436. package/dist/session-titled-frame.d.ts +33 -0
  437. package/dist/session-titled-frame.js +4 -0
  438. package/dist/session-titler.d.ts +9 -1
  439. package/dist/session-titler.js +27 -6
  440. package/dist/spec-fields.d.ts +28 -16
  441. package/dist/spec-fields.js +66 -54
  442. package/dist/sql-engine-posture.d.ts +31 -4
  443. package/dist/sql-engine-posture.js +36 -1
  444. package/dist/stored-request-tool-axes-m19.d.ts +19 -4
  445. package/dist/stored-request-tool-axes-m19.js +30 -2
  446. package/dist/task-a2a.d.ts +9 -1
  447. package/dist/task-a2a.js +73 -47
  448. package/dist/task-cwd.d.ts +1 -1
  449. package/dist/task-mcp.d.ts +46 -58
  450. package/dist/task-mcp.js +82 -107
  451. package/dist/task-settings.d.ts +95 -108
  452. package/dist/task-settings.js +77 -98
  453. package/dist/text-bidi.d.ts +1 -48
  454. package/dist/text-bidi.js +3 -5
  455. package/dist/tighten-only-seats.d.ts +150 -0
  456. package/dist/tighten-only-seats.js +196 -0
  457. package/dist/tool-approval.d.ts +304 -88
  458. package/dist/tool-approval.js +623 -239
  459. package/dist/trace/core-keyset-guard.d.ts +122 -20
  460. package/dist/trace/engine-notice-wire.d.ts +11 -3
  461. package/dist/trace/engine-notice-wire.js +8 -1
  462. package/dist/trace/exit-seat.d.ts +28 -0
  463. package/dist/trace/exit-seat.js +76 -0
  464. package/dist/trace/ledger-events.d.ts +6 -3
  465. package/dist/trace/ledger-sink.js +4 -1
  466. package/dist/trace/model-segment.js +1 -1
  467. package/dist/trace/project.d.ts +98 -17
  468. package/dist/trace/project.js +98 -50
  469. package/dist/trace/projection-table.d.ts +100 -0
  470. package/dist/trace/projection-table.js +130 -0
  471. package/dist/trace/redact.d.ts +54 -0
  472. package/dist/trace/redact.js +778 -152
  473. package/dist/trace/self-minted-wire.d.ts +345 -1
  474. package/dist/trace/self-minted-wire.js +117 -1
  475. package/dist/trace/wire-projection-faces.d.ts +377 -3
  476. package/dist/trace/wire-projection-faces.js +164 -1
  477. package/dist/url-refusal.js +2 -0
  478. package/dist/web-admin-client/apply-effective.d.ts +12 -0
  479. package/dist/web-admin-client/apply-effective.js +56 -5
  480. package/dist/web-admin-client/candidate-gate.d.ts +6 -6
  481. package/dist/web-admin-client/candidate-gate.js +3 -3
  482. package/dist/web-admin-client/facade.d.ts +1 -1
  483. package/dist/web-admin-client/facade.js +1 -1
  484. package/dist/web-admin-client/http-client.js +0 -6
  485. package/dist/web-admin-client/read-face.js +5 -9
  486. package/dist/web-admin-client/read-warnings.d.ts +13 -0
  487. package/dist/web-admin-client/read-warnings.js +10 -0
  488. package/dist/web-admin-client/skills-mcp.d.ts +3 -0
  489. package/dist/web-admin-client/skills-mcp.js +11 -9
  490. package/dist/web-admin-client/types.d.ts +2 -3
  491. package/dist/workspace-cache.d.ts +35 -0
  492. package/dist/workspace-cache.js +36 -0
  493. package/dist/workspace-space.d.ts +3 -2
  494. package/dist/workspace-space.js +2 -0
  495. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +65 -10
  496. package/package.json +8 -4
  497. package/scripts/drain-check-local.mjs +34 -1
  498. package/scripts/drain-check.mysql.sql +30 -2
  499. package/scripts/drain-check.pg.sql +29 -2
  500. package/dist/capabilities/memory-notice.d.ts +0 -138
  501. package/dist/capabilities/memory-notice.js +0 -64
  502. package/dist/capabilities/prompt.d.ts +0 -17
  503. package/dist/capabilities/prompt.js +0 -6
  504. package/dist/mcp-content-origin.d.ts +0 -34
  505. package/dist/mcp-content-origin.js +0 -10
  506. package/dist/tool-axis-words.d.ts +0 -27
  507. package/dist/tool-axis-words.js +0 -9
  508. package/dist/wire-name-bound.d.ts +0 -14
  509. package/dist/wire-name-bound.js +0 -13
@@ -64,7 +64,27 @@ export function mutateInPlace(target, source) {
64
64
  Object.assign(target, source);
65
65
  }
66
66
  const appliedGeneration = new WeakMap();
67
- const envRolesBaseline = new WeakMap();
67
+ const envModelPlane = new WeakMap();
68
+ function envModelPlaneOf(config) {
69
+ let snap = envModelPlane.get(config);
70
+ if (snap === undefined) {
71
+ snap = {
72
+ models: { ...config.models },
73
+ model: config.envModel,
74
+ modelApiKeyEnv: { ...config.modelApiKeyEnv },
75
+ modelApiKeys: { ...config.modelApiKeys },
76
+ modelRefusals: { ...config.modelRefusals },
77
+ droppedCatalogEntries: [...config.droppedCatalogEntries],
78
+ quotaWeights: { ...config.modelQuotaWeights },
79
+ roles: { ...config.roles },
80
+ activeTiers: { ...config.tiers },
81
+ atMention: config.atMention,
82
+ infoLine: {},
83
+ };
84
+ envModelPlane.set(config, snap);
85
+ }
86
+ return snap;
87
+ }
68
88
  function stageAtMention(raw, usableNames, logger) {
69
89
  const read = readAtMention(raw);
70
90
  if (read === undefined) {
@@ -172,7 +192,7 @@ function commitStaged(config, staged) {
172
192
  config.droppedCatalogEntries = mp.droppedCatalogEntries;
173
193
  mutateInPlace(config.modelQuotaWeights, mp.quotaWeights);
174
194
  mutateInPlace(config.models, mp.models);
175
- config.model = config.models.default;
195
+ config.model = mp.model;
176
196
  mutateInPlace(config.roles, mp.roles);
177
197
  mutateInPlace(config.tiers, mp.activeTiers);
178
198
  config.atMention = mp.atMention;
@@ -227,7 +247,38 @@ function modelPlaneGroupReport(group) {
227
247
  };
228
248
  }
229
249
  const ROLE_BINDING_UNAVAILABLE_HINT = "tasks resolving this role fail: alias/tier expansion cannot mint this name; the engine refuses it loud — bind the role to a usable model, or make the entry usable (see the entry's own warn)";
250
+ export const NO_CENTER_EFFECTIVE = deepFreeze({ version: 0, updatedAt: "", models: { models: [], roles: {} }, mcp: { servers: [] } });
251
+ export const isNoCenterEffective = (eff) => eff === NO_CENTER_EFFECTIVE;
252
+ function deepFreeze(v) {
253
+ if (v !== null && typeof v === "object") {
254
+ for (const x of Object.values(v))
255
+ deepFreeze(x);
256
+ Object.freeze(v);
257
+ }
258
+ return v;
259
+ }
260
+ function stageEnvModelPlane(config) {
261
+ if (servedModelTableLeg(config) === "env")
262
+ return { stages: {} };
263
+ const snap = envModelPlane.get(config);
264
+ if (snap === undefined)
265
+ throw new Error("applyEffective: the center model plane is in force but no env snapshot was taken — the model plane was written outside applyEffective, so the no-center tier has no baseline to return to");
266
+ return {
267
+ stages: {
268
+ models: {
269
+ ...snap,
270
+ modelApiKeyEnv: { ...snap.modelApiKeyEnv },
271
+ modelApiKeys: { ...snap.modelApiKeys },
272
+ modelRefusals: { ...snap.modelRefusals },
273
+ droppedCatalogEntries: [...snap.droppedCatalogEntries],
274
+ infoLine: { source: "no-center-baseline", default: snap.model.id, roles: Object.keys(snap.roles), tiers: [], atMention: snap.atMention.mode },
275
+ },
276
+ },
277
+ };
278
+ }
230
279
  function stageModelPlane(config, eff, logger, sealedKeys) {
280
+ if (isNoCenterEffective(eff))
281
+ return stageEnvModelPlane(config);
231
282
  const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
232
283
  const verdict = judgeCatalogEntries(enabled, { anthropicRouteMounted: config.anthropic !== undefined }, eff.models?.droppedCatalogEntries);
233
284
  if (enabled.length === 0 && verdict.malformed.length === 0 && verdict.dropped.every((row) => row.identity?.enabled === false))
@@ -293,8 +344,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
293
344
  quotaWeights[m.id] = qw;
294
345
  }
295
346
  }
296
- if (!envRolesBaseline.has(config))
297
- envRolesBaseline.set(config, { ...config.roles });
347
+ const envPlane = envModelPlaneOf(config);
298
348
  const centerRoles = {};
299
349
  for (const [role, tgt] of Object.entries(eff.models.roles ?? {})) {
300
350
  if ("model" in tgt)
@@ -302,7 +352,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
302
352
  else if ("select" in tgt)
303
353
  centerRoles[role] = { select: tgt.select };
304
354
  }
305
- const roles = { ...envRolesBaseline.get(config), ...centerRoles };
355
+ const roles = { ...envPlane.roles, ...centerRoles };
306
356
  const activeTiers = {};
307
357
  for (const [tier, target] of Object.entries(resolveActiveTiers(eff.models) ?? {})) {
308
358
  if (models[target] !== undefined) {
@@ -365,6 +415,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
365
415
  const defaultModel = models.default;
366
416
  const plane = {
367
417
  models,
418
+ model: defaultModel,
368
419
  modelApiKeyEnv,
369
420
  modelApiKeys,
370
421
  modelRefusals: { ...verdict.refused },
@@ -2,7 +2,7 @@
2
2
  * 配置候选的**唯一判官**:一份候选(中心有效视图,或本地 `config.d` 读出的那份)能不能收 —— 只有这一种判法。
3
3
  *
4
4
  * 判据(三件,任一即拒;每条拒因带成因词,闭集 {@link CandidateRejectReason}):读腿交出的 `domainErrors`(某个域读不懂 / 拼错键 / 坏文件:那个域已落
5
- * schema 缺省)与 prompts 面的判形(`validatePromptsDomain`;坏形的 pack 会让 core 在每个任务的装配期抛)—— 成因 `domain_invalid`;读腿交出的
5
+ * schema 缺省)与 prompts 面的判官(`judgePromptsWire`:契约包的 wire 形再交引擎 verify —— 中心层只有一种形)—— 成因 `domain_invalid`;读腿交出的
6
6
  * `retiredDomains`(带事实的退役域文档,契约包的墓碑;拒句点名退役版本与一次性迁移)—— 成因 `retired_domain`。任一 ⇒ `rejected`:
7
7
  * **整份候选不收**,一个域都不应用。退役域此前在读腿整包抛、走「源不可达」那条臂(版本读不到、没有拒判行、刷新拍算失败);现在读腿只交数据,
8
8
  * 坏候选只有本判官一种判法。
@@ -22,11 +22,11 @@
22
22
  * `domains` 排序去重:收集器的顺序没有承诺,它是拒行去重键的一部分(同一份候选、同一组坏域只响一次)。`errors` 原样逐条
23
23
  * (读腿的诊断在前、prompts 在后)—— 那是可操作的诊断,不是键。纯函数:不改入参,不读环境。
24
24
  *
25
- * **判官交值**:`clean` 判决带着 prompts 面的已判形值(`prompts`;候选不带 prompts 键 ⇒ `undefined`)。采纳 prompts 的各处(起服 / 刷新 /
26
- * 迟到起服 / LKG 起服 / `run-local`)直接用这一份值,不再自己判第二遍 —— `validatePromptsDomain` 在 src 里只有本判官一个调用点
25
+ * **判官交值**:`clean` 判决带着 prompts 面的判官结论(`prompts` = 引擎 verify 过的发布形 artifact;候选不带 prompts 键 ⇒ `undefined`)。采纳 prompts 的各处
26
+ * (起服 / 刷新 / 迟到起服 / LKG 起服 / `run-local`)直接用这一份值,不再自己判第二遍 —— `judgePromptsWire` 在 src 里只有本判官一个调用点
27
27
  * (盘上 LKG 的读回复判也经本判官,`config-lkg.ts`)。
28
28
  */
29
- import { type PromptsDomainFaces } from "../prompts-domain-validate.js";
29
+ import type { PublishedPromptArtifact } from "@sema-agent/core";
30
30
  import type { EffectiveConfig } from "./types.js";
31
31
  /** 读腿交出的一条域级诊断(与 `FetchEffectiveResult.domainErrors` 同形;error 文案已在产出边界脱敏)。 */
32
32
  export interface CandidateDomainError {
@@ -46,10 +46,10 @@ export interface ConfigCandidateRead {
46
46
  readonly retiredDomains?: readonly CandidateDomainError[];
47
47
  }
48
48
  export type ConfigCandidateVerdict =
49
- /** `prompts` = 候选 prompts 面的已判形值(候选不带 prompts 键 ⇒ `undefined` = 撤回中心包、内置提示词服务)。 */
49
+ /** `prompts` = 候选的中心层(引擎 verify 过的发布形 artifact;候选不带 prompts 键 ⇒ `undefined` = 撤下中心层、内置提示词服务)。 */
50
50
  {
51
51
  readonly verdict: "clean";
52
- readonly prompts: PromptsDomainFaces | undefined;
52
+ readonly prompts: PublishedPromptArtifact | undefined;
53
53
  } | {
54
54
  readonly verdict: "rejected";
55
55
  readonly domains: readonly string[];
@@ -1,14 +1,14 @@
1
- import { validatePromptsDomain } from "../prompts-domain-validate.js";
1
+ import { judgePromptsWire } from "../prompts-domain-validate.js";
2
2
  export function judgeConfigCandidate(r) {
3
3
  const prompts = r.effective.prompts;
4
- const promptsGate = prompts === undefined ? undefined : validatePromptsDomain(prompts);
4
+ const promptsGate = prompts === undefined ? undefined : judgePromptsWire(prompts);
5
5
  const errors = [
6
6
  ...(r.domainErrors ?? []).map((de) => ({ domain: de.domain, error: de.error, reason: "domain_invalid" })),
7
7
  ...(promptsGate !== undefined && !promptsGate.ok ? [{ domain: "prompts", error: promptsGate.error, reason: "domain_invalid" }] : []),
8
8
  ...(r.retiredDomains ?? []).map((rd) => ({ domain: rd.domain, error: rd.error, reason: "retired_domain" })),
9
9
  ];
10
10
  if (errors.length === 0)
11
- return { verdict: "clean", prompts: promptsGate?.ok ? promptsGate.value : undefined };
11
+ return { verdict: "clean", prompts: promptsGate?.ok ? promptsGate.artifact : undefined };
12
12
  return { verdict: "rejected", domains: [...new Set(errors.map((e) => e.domain))].sort(), errors };
13
13
  }
14
14
  export function buildCandidateRejectedFields(r, verdict, leg, ctx) {
@@ -48,7 +48,7 @@
48
48
  * importers to update, which was done in the same commit.
49
49
  */
50
50
  export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, WebAdminHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
51
- export { mutateInPlace, applyEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
51
+ export { mutateInPlace, applyEffective, NO_CENTER_EFFECTIVE, isNoCenterEffective, type ApplyReport, applyRuntimeGates, applyRuntimeHot, logEffectiveDiff, MODEL_PLANE_DOMAINS, type ModelPlaneDomain, type ModelPlaneOf, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
52
52
  export { restartReasons, modelPlaneChanged, type RestartSlice, type RestartSliceCtx, type EngineRestartSignal, } from "./restart-signal.js";
53
53
  export { applySkillsManifest, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
54
54
  export { applyCenterReadFace, buildReadFaceBaseline, buildReadFaceSwapCandidate, restoreReadFaceBaseline, CENTER_READ_FACE_LIVE_KEYS, type CenterReadFaceApply, type CenterReadFaceReport, type ReadFaceBaseline, type ReadFaceSeat } from "./read-face.js";
@@ -1,5 +1,5 @@
1
1
  export { fetchEffective, fetchPrincipalCaps, fetchPrincipalOrgMemory, WebAdminHttpError, fetchSkillContent, fetchPromptArtifact, fetchPromptBlob, } from "./http-client.js";
2
- export { mutateInPlace, applyEffective, applyRuntimeGates, applyRuntimeHot, logEffectiveDiff, MODEL_PLANE_DOMAINS, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
2
+ export { mutateInPlace, applyEffective, NO_CENTER_EFFECTIVE, isNoCenterEffective, applyRuntimeGates, applyRuntimeHot, logEffectiveDiff, MODEL_PLANE_DOMAINS, withModelPlaneOf, fullyLandedForLkg, } from "./apply-effective.js";
3
3
  export { restartReasons, modelPlaneChanged, } from "./restart-signal.js";
4
4
  export { applySkillsManifest, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./skills-mcp.js";
5
5
  export { applyCenterReadFace, buildReadFaceBaseline, buildReadFaceSwapCandidate, restoreReadFaceBaseline, CENTER_READ_FACE_LIVE_KEYS } from "./read-face.js";
@@ -1,6 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { readEffectiveWire, PrincipalCapsWire } from "@sema-agent/settings-schema";
3
- import { centerPromptsFromEffective } from "../prompts-domain-validate.js";
4
3
  import { separateRetiredDomains, splitFoldedReadWarnings, withDroppedCatalogEntries } from "./read-warnings.js";
5
4
  import { CENTER_PULL_TIMEOUT_MS, centerFetch } from "./center-request.js";
6
5
  import { answerStillCurrent } from "../center-credential.js";
@@ -13,11 +12,6 @@ export async function fetchEffective(center, etag, fetchImpl = fetch, worker) {
13
12
  const warnings = [];
14
13
  const { body, retiredDomains } = separateRetiredDomains(await res.json());
15
14
  const wire = readEffectiveWire(body, (w) => warnings.push(w));
16
- const centerPrompts = centerPromptsFromEffective(wire);
17
- if (centerPrompts === undefined)
18
- delete wire.prompts;
19
- else
20
- wire.prompts = centerPrompts;
21
15
  const { domainErrors, droppedEntries, droppedCatalogEntries, unreadFiles } = splitFoldedReadWarnings(warnings, "center");
22
16
  const effective = withDroppedCatalogEntries(wire, droppedCatalogEntries);
23
17
  return {
@@ -1,4 +1,5 @@
1
1
  import { compileReadDeny, isSwappableDepSeat, resolveReadDenyBuiltins } from "@sema-agent/core";
2
+ import { readReadDenyEntry } from "../read-deny-entry.js";
2
3
  export function buildReadFaceBaseline(config) {
3
4
  return {
4
5
  ...(config.readFace !== undefined ? { readFace: config.readFace } : {}),
@@ -44,15 +45,10 @@ function normalizeDenyPatterns(raw) {
44
45
  if (!Array.isArray(raw))
45
46
  throw new ReadFaceDomainError("readFace.denyPatterns must be an array of \"pattern\" strings or {pattern, caseSensitive?} objects");
46
47
  return raw.map((it, i) => {
47
- if (typeof it === "string")
48
- return { pattern: it, caseSensitive: false };
49
- if (typeof it !== "object" || it === null || typeof it.pattern !== "string") {
50
- throw new ReadFaceDomainError(`readFace.denyPatterns[${i}] must be a string or an object with a string 'pattern'`);
51
- }
52
- const cs = it.caseSensitive;
53
- if (cs !== undefined && typeof cs !== "boolean")
54
- throw new ReadFaceDomainError(`readFace.denyPatterns[${i}].caseSensitive must be boolean when present`);
55
- return { pattern: it.pattern, caseSensitive: cs === true };
48
+ const read = readReadDenyEntry(it);
49
+ if (!read.ok)
50
+ throw new ReadFaceDomainError(`readFace.denyPatterns[${i}]${read.after}`);
51
+ return read.entry;
56
52
  });
57
53
  }
58
54
  function assertFace(raw) {
@@ -70,6 +70,19 @@ export type UnreadConfigFile = {
70
70
  readonly why: "not-a-domain" | "non-portable-domain";
71
71
  readonly domain?: string;
72
72
  };
73
+ /**
74
+ * `config.d` 里一份**被读**的文件中、本地读腿结构上**不读**的一键(本服务铸,不是契约告警):今天只有 `prompts.json` 的 `workers` —— 本地腿没有
75
+ * worker 身份,中心层的唯一选择读法(`promptArtifactFor`)对它恒以 `null` 读,只交 `artifact`。写了不生效要响:调用方在采纳路径上一键一行 `config_unread_key`
76
+ * (与 `config_unread_file` 同频:起服与之后每次采纳一份变了的候选),不判拒。
77
+ */
78
+ export type UnreadConfigKey = {
79
+ readonly file: string;
80
+ readonly domain: string;
81
+ readonly key: string;
82
+ readonly names: readonly string[];
83
+ };
84
+ /** `config_unread_key` 一行的体(纯数据;起服 / 刷新 / 迟到起服与 `run-local` 同一体):`dir` = 本地读腿的配置根。 */
85
+ export declare function buildUnreadConfigKeyFields(k: UnreadConfigKey, dir: string | undefined): Record<string, unknown>;
73
86
  /** `config_unread_file` 一行的体(纯数据;起服 / 刷新 / 迟到起服与 `run-local` 同一体):`dir` = 本地读腿的配置根。 */
74
87
  export declare function buildUnreadConfigFileFields(f: UnreadConfigFile, dir: string | undefined): Record<string, unknown>;
75
88
  /** 逐条丢弃面的一行(两条读腿的 `droppedEntries` 同形,`config_entry_dropped` 日志行读它):`entry` = 被丢那条的出门形坐标(`models.1`),
@@ -22,6 +22,16 @@ export function separateRetiredDomains(body) {
22
22
  }
23
23
  return findings.length === 0 ? { body, retiredDomains: [] } : { body: rest, retiredDomains: findings };
24
24
  }
25
+ export function buildUnreadConfigKeyFields(k, dir) {
26
+ return {
27
+ ...(dir !== undefined ? { dir } : {}),
28
+ file: k.file,
29
+ domain: k.domain,
30
+ key: k.key,
31
+ names: k.names,
32
+ note: "the local config read has no worker identity, so this key is never read — prompts.json `workers` entries are not delivered, only `artifact` is; put the artifact this engine should run under `artifact` (or remove the key if it is meant for the center)",
33
+ };
34
+ }
25
35
  export function buildUnreadConfigFileFields(f, dir) {
26
36
  return {
27
37
  ...(dir !== undefined ? { dir } : {}),
@@ -18,6 +18,9 @@ export type SkillBodySource = Pick<ConfigProvider, "fetchSkillContent" | "skillB
18
18
  * hash, deduped in memory (same hash read once). A skill whose body is absent at the source or fails to read (incl. the
19
19
  * remote transport's hash check) is NOT installed — exactly one `web_admin_skill_failed` warn names it (`reason`:
20
20
  * `body_absent` | `read_failed`), the baseline of that name (if any) stays, and boot is never blocked.
21
+ * 🔴 **「正文在场」只有一条判据:非空串**({@link skillBodyPresent})。配置源答 `undefined`(没有这个 hash)与答 `""`(空正文)同为缺席 ⇒ `body_absent`:
22
+ * 一条空正文的技能是**惰性无提示**(名册里有它、模型调用回空、零行日志),不是「装了个什么都不说的技能」。本地腿尤其:settings-schema 把缺省的 `content`
23
+ * 补成 `""`,本地配置源因此**永远答得出**(空串)—— 没有这一条,`body_absent` 臂只在中心形上可达。本地 / 中心 / 盘缓存三个来源同过这一只判据。
21
24
  * Restart-to-apply: this runs at boot, before `buildScenarios` bakes skills into scenarios.
22
25
  */
23
26
  export declare function applySkillsManifest(baseline: LoadedSkill[], manifest: {
@@ -2,10 +2,12 @@ import { readMcpSourceLabel } from "../mcp-source-label.js";
2
2
  import { readMcpToolFaces } from "../mcp-tool-faces.js";
3
3
  import { skillContentHash } from "@sema-agent/settings-schema";
4
4
  import { readMcpToolAxes } from "@sema-agent/core";
5
- import { CONTENT_ORIGIN_WORDS, describeBadContentOrigin, isContentOriginWord } from "../mcp-content-origin.js";
6
- import { noteMcpAssertionHeaderNotSent } from "../mcp-assertion-header.js";
5
+ import { MCP_CONTENT_ORIGINS, describeBadContentOrigin, describeBadWireName, isContentOriginWord, isUsableWireName } from "@sema-agent/settings-schema";
6
+ import { noteMcpAssertionHeaderNotSent, sentMcpAssertionHeaderMember, withholdsMcpAssertionHeader } from "../mcp-assertion-header.js";
7
7
  import { SHA256_HEX_RE } from "../digest-form.js";
8
- import { describeBadWireName, isUsableWireName } from "../wire-name-bound.js";
8
+ function skillBodyPresent(body) {
9
+ return body !== undefined && body !== "";
10
+ }
9
11
  export async function applySkillsManifest(baseline, manifest, bodies, logger) {
10
12
  const diskCacheDir = bodies.skillBodyCacheDir;
11
13
  const { promises: fsp } = await import("node:fs");
@@ -19,7 +21,7 @@ export async function applySkillsManifest(baseline, manifest, bodies, logger) {
19
21
  try {
20
22
  const text = await fsp.readFile(joinPath(diskCacheDir, hex), "utf8");
21
23
  if (skillContentHash(text) === hash)
22
- return text;
24
+ return skillBodyPresent(text) ? text : undefined;
23
25
  logger?.warn("web_admin_skill_cache_corrupt", { hash, note: "on-disk body fails its own hash — ignored, re-reading from the config source" });
24
26
  }
25
27
  catch {
@@ -53,8 +55,8 @@ export async function applySkillsManifest(baseline, manifest, bodies, logger) {
53
55
  content = await diskRead(m.contentHash);
54
56
  if (content === undefined) {
55
57
  const answered = await bodies.fetchSkillContent(m.contentHash);
56
- if (answered === undefined) {
57
- logger?.warn("web_admin_skill_failed", { name: m.name, hash: m.contentHash, reason: "body_absent", note: "the config source holds no body for this hash — this entry is NOT installed; the baseline skill of this name (if any) stays" });
58
+ if (!skillBodyPresent(answered)) {
59
+ logger?.warn("web_admin_skill_failed", { name: m.name, hash: m.contentHash, reason: "body_absent", note: "the config source holds no body for this hash (absent, or an empty string) — this entry is NOT installed; the baseline skill of this name (if any) stays" });
58
60
  continue;
59
61
  }
60
62
  content = answered;
@@ -109,7 +111,7 @@ export function resolveMcpServers(mcp, logger) {
109
111
  logger?.warn("web_admin_mcp_content_class_invalid", {
110
112
  server: s.name,
111
113
  value: describeBadContentOrigin(s.contentOrigin),
112
- allowed: CONTENT_ORIGIN_WORDS,
114
+ allowed: MCP_CONTENT_ORIGINS,
113
115
  note: "declaration DROPPED — this server's tools keep the pre-378 default (external content, session gets marked). Fix the center entry; the server itself still mounts.",
114
116
  });
115
117
  }
@@ -147,11 +149,11 @@ export function resolveMcpServers(mcp, logger) {
147
149
  const headers = resolveRefs(s.transport.headerRefs, s.name, "header", logger);
148
150
  if (headers === null)
149
151
  continue;
150
- if (s.transport.assertionHeader !== undefined)
152
+ if (withholdsMcpAssertionHeader(s.transport.assertionHeader))
151
153
  noteMcpAssertionHeaderNotSent(logger, s.name);
152
154
  out.push({
153
155
  scenarios: s.scenarios ?? [],
154
- spec: { name: s.name, transport: { kind: "http", url: s.transport.url, ...(Object.keys(headers).length ? { headers } : {}), ...(s.transport.principalHeader ? { principalHeader: s.transport.principalHeader } : {}) }, ...allow, ...elicit, ...contentOrigin, ...source, ...toolFaces, ...toolAxes },
156
+ spec: { name: s.name, transport: { kind: "http", url: s.transport.url, ...(Object.keys(headers).length ? { headers } : {}), ...(s.transport.principalHeader ? { principalHeader: s.transport.principalHeader } : {}), ...sentMcpAssertionHeaderMember(s.transport.assertionHeader) }, ...allow, ...elicit, ...contentOrigin, ...source, ...toolFaces, ...toolAxes },
155
157
  });
156
158
  }
157
159
  }
@@ -371,9 +371,8 @@ export interface EffectiveConfig {
371
371
  * 这条腿对本部署不存在,四键仍由 env / 引擎默认独占。restart-to-apply(boot 期被 `createRunnerDeps`
372
372
  * 捕获),改动经 `read-face` restart 片通告。 */
373
373
  readFace?: CenterReadFace;
374
- /** prompts 域(additive key;双轴 wire=registry-core `EffectivePromptsWire` 或 legacy 平铺形——
375
- * server 侧判形/采用全在 capabilities/center-prompts.ts `validatePromptsDomain`,本 interface 只
376
- * 声明键位存在(unknown:候选门是唯一解释者,这里不复刻形)。 */
374
+ /** prompts 域(中心层;wire 形只有一种 = 契约包 `EffectivePromptsWire` `{ schemaVersion: 2, artifact }`)—— 判法只在候选判官一处
375
+ * (`prompts-domain-validate.ts` 的 `judgePromptsWire`,经 `judgeConfigCandidate`),本 interface 只声明键位存在(unknown:判官是唯一解释者,这里不复刻形)。 */
377
376
  prompts?: unknown;
378
377
  }
379
378
  /** (stage7 P5,registry-core 0.8 `execution` 域): the center-resolved per-principal execution
@@ -39,6 +39,41 @@ export declare function workspaceCacheClaimOf(spaceKey: string): {
39
39
  name: string;
40
40
  label: string;
41
41
  };
42
+ /**
43
+ * The directories of the sandbox image the cache volume must never hide — a CLOSED set of path prefixes: a mount that is one of them,
44
+ * sits under one, or is an ancestor of one (a volume mounted there shadows everything the image keeps at that path) refuses boot.
45
+ * - the system roots every image has (`/bin` `/boot` `/dev` `/etc` `/lib` `/lib64` `/proc` `/root` `/run` `/sbin` `/sys` `/usr` `/var`);
46
+ * - `/tmp` — the adapter's own scratch (the snapshot archive, the background-job directories);
47
+ * - `/opt/sema` — the image's package-source hook the pod start runs;
48
+ * - `/home/sandbox` — the sandbox user's home in the images this lane ships.
49
+ * One reading: the workspace mount is the set's one dynamic member (`K8S_MOUNT_PATH`); config reads both through {@link mountsOverlap}.
50
+ */
51
+ export declare const WORKSPACE_CACHE_MOUNT_RESERVED: readonly ["/bin", "/boot", "/dev", "/etc", "/home/sandbox", "/lib", "/lib64", "/opt/sema", "/proc", "/root", "/run", "/sbin", "/sys", "/tmp", "/usr", "/var"];
52
+ /** The reserved directory a cache mount collides with (at / under / above it), or `undefined`. `mount` is absolute and normalized. */
53
+ export declare function reservedMountCollision(mount: string): (typeof WORKSPACE_CACHE_MOUNT_RESERVED)[number] | undefined;
54
+ /** The most spaces a {@link CacheMissMemory} holds at once (a process serving more spaces than this forgets the oldest first). */
55
+ export declare const CACHE_MISS_MEMORY_CAP = 1024;
56
+ /** A cache volume that could not be had for a pod and cost a wait: its claim did not bind in time, or it bound and the pod carrying it did not become ready. */
57
+ export interface CacheMissRecord {
58
+ readonly reason: "claim_unbound" | "claim_unmountable";
59
+ readonly detail: string;
60
+ }
61
+ /**
62
+ * The process's memory of cache volumes that could not be had — per space, for a window the caller states: a run that waited out a volume the cluster could not
63
+ * bind says so here, and the next runs of that space inside the window start without it at once (no claim request, no pod waiting on it) instead of each paying the
64
+ * whole wait again. A cache is regenerable, so "not asked for a while" costs only builds that re-download. Keyed by the claim's one-way digest (never the space key
65
+ * itself); bounded ({@link CACHE_MISS_MEMORY_CAP}: expired entries go first, then the oldest). One per deployment, held by the per-run binding — never process-global.
66
+ */
67
+ export interface CacheMissMemory {
68
+ /** `spaceKey`'s volume could not be had: remembered for `windowMs` from now. */
69
+ remember(spaceKey: string, miss: CacheMissRecord, windowMs: number): void;
70
+ /** Inside the window ⇒ the remembered miss; otherwise `undefined` (and the entry is dropped). */
71
+ recall(spaceKey: string): CacheMissRecord | undefined;
72
+ /** Entries held (expired ones that were not read again included). */
73
+ size(): number;
74
+ }
75
+ /** A fresh {@link CacheMissMemory} (`now` = the clock, epoch ms; a test hands its own). */
76
+ export declare function createCacheMissMemory(now?: () => number): CacheMissMemory;
42
77
  /** Does the cache mount overlap the workspace mount (equal, or one inside the other)? Both are absolute, normalized paths. */
43
78
  export declare function mountsOverlap(a: string, b: string): boolean;
44
79
  export {};
@@ -23,6 +23,42 @@ export function workspaceCacheClaimOf(spaceKey) {
23
23
  const digest = createHash("sha256").update(spaceKey).digest("hex").slice(0, 20);
24
24
  return { name: `ws-cache-${digest}`, label: digest };
25
25
  }
26
+ export const WORKSPACE_CACHE_MOUNT_RESERVED = ["/bin", "/boot", "/dev", "/etc", "/home/sandbox", "/lib", "/lib64", "/opt/sema", "/proc", "/root", "/run", "/sbin", "/sys", "/tmp", "/usr", "/var"];
27
+ export function reservedMountCollision(mount) {
28
+ return WORKSPACE_CACHE_MOUNT_RESERVED.find((dir) => mountsOverlap(mount, dir));
29
+ }
30
+ export const CACHE_MISS_MEMORY_CAP = 1024;
31
+ export function createCacheMissMemory(now = Date.now) {
32
+ const held = new Map();
33
+ const keyOf = (spaceKey) => workspaceCacheClaimOf(spaceKey).label;
34
+ return {
35
+ remember(spaceKey, miss, windowMs) {
36
+ const at = now();
37
+ const key = keyOf(spaceKey);
38
+ held.delete(key);
39
+ held.set(key, { miss, until: at + windowMs });
40
+ if (held.size <= CACHE_MISS_MEMORY_CAP)
41
+ return;
42
+ for (const [k, e] of held)
43
+ if (e.until <= at)
44
+ held.delete(k);
45
+ while (held.size > CACHE_MISS_MEMORY_CAP)
46
+ held.delete(held.keys().next().value);
47
+ },
48
+ recall(spaceKey) {
49
+ const key = keyOf(spaceKey);
50
+ const e = held.get(key);
51
+ if (e === undefined)
52
+ return undefined;
53
+ if (e.until <= now()) {
54
+ held.delete(key);
55
+ return undefined;
56
+ }
57
+ return e.miss;
58
+ },
59
+ size: () => held.size,
60
+ };
61
+ }
26
62
  export function mountsOverlap(a, b) {
27
63
  const inside = (x, y) => x === y || x.startsWith(y.endsWith("/") ? y : `${y}/`);
28
64
  return inside(a, b) || inside(b, a);
@@ -71,8 +71,9 @@ export declare const WORKSPACE_SNAPSHOT_EXCLUDE_INVALID = "config.workspace_snap
71
71
  * space snapshot and the park suspend share it). Unset / empty ⇒ no table (the upload command is what it always was).
72
72
  *
73
73
  * The item form is CLOSED: plain relative path segments — no leading `/`, no `.` / `..` / empty segment (`./x`, `a//b`, a
74
- * trailing `/`), no whitespace, no quote characters, no control characters. Tar wildcards inside a segment (`*.log`) are items
75
- * like any other. A project's `.gitignore` is NOT read (a second grammar, per project, not equivalent to tar's). Items are
74
+ * trailing `/`), no whitespace, no quote characters, no control characters, and no whole wildcard (an item made only of `*` / `?`
75
+ * names no path: `*` would exclude every top-level entry and the snapshot would be empty). Tar wildcards inside a segment (`*.log`)
76
+ * are items like any other. A project's `.gitignore` is NOT read (a second grammar, per project, not equivalent to tar's). Items are
76
77
  * trimmed (the CSV separator's spacing is not part of an item); a bad item refuses boot naming its position — the value is a
77
78
  * path pattern, never a secret, so it is echoed.
78
79
  */
@@ -30,6 +30,8 @@ function snapshotExcludeItemProblem(item) {
30
30
  return "not a plain path (it holds whitespace, a quote or a control character)";
31
31
  if (item.startsWith("/"))
32
32
  return "absolute (a leading /) — items are relative to the workspace root";
33
+ if (/^[*?]+$/.test(item))
34
+ return "a whole wildcard (only `*` / `?` — it names no path and would leave the snapshot without any top-level entry)";
33
35
  const segments = item.split("/");
34
36
  if (segments.includes(".."))
35
37
  return "escaping the workspace (a .. segment)";
@@ -20,7 +20,8 @@ dials the device, so the device needs no inbound port, no public address and no
20
20
  (this deployment has no device lane) or an object `{ enabled: true, protocolVersion, maxInflightPerDevice,
21
21
  wsPath, management }`. Read `wsPath` from there rather than hardcoding it: the path is part of the wire
22
22
  contract and reading it means a future path change is not a cross-repo break. `protocolVersion` tells you
23
- which version the server speaks before you spend a handshake finding out.
23
+ which protocol **revision** the server speaks before you spend a handshake finding out (§2.1: the revision decides
24
+ which `errorCode` words the server accepts from you).
24
25
 
25
26
  ---
26
27
 
@@ -144,6 +145,13 @@ The server sends `challenge` immediately on open. From the moment the socket ope
144
145
  the handshake completes, **frames are capped at `maxPreAuthFrameBytes` (4096 bytes)** and a malformed
145
146
  pre-auth frame closes the connection outright.
146
147
 
148
+ **Which revision to declare.** `challenge.protocolVersion` is the highest protocol revision this server speaks
149
+ (`DEVICE_MIN_PROTOCOL_VERSION` is the lowest it accepts, §2.1). Put the highest revision *you* implement that is
150
+ not above `challenge.protocolVersion` into `hello.protocolVersion` — the same integer you sign (§1.3) — and emit
151
+ only the `errorCode` words of that revision (the revision table in §2.1). Nothing is renegotiated after `hello`:
152
+ the revision you declared is the contract for the whole connection. If the highest revision you implement is below
153
+ `DEVICE_MIN_PROTOCOL_VERSION`, the server answers `helloReject: protocol_version_unsupported` (§1.4).
154
+
147
155
  ### 1.3 The `hello` signature (byte-exact — do not improvise)
148
156
 
149
157
  Sign with the device private key over exactly these bytes:
@@ -245,18 +253,32 @@ backoff, exponential to a 60 s ceiling, jittered — except for `revoked`, which
245
253
 
246
254
  `helloAck` carries:
247
255
 
256
+ <!-- closed-set: DEVICE_HELLO_ACK_BOUNDS -->
257
+
248
258
  | Field | Form | Meaning |
249
259
  |---|---|---|
250
260
  | `ok` | always `true` | — |
251
- | `heartbeatIntervalMs` | integer ms | how often you should send `heartbeat` (deployment default 15000) |
252
- | `maxInflight` | integer | how many instructions the server will have outstanding on this device at once (deployment default 4) |
261
+ | `heartbeatIntervalMs` | `integer ms ≥ 1` | how often you should send `heartbeat` (deployment default 15000) |
262
+ | `maxInflight` | `integer ≥ 1` | how many instructions the server will have outstanding on this device at once (deployment default 4) |
253
263
  | `gen` | **canonical decimal string** (`"7"`, never `7`, never `"07"`) | the connection generation. Stamp it on every frame you send |
254
264
  | `pendingInstructions` | `string[]` | instruction ids the server still considers in flight for you — see §5 R2 |
255
265
  | `chunkWatermarks` | `{ [instructionId]: number }` | per instruction, the highest **contiguous** `streamSeq` the server has already accepted. `-1` means "nothing accepted yet" |
256
- | `reconnectGraceMs` | integer ms | the reconnect grace **in force on this server** (R4): how long in-flight work survives a dropped socket. Deployment-tunable, default 120000 |
257
- | `cancelGraceMs` | integer ms | the cancel grace in force (R11): how long after a `cancel` is delivered the server waits for your terminal `result`. Default 10000 |
258
- | `chunkGapTimeoutMs` | integer ms | the chunk-gap timeout in force (R8): how long the server waits for a missing `streamSeq` before failing the instruction. Default 30000 |
259
- | `executionCeilingGraceMs` | integer ms | the grace added to an instruction's `timeoutMs` before the server gives up and settles it as outcome-unknown (R12: the ceiling is `timeoutMs + executionCeilingGraceMs`). Default 30000 |
266
+ | `reconnectGraceMs` | `integer ms ≥ 0` | the reconnect grace **in force on this server** (R4): how long in-flight work survives a dropped socket. Deployment-tunable, default 120000 |
267
+ | `cancelGraceMs` | `integer ms ≥ 0` | the cancel grace in force (R11): how long after a `cancel` is delivered the server waits for your terminal `result`. Default 10000 |
268
+ | `chunkGapTimeoutMs` | `integer ms ≥ 0` | the chunk-gap timeout in force (R8): how long the server waits for a missing `streamSeq` before failing the instruction. Default 30000 |
269
+ | `executionCeilingGraceMs` | `integer ms ≥ 0` | the grace added to an instruction's `timeoutMs` before the server gives up and settles it as outcome-unknown (R12: the ceiling is `timeoutMs + executionCeilingGraceMs`). Default 30000 |
270
+
271
+ **Bounds.** The six numeric fields above are validated by the server when it starts: a deployment whose knob (or
272
+ embedding option) is outside its row's range refuses to boot — the value is never clamped and never replaced by a
273
+ default — so a `helloAck` on the wire is always in range, and every number in it is the value the server's own
274
+ timers and slot counts are armed with. Every `ms` field is also at most `2147483647` (the largest delay a
275
+ timer can hold; a larger value would be silently collapsed to 1 ms); `maxInflight` has no ceiling beyond an
276
+ integer JSON represents exactly (at most `9007199254740991`). A `0` grace means "no grace": that timer
277
+ fires at once. An executor that reads a value outside its row's range must treat it as a protocol violation — the executor-side twin
278
+ of the server's `bad_shape` refusal (§2.4): close the connection and reconnect with backoff — and must **not** read the
279
+ out-of-range value as the default.
280
+ `deviceHelloAckNumberFlaw` (from `@sema-agent/server/device-protocol`) is the same check the server applies.
281
+ The *absence* of one of the four grace fields is a different case, described below, and still means "not told".
260
282
 
261
283
  The four grace fields are the values the server's own timers are armed with — not documentation defaults. Size
262
284
  your reconnect backoff ceiling, your `cancel` answer deadline, your retransmission cadence and your own
@@ -314,8 +336,8 @@ base64 inside JSON fields.
314
336
  | Constant | Value | Meaning |
315
337
  |---|---|---|
316
338
  | `DEVICE_WS_PATH` | `/v1/device/ws` | the upgrade path (prefer the `wsPath` from `/v1/capabilities`) |
317
- | `DEVICE_PROTOCOL_VERSION` | `1` | the version this server speaks |
318
- | `DEVICE_MIN_PROTOCOL_VERSION` | `1` | the lowest version it accepts; below it, a loud `helloReject` — never a silent downgrade |
339
+ | `DEVICE_PROTOCOL_VERSION` | `2` | the protocol revision this server speaks (the highest it accepts) |
340
+ | `DEVICE_MIN_PROTOCOL_VERSION` | `1` | the lowest revision it accepts; below it (or above the highest), a loud `helloReject` — never a silent downgrade |
319
341
  | `DEVICE_HELLO_DOMAIN_TAG` | `sema.device.hello.v1` | the handshake signature domain tag (§1.3) |
320
342
 
321
343
  <!-- closed-set: DEVICE_WS_LIMITS -->
@@ -330,6 +352,29 @@ base64 inside JSON fields.
330
352
  Note the asymmetry: the transport cap is 1 MiB, and the 4096-byte pre-auth cap is applied **after** the
331
353
  frame has been reassembled. Keep `hello` small; it is not a place for long `supports` prose.
332
354
 
355
+ #### Protocol revisions and the `errorCode` set
356
+
357
+ The protocol revision is the only thing an executor can read *before* it sends `hello` (the `challenge` frame
358
+ carries it), so the set of `errorCode` words the server accepts from you is a function of the revision, not a
359
+ separate list. The table gives, per revision, the words **added** at that revision; the set at revision *N* is
360
+ the union of the rows up to *N*, and the full set at the current revision is the §3.3 table.
361
+
362
+ <!-- closed-set: DEVICE_OUTCOME_ERROR_CODES_BY_REVISION -->
363
+
364
+ | Revision | `errorCode` words added at this revision |
365
+ |---|---|
366
+ | `1` | `aborted`, `already_exists`, `auth_failed`, `callback_error`, `invalid`, `is_directory`, `not_directory`, `not_found`, `not_supported`, `outcome_unknown`, `permission_denied`, `precondition_failed`, `shell_unavailable`, `spawn_error`, `suspended`, `target_unavailable`, `timeout`, `transport_lost`, `unknown` |
367
+ | `2` | `liveness_no_output` |
368
+
369
+ Rules: adding a frame or a field is additive and does not raise the revision; adding a word to a closed set the
370
+ server *accepts from you* (today: the `errorCode` set) raises `DEVICE_PROTOCOL_VERSION` and adds a row here; a
371
+ released row never changes. Declare a revision as §1.2 says and send only the words of that revision: an executor
372
+ that declared revision 1 never sends `liveness_no_output` (the server does not police this per connection — it
373
+ decodes every connection against the current revision's full set — but a word above your declared revision is outside
374
+ your contract). The same sets are importable:
375
+ `DEVICE_OUTCOME_ERROR_CODES_BY_REVISION` and `deviceOutcomeErrorCodesAt(revision)` from
376
+ `@sema-agent/server/device-protocol`.
377
+
333
378
  ### 2.2 Device → server frames
334
379
 
335
380
  <!-- closed-set: DEVICE_FRAME_TYPES -->
@@ -515,6 +560,9 @@ a moment ago. If you omit `created`, the consumer reports the weaker of the two
515
560
 
516
561
  `message` is required on the failure arm and is shown to the model — make it specific.
517
562
 
563
+ This is the full set at the current revision (`2`); the revision table in §2.1 says which revision each word arrived
564
+ with, and an executor sends only the words of the revision it declared (§1.2).
565
+
518
566
  <!-- closed-set: DEVICE_OUTCOME_ERROR_CODES -->
519
567
 
520
568
  | `errorCode` | Use it when |
@@ -525,6 +573,7 @@ a moment ago. If you omit `created`, the consumer reports the weaker of the two
525
573
  | `callback_error` | a callback/hook on the device side failed (exec family) |
526
574
  | `invalid` | the arguments do not make sense for this target (e.g. `readLink` on a non-symlink) |
527
575
  | `is_directory` | a file operation hit a directory |
576
+ | `liveness_no_output` | **you** killed the command because it produced no output for your own silence window (a hung command); the wall-clock budget `timeoutMs` running out stays `timeout`. The frame has no field for the window length — do not invent one |
528
577
  | `not_directory` | a directory operation hit a non-directory |
529
578
  | `not_found` | the path does not exist |
530
579
  | `not_supported` | you do not implement this face (prefer simply not declaring it) |
@@ -539,10 +588,16 @@ a moment ago. If you omit `created`, the consumer reports the weaker of the two
539
588
  | `transport_lost` | a transport the device itself depends on went away |
540
589
  | `unknown` | none of the above |
541
590
 
542
- Two codes carry load-bearing semantics upstream and must not be used loosely:
591
+ Three codes carry load-bearing semantics upstream and must not be used loosely:
543
592
 
544
593
  - `timeout` is treated as **retryable**. Use it only when you actually killed the work and know it is
545
594
  dead. If you are not sure the command is dead, use `outcome_unknown`.
595
+ - `liveness_no_output` is the same kind of word — the command ran, you killed it, **retryable** — with a
596
+ different cause: it names the silence, `timeout` names the wall-clock budget. They are two different
597
+ numbers and the model is told a different sentence for each, so report the bound that actually fired.
598
+ The same rule applies: use it only when you actually killed the work and know it is dead; if you are
599
+ not sure, use `outcome_unknown`. On a file instruction it is a protocol violation (a file operation has
600
+ no silence window) and the server reads it as `unknown`.
546
601
  - `outcome_unknown` is never auto-retried, anywhere. It is the honest answer for "the command may have
547
602
  already run and I cannot confirm". Choosing `timeout`/`transport_lost` instead invites a second
548
603
  execution of a command that already had side effects.