@sema-agent/core 7.52.0 → 7.54.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 (357) hide show
  1. package/CHANGELOG.md +470 -0
  2. package/LICENSE +3 -1
  3. package/README.md +3 -1
  4. package/dist/agents/cross-session-envelope.d.ts +8 -0
  5. package/dist/agents/cross-session-envelope.js +6 -0
  6. package/dist/agents/cross-session-judge.d.ts +21 -1
  7. package/dist/agents/cross-session-judge.js +8 -0
  8. package/dist/agents/cross-session-ref.d.ts +5 -0
  9. package/dist/agents/cross-session-ref.js +1 -0
  10. package/dist/agents/org-delivery-gate.d.ts +122 -0
  11. package/dist/agents/org-delivery-gate.js +69 -0
  12. package/dist/agents/peer-held-queue.d.ts +9 -4
  13. package/dist/agents/peer-held-queue.js +6 -5
  14. package/dist/agents/peer-session-drain.d.ts +35 -6
  15. package/dist/agents/peer-session-drain.js +271 -182
  16. package/dist/agents/read-inbox-tool.d.ts +7 -0
  17. package/dist/agents/read-inbox-tool.js +5 -1
  18. package/dist/agents/send-message-tool.js +3 -4
  19. package/dist/agents/subagent.d.ts +6 -5
  20. package/dist/agents/subagent.js +117 -83
  21. package/dist/agents/tool-filter.d.ts +25 -4
  22. package/dist/agents/tool-filter.js +13 -3
  23. package/dist/brain/anthropic.d.ts +6 -1
  24. package/dist/brain/anthropic.js +4 -4
  25. package/dist/brain/model-presets.js +2 -1
  26. package/dist/brain/route-adjudicator.d.ts +6 -0
  27. package/dist/brain/routing.d.ts +24 -1
  28. package/dist/brain/routing.js +54 -19
  29. package/dist/brain/timeout.js +5 -4
  30. package/dist/core/a2a.d.ts +5 -2
  31. package/dist/core/a2a.js +33 -14
  32. package/dist/core/allow-entry.d.ts +3 -1
  33. package/dist/core/allow-entry.js +3 -2
  34. package/dist/core/approval-object.d.ts +87 -0
  35. package/dist/core/approval-object.js +85 -0
  36. package/dist/core/arg-summary.d.ts +3 -2
  37. package/dist/core/arg-summary.js +17 -11
  38. package/dist/core/ask-origin.d.ts +43 -27
  39. package/dist/core/ask-origin.js +20 -11
  40. package/dist/core/ask-question.d.ts +0 -1
  41. package/dist/core/ask-question.js +4 -67
  42. package/dist/core/ask-unresolvable-notice.d.ts +3 -2
  43. package/dist/core/auto-compaction.d.ts +106 -50
  44. package/dist/core/auto-compaction.js +69 -53
  45. package/dist/core/auto-mode-prompt.d.ts +4 -2
  46. package/dist/core/auto-mode-prompt.js +4 -3
  47. package/dist/core/canonical-json.d.ts +69 -0
  48. package/dist/core/canonical-json.js +130 -8
  49. package/dist/core/checkpoint-requirements.d.ts +13 -7
  50. package/dist/core/checkpoint-requirements.js +4 -1
  51. package/dist/core/checkpoint-store.d.ts +60 -36
  52. package/dist/core/checkpoint-store.js +7 -6
  53. package/dist/core/child-clamp-seats.d.ts +45 -6
  54. package/dist/core/child-clamp-seats.js +56 -4
  55. package/dist/core/compliance.js +3 -1
  56. package/dist/core/consolidate-scope.d.ts +1 -1
  57. package/dist/core/cross-boundary-taint.d.ts +213 -0
  58. package/dist/core/cross-boundary-taint.js +196 -0
  59. package/dist/core/decision-receipt.d.ts +44 -1
  60. package/dist/core/decision-receipt.js +16 -0
  61. package/dist/core/effective-path-target.d.ts +28 -6
  62. package/dist/core/effective-path-target.js +28 -2
  63. package/dist/core/engine-notice.d.ts +47 -4
  64. package/dist/core/engine-notice.js +17 -1
  65. package/dist/core/engine-storage-backend.d.ts +73 -0
  66. package/dist/core/engine-storage-backend.js +1 -0
  67. package/dist/core/file-history-store.d.ts +6 -2
  68. package/dist/core/file-history-store.js +1 -0
  69. package/dist/core/gate-fold.js +13 -4
  70. package/dist/core/gate-lanes.js +41 -34
  71. package/dist/core/gate-outcome.d.ts +18 -9
  72. package/dist/core/governance-codes.d.ts +9 -2
  73. package/dist/core/governance-codes.js +9 -0
  74. package/dist/core/hands-band-screen.d.ts +7 -0
  75. package/dist/core/hands-band-screen.js +32 -0
  76. package/dist/core/hooks.d.ts +21 -19
  77. package/dist/core/hooks.js +6 -5
  78. package/dist/core/locked-config.d.ts +110 -31
  79. package/dist/core/locked-config.js +155 -42
  80. package/dist/core/lsp.d.ts +1 -1
  81. package/dist/core/mcp-probe.js +1 -1
  82. package/dist/core/mcp-server-spec.d.ts +152 -17
  83. package/dist/core/mcp-server-spec.js +223 -40
  84. package/dist/core/mcp.d.ts +21 -14
  85. package/dist/core/mcp.js +59 -54
  86. package/dist/core/memory-engine/engine.d.ts +14 -19
  87. package/dist/core/memory-engine/engine.js +9 -6
  88. package/dist/core/memory-engine/export-bundle.d.ts +1 -1
  89. package/dist/core/memory-engine/export-bundle.js +1 -1
  90. package/dist/core/memory-engine/file-backend.d.ts +2 -6
  91. package/dist/core/memory-engine/file-backend.js +1 -12
  92. package/dist/core/memory.d.ts +3 -3
  93. package/dist/core/mode-policy.d.ts +16 -26
  94. package/dist/core/mode-policy.js +5 -18
  95. package/dist/core/mods/kernel/dispatch.js +13 -15
  96. package/dist/core/mods/kernel/events.d.ts +9 -6
  97. package/dist/core/mods/kernel/events.js +2 -2
  98. package/dist/core/mods/kernel/receive.d.ts +41 -11
  99. package/dist/core/mods/kernel/receive.js +124 -54
  100. package/dist/core/mods/kernel/site-rules.d.ts +80 -15
  101. package/dist/core/mods/kernel/site-rules.js +232 -40
  102. package/dist/core/mods/stations.d.ts +0 -6
  103. package/dist/core/mods/stations.js +0 -7
  104. package/dist/core/mounted-mcp.d.ts +57 -0
  105. package/dist/core/mounted-mcp.js +71 -2
  106. package/dist/core/own-property-receipt.d.ts +13 -0
  107. package/dist/core/own-property-receipt.js +3 -1
  108. package/dist/core/parent-spec-seat.d.ts +12 -15
  109. package/dist/core/parent-spec-seat.js +14 -6
  110. package/dist/core/pause-registry.d.ts +35 -1
  111. package/dist/core/pause-registry.js +10 -0
  112. package/dist/core/permission-rule-consent.d.ts +18 -5
  113. package/dist/core/permission-rule-consent.js +13 -8
  114. package/dist/core/permission-rule-model.d.ts +87 -15
  115. package/dist/core/permission-rule-model.js +87 -8
  116. package/dist/core/permission-rule-org.d.ts +200 -33
  117. package/dist/core/permission-rule-org.js +217 -91
  118. package/dist/core/permission-rule-session.js +2 -2
  119. package/dist/core/permission-rule-store.d.ts +8 -0
  120. package/dist/core/permission-rule-store.js +1 -0
  121. package/dist/core/permission-rule-sync.js +3 -2
  122. package/dist/core/permission-rules.d.ts +7 -1
  123. package/dist/core/permission-rules.js +37 -8
  124. package/dist/core/read-only-shell.d.ts +1 -1
  125. package/dist/core/read-only-shell.js +30 -9
  126. package/dist/core/remote-env.d.ts +41 -1
  127. package/dist/core/remote-env.js +18 -0
  128. package/dist/core/resume-startup-cause.d.ts +18 -0
  129. package/dist/core/resume-startup-cause.js +12 -0
  130. package/dist/core/retention.js +7 -2
  131. package/dist/core/retired-keys.d.ts +8 -2
  132. package/dist/core/retired-keys.js +12 -1
  133. package/dist/core/roles.js +60 -10
  134. package/dist/core/rule-lane-reads.js +1 -1
  135. package/dist/core/runner/approval-feedback.d.ts +46 -0
  136. package/dist/core/runner/approval-feedback.js +41 -0
  137. package/dist/core/runner/compaction-call-options.d.ts +3 -3
  138. package/dist/core/runner/compaction-call-options.js +2 -2
  139. package/dist/core/runner/contracts.d.ts +34 -41
  140. package/dist/core/runner/cross-boundary-lane.d.ts +87 -0
  141. package/dist/core/runner/cross-boundary-lane.js +190 -0
  142. package/dist/core/runner/decide-continuation.d.ts +4 -4
  143. package/dist/core/runner/decide-continuation.js +3 -1
  144. package/dist/core/runner/gate-exit.d.ts +32 -12
  145. package/dist/core/runner/gate-exit.js +77 -34
  146. package/dist/core/runner/inherited-ask-grants.js +2 -2
  147. package/dist/core/runner/mcp-redial.d.ts +52 -10
  148. package/dist/core/runner/mcp-redial.js +38 -9
  149. package/dist/core/runner/permission-rule-lanes.d.ts +5 -2
  150. package/dist/core/runner/permission-rule-lanes.js +22 -10
  151. package/dist/core/runner/person-path-rows.d.ts +5 -3
  152. package/dist/core/runner/person-path-rows.js +2 -2
  153. package/dist/core/runner/prepare-acquire-reconcile.d.ts +3 -4
  154. package/dist/core/runner/prepare-acquire-reconcile.js +3 -3
  155. package/dist/core/runner/prepare-ask-lane.js +3 -3
  156. package/dist/core/runner/prepare-caps-and-workflow.d.ts +6 -5
  157. package/dist/core/runner/prepare-caps-and-workflow.js +3 -3
  158. package/dist/core/runner/prepare-config-doors.d.ts +58 -15
  159. package/dist/core/runner/prepare-config-doors.js +167 -146
  160. package/dist/core/runner/prepare-context-lane.js +6 -3
  161. package/dist/core/runner/prepare-defer-classify.js +4 -7
  162. package/dist/core/runner/prepare-delegation-surface.d.ts +5 -4
  163. package/dist/core/runner/prepare-delegation-surface.js +8 -7
  164. package/dist/core/runner/prepare-execution-env.d.ts +1 -2
  165. package/dist/core/runner/prepare-execution-env.js +2 -1
  166. package/dist/core/runner/prepare-gate-stations.d.ts +2 -0
  167. package/dist/core/runner/prepare-gate-stations.js +4 -3
  168. package/dist/core/runner/prepare-hands-readface.d.ts +3 -3
  169. package/dist/core/runner/prepare-hands-readface.js +3 -2
  170. package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
  171. package/dist/core/runner/prepare-inherited-gate.js +3 -2
  172. package/dist/core/runner/prepare-listings.d.ts +1 -1
  173. package/dist/core/runner/prepare-listings.js +4 -2
  174. package/dist/core/runner/prepare-memory-engine-session.d.ts +0 -6
  175. package/dist/core/runner/prepare-memory-engine-session.js +19 -6
  176. package/dist/core/runner/prepare-memory.d.ts +3 -2
  177. package/dist/core/runner/prepare-memory.js +6 -2
  178. package/dist/core/runner/prepare-offload-wrappers.d.ts +4 -4
  179. package/dist/core/runner/prepare-offload-wrappers.js +2 -1
  180. package/dist/core/runner/prepare-park-ask.d.ts +1 -1
  181. package/dist/core/runner/prepare-park-ask.js +9 -3
  182. package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
  183. package/dist/core/runner/prepare-policy-chain.js +14 -19
  184. package/dist/core/runner/prepare-project-context.d.ts +1 -1
  185. package/dist/core/runner/prepare-project-context.js +6 -4
  186. package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
  187. package/dist/core/runner/prepare-prompt-assembly.js +20 -16
  188. package/dist/core/runner/prepare-prompt-inputs.d.ts +12 -6
  189. package/dist/core/runner/prepare-prompt-inputs.js +6 -3
  190. package/dist/core/runner/prepare-protocol-tools.d.ts +7 -5
  191. package/dist/core/runner/prepare-protocol-tools.js +19 -12
  192. package/dist/core/runner/prepare-question-face.d.ts +4 -2
  193. package/dist/core/runner/prepare-question-face.js +4 -3
  194. package/dist/core/runner/prepare-safety-scan.d.ts +7 -2
  195. package/dist/core/runner/prepare-safety-scan.js +34 -27
  196. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -2
  197. package/dist/core/runner/prepare-suspend-saga.js +6 -2
  198. package/dist/core/runner/prepare-task.js +22 -19
  199. package/dist/core/runner/prepare-turn-wiring.d.ts +10 -5
  200. package/dist/core/runner/prepare-turn-wiring.js +14 -49
  201. package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -5
  202. package/dist/core/runner/prepare-wiring-manifest.js +9 -6
  203. package/dist/core/runner/prepare-workspace-restore.js +2 -2
  204. package/dist/core/runner/resume-admission.d.ts +4 -1
  205. package/dist/core/runner/resume-admission.js +16 -14
  206. package/dist/core/runner/resume-apply.js +4 -4
  207. package/dist/core/runner/resume-internals-and-config.d.ts +3 -3
  208. package/dist/core/runner/resume-internals-and-config.js +3 -8
  209. package/dist/core/runner/resume-policy-outcome.d.ts +2 -2
  210. package/dist/core/runner/resume-policy-outcome.js +18 -15
  211. package/dist/core/runner/resume-preflight.js +2 -2
  212. package/dist/core/runner/resume-review-outcome.js +3 -3
  213. package/dist/core/runner/retired-runner-deps.d.ts +5 -6
  214. package/dist/core/runner/retired-runner-deps.js +1 -4
  215. package/dist/core/runner/run-leg.js +97 -43
  216. package/dist/core/runner/run-recovery-lanes.js +5 -3
  217. package/dist/core/runner/run-settle-and-teardown.js +2 -0
  218. package/dist/core/runner/run-terminal-adoption.js +21 -9
  219. package/dist/core/runner/run-turn-boundary.js +9 -3
  220. package/dist/core/runner/runtask.js +1 -1
  221. package/dist/core/runner/session-file-state-replay.d.ts +5 -2
  222. package/dist/core/runner/session-file-state-replay.js +3 -2
  223. package/dist/core/runner/skills-seat.d.ts +15 -0
  224. package/dist/core/runner/skills-seat.js +57 -0
  225. package/dist/core/runner/stream-steer-verb.js +2 -2
  226. package/dist/core/runner/sub-agent-deny-lists.d.ts +13 -11
  227. package/dist/core/runner/sub-agent-deny-lists.js +7 -5
  228. package/dist/core/runner/synthetic-tools.d.ts +5 -2
  229. package/dist/core/runner/synthetic-tools.js +2 -12
  230. package/dist/core/runner/tool-face-overlay.d.ts +24 -6
  231. package/dist/core/runner/tool-face-overlay.js +24 -15
  232. package/dist/core/runner-deps.d.ts +37 -22
  233. package/dist/core/session-policy-store.d.ts +19 -0
  234. package/dist/core/session-policy-store.js +22 -2
  235. package/dist/core/session-store.d.ts +35 -16
  236. package/dist/core/session-store.js +20 -12
  237. package/dist/core/{shell-lexer.d.ts → shell-run-candidates.d.ts} +36 -12
  238. package/dist/core/{shell-lexer.js → shell-run-candidates.js} +145 -225
  239. package/dist/core/shell-wrapper-table.d.ts +5 -3
  240. package/dist/core/shell-wrapper-table.js +1 -1
  241. package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
  242. package/dist/core/store-contracts/mailbox-store-contract.js +13 -0
  243. package/dist/core/store-contracts/session-store-contract.d.ts +49 -0
  244. package/dist/core/store-contracts/session-store-contract.js +639 -0
  245. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +12 -0
  246. package/dist/core/store-contracts/session-store-incarnation-contract.js +5 -2
  247. package/dist/core/task-event.d.ts +44 -6
  248. package/dist/core/task-notification.js +2 -2
  249. package/dist/core/task-registry-agent.js +1 -0
  250. package/dist/core/task-registry-shared.d.ts +5 -0
  251. package/dist/core/task-registry.d.ts +7 -0
  252. package/dist/core/task-registry.js +17 -0
  253. package/dist/core/task-result.d.ts +1 -1
  254. package/dist/core/task-spec.d.ts +7 -7
  255. package/dist/core/tighten-task-spec.d.ts +6 -4
  256. package/dist/core/tighten-task-spec.js +2 -2
  257. package/dist/core/tool-catalog-entries.d.ts +1 -1
  258. package/dist/core/tool-catalog-entries.js +52 -52
  259. package/dist/core/tool-face-reader.d.ts +49 -0
  260. package/dist/core/tool-face-reader.js +94 -0
  261. package/dist/core/tool-face.d.ts +17 -8
  262. package/dist/core/tool-face.js +1 -0
  263. package/dist/core/tool-policy.d.ts +67 -23
  264. package/dist/core/tool-policy.js +72 -60
  265. package/dist/core/tool-roster.d.ts +46 -28
  266. package/dist/core/tool-roster.js +91 -14
  267. package/dist/core/tool-spec-members.js +2 -2
  268. package/dist/core/tool-spec.d.ts +20 -4
  269. package/dist/core/tools.js +2 -0
  270. package/dist/core/trace.d.ts +24 -2
  271. package/dist/core/types.d.ts +3 -3
  272. package/dist/core/types.js +1 -1
  273. package/dist/core/untrusted-text.d.ts +23 -0
  274. package/dist/core/untrusted-text.js +10 -10
  275. package/dist/core/user-attachment.d.ts +56 -2
  276. package/dist/core/user-attachment.js +139 -56
  277. package/dist/core/wiring-manifest.d.ts +4 -2
  278. package/dist/engine/compaction/compaction.d.ts +2 -2
  279. package/dist/engine/compaction/utils.d.ts +2 -2
  280. package/dist/engine/harness/agent-harness.js +2 -1
  281. package/dist/engine/harness/types.d.ts +10 -4
  282. package/dist/engine/llm/types.d.ts +7 -2
  283. package/dist/engine/llm/types.js +2 -1
  284. package/dist/engine/loop/agent-loop.d.ts +12 -1
  285. package/dist/engine/loop/agent-loop.js +17 -5
  286. package/dist/engine/loop/types.d.ts +7 -1
  287. package/dist/engine/session/repo-utils.d.ts +5 -0
  288. package/dist/engine/session/repo-utils.js +9 -0
  289. package/dist/engine/session/storage-base.js +2 -2
  290. package/dist/index.d.ts +37 -17
  291. package/dist/index.js +31 -14
  292. package/dist/internal/harness.d.ts +1 -0
  293. package/dist/internal/harness.js +1 -0
  294. package/dist/internal/llm.d.ts +1 -1
  295. package/dist/internal/llm.js +1 -1
  296. package/dist/orchestration/goal.d.ts +24 -1
  297. package/dist/orchestration/goal.js +3 -2
  298. package/dist/orchestration/run-workflow-tool.d.ts +3 -0
  299. package/dist/orchestration/run-workflow-tool.js +1 -0
  300. package/dist/orchestration/workflow-governance.d.ts +1 -1
  301. package/dist/orchestration/workflow.d.ts +3 -0
  302. package/dist/orchestration/workflow.js +2 -1
  303. package/dist/prompt-assembly/artifact-store.d.ts +44 -9
  304. package/dist/prompt-assembly/artifact-store.js +38 -12
  305. package/dist/prompt-assembly/artifact.d.ts +12 -11
  306. package/dist/prompt-assembly/artifact.js +52 -21
  307. package/dist/prompt-assembly/assemble.d.ts +2 -0
  308. package/dist/prompt-assembly/assemble.js +2 -1
  309. package/dist/prompt-assembly/catalog/index.js +2 -1
  310. package/dist/prompt-assembly/catalog/memory.d.ts +13 -0
  311. package/dist/prompt-assembly/catalog/memory.js +6 -0
  312. package/dist/prompt-assembly/catalog/schema.d.ts +19 -2
  313. package/dist/prompt-assembly/catalog/schema.js +3 -0
  314. package/dist/prompt-assembly/composer.d.ts +2 -0
  315. package/dist/prompt-assembly/composer.js +1 -1
  316. package/dist/prompt-assembly/overlay.d.ts +21 -11
  317. package/dist/prompt-assembly/overlay.js +57 -19
  318. package/dist/prompt-assembly/select.d.ts +21 -6
  319. package/dist/prompt-assembly/select.js +41 -4
  320. package/dist/prompts/default.d.ts +1 -12
  321. package/dist/prompts/default.js +0 -3
  322. package/dist/shell/lex.d.ts +87 -8
  323. package/dist/shell/lex.js +283 -49
  324. package/dist/stores/file/index.d.ts +8 -6
  325. package/dist/stores/file/permission-rule-store.d.ts +1 -1
  326. package/dist/stores/file/permission-rule-store.js +2 -1
  327. package/dist/tools/artifact/artifact-tool.js +14 -3
  328. package/dist/tools/fs/bash-program-position.d.ts +3 -15
  329. package/dist/tools/fs/bash-program-position.js +3 -10
  330. package/dist/tools/fs/bash-readonly-classifier.js +2 -0
  331. package/dist/tools/fs/fs-bash.d.ts +3 -3
  332. package/dist/tools/fs/fs-bash.js +72 -101
  333. package/dist/tools/fs/fs-read.d.ts +12 -0
  334. package/dist/tools/fs/fs-read.js +10 -9
  335. package/dist/tools/fs/fs-shared.d.ts +27 -31
  336. package/dist/tools/fs/fs-shared.js +12 -15
  337. package/dist/tools/fs/fs-write.js +1 -1
  338. package/dist/tools/fs/index.js +2 -2
  339. package/dist/tools/fs/read-deny-home.d.ts +1 -1
  340. package/dist/tools/fs/read-deny-home.js +5 -4
  341. package/dist/tools/fs/read-deny.d.ts +16 -3
  342. package/dist/tools/fs/read-deny.js +12 -9
  343. package/dist/tools/fs/safety.d.ts +7 -6
  344. package/dist/tools/fs/shell-read-bindings.d.ts +5 -3
  345. package/dist/tools/fs/shell-read-bindings.js +15 -16
  346. package/dist/tools/fs/shell-read-device.js +1 -1
  347. package/dist/tools/fs/shell-read-plan.js +36 -47
  348. package/dist/tools/fs/shell-read-word.d.ts +1 -6
  349. package/dist/tools/fs/shell-read-word.js +2 -20
  350. package/dist/tools/fs/shell-reentry-text.js +1 -1
  351. package/dist/tools/web.d.ts +2 -2
  352. package/package.json +4 -1
  353. package/test/export-surface.snapshot.json +5651 -5457
  354. package/wire-changes/core@7.53.0.json +1968 -0
  355. package/wire-changes/core@7.54.0.json +2870 -0
  356. package/dist/core/shell-scan.d.ts +0 -60
  357. package/dist/core/shell-scan.js +0 -183
@@ -1,13 +1,35 @@
1
+ import type { RunnerDeps } from "../core/runner-deps.js";
1
2
  import { type ArtifactVerifyCode, type PromptArtifact, type PublishedPromptArtifact } from "./artifact.js";
3
+ /** Why a copy a store holds could not be used: the verifier refused it (its code and reason — a retired schema's reason
4
+ * names the republish rewrite), or the store could not read it back (`unreadable`, the reason naming the read error). */
5
+ export interface StoredArtifactRefusal {
6
+ readonly code: ArtifactVerifyCode | "unreadable";
7
+ readonly reason: string;
8
+ }
9
+ /** What a read tells its caller when a copy was there and could not be used. */
10
+ export type OnArtifactRefused = (refusal: StoredArtifactRefusal) => void;
11
+ /** The clause every reader says of a copy that could not be used, by its cause — one wording, wherever it is said. */
12
+ export declare function refusalClause(refusal: StoredArtifactRefusal): string;
13
+ /**
14
+ * The operator line for a stored center artifact that could not be read back: which copy, why — refused or unreadable
15
+ * (the clause above), or no copy at all — and what follows from it. The one sentence a boot restore and a pinned resume
16
+ * say.
17
+ */
18
+ export declare function storedArtifactLine(artifactDigest: string, refusal: StoredArtifactRefusal | undefined, consequence: string): string;
2
19
  export interface PromptArtifactStore {
3
- /** Verified read by digest — undefined on a miss OR on any verification failure of the stored bytes. */
4
- get(artifactDigest: string): Promise<PublishedPromptArtifact | undefined>;
20
+ /**
21
+ * Verified read by digest — undefined on a miss OR on a copy that cannot be used. When a copy was there and could not
22
+ * be used — the verifier refused its bytes, or the store could not read them — `onRefused` is told once before the
23
+ * read answers undefined, so a caller can tell a refused or unreadable copy (a retired schema, a corrupt file, a file
24
+ * the process may not read) from a missing one. A store that holds a copy it cannot use calls it.
25
+ */
26
+ get(artifactDigest: string, onRefused?: OnArtifactRefused): Promise<PublishedPromptArtifact | undefined>;
5
27
  /** Write by digest (the caller verified before putting). */
6
28
  put(artifact: PublishedPromptArtifact): Promise<void>;
7
29
  }
8
30
  export declare class MemoryPromptArtifactStore implements PromptArtifactStore {
9
31
  private readonly byDigest;
10
- get(artifactDigest: string): Promise<PublishedPromptArtifact | undefined>;
32
+ get(artifactDigest: string, onRefused?: OnArtifactRefused): Promise<PublishedPromptArtifact | undefined>;
11
33
  put(artifact: PublishedPromptArtifact): Promise<void>;
12
34
  }
13
35
  /** `artifacts/<hex>.json` under a caller-owned directory; atomic tmp+rename writes. */
@@ -15,7 +37,9 @@ export declare class FilePromptArtifactStore implements PromptArtifactStore {
15
37
  private readonly dir;
16
38
  constructor(dir: string);
17
39
  private fileFor;
18
- get(artifactDigest: string): Promise<PublishedPromptArtifact | undefined>;
40
+ /** A file that is not there is a miss; one that is there and cannot be read (a directory in its place, a file the
41
+ * process may not open, too many open files) is told to `onRefused` as `unreadable`, by its error code. */
42
+ get(artifactDigest: string, onRefused?: OnArtifactRefused): Promise<PublishedPromptArtifact | undefined>;
19
43
  put(artifact: PublishedPromptArtifact): Promise<void>;
20
44
  }
21
45
  export type PromptSourceState = {
@@ -47,8 +71,10 @@ export declare class FilePromptSourceStateStore implements PromptSourceStateStor
47
71
  export interface PromptArtifactSource {
48
72
  /** The current verified candidate new sessions (and every top-level session at a compaction) adopt; undefined = none. */
49
73
  current(): PublishedPromptArtifact | undefined;
50
- /** Resolve a PINNED digest (the resume path). Undefined = unavailable — the caller fails loud. */
51
- get(artifactDigest: string): Promise<PromptArtifact | undefined>;
74
+ /** Resolve a PINNED digest (the resume path). Undefined = unavailable — the caller fails loud. `onRefused` is the
75
+ * store's (see {@link PromptArtifactStore.get}): told when a copy was there and could not be used, so the caller says
76
+ * why. */
77
+ get(artifactDigest: string, onRefused?: OnArtifactRefused): Promise<PromptArtifact | undefined>;
52
78
  /** The tri-state every adoption decision keys on: `"active"` = a verified candidate is live; `"disabled"` = an
53
79
  * EXPLICIT verified revocation (`applyDisabled`) — the one state that empties a pinned center layer;
54
80
  * `"unavailable"` = no verified state (never configured, or a store miss at boot) — an outage never strips policy,
@@ -61,12 +87,16 @@ export interface PromptArtifactSource {
61
87
  * in-memory swap. Any verification failure leaves EVERYTHING unchanged.
62
88
  * - `applyDisabled(sourceRevision)` — an explicit, successful disable/delete response: persist disabled state +
63
89
  * drop the candidate. (Network failure/304 = simply don't call anything.)
64
- * - `restore()` — boot: re-resolve the persisted active state through the verified store; a miss or verify failure
65
- * boots stateless (no candidate) without touching the persisted record.
90
+ * - `restore()` — boot: re-resolve the persisted active state through the verified store; a miss, an unreadable copy
91
+ * or a verify failure boots stateless (no candidate) without touching the persisted record, and says which on
92
+ * `onError` (a refused copy — a retired schema's reason names the republish rewrite — and an unreadable one are told
93
+ * apart from no copy at all).
66
94
  */
67
95
  export declare class CenterPromptSource implements PromptArtifactSource {
68
96
  private readonly store;
69
97
  private readonly stateStore;
98
+ private readonly onError;
99
+ private readonly notifier;
70
100
  private candidate;
71
101
  private state;
72
102
  /** Serialization tail for the three STATE-MUTATING entry points. Each is a multi-await sequence ending in a
@@ -78,12 +108,17 @@ export declare class CenterPromptSource implements PromptArtifactSource {
78
108
  private tail;
79
109
  /** Run `fn` after every previously-queued mutation settles (success OR failure — a failed apply must not wedge the queue). */
80
110
  private exclusive;
111
+ /** `onError` is the operator channel of the source's own acts (a boot restore that finds its persisted artifact
112
+ * refused, unreadable or gone) — the Runner's sink, in its shape (`RunnerDeps.onError`): pass the Runner's. A boot
113
+ * restore belongs to no session: its context is `{ phase: "prompt-constitution", sessionId: "", classification:
114
+ * "center-source-restore" }`. Omitted = those acts are silent. */
81
115
  constructor(opts: {
82
116
  store: PromptArtifactStore;
83
117
  stateStore?: PromptSourceStateStore;
118
+ onError?: RunnerDeps["onError"];
84
119
  });
85
120
  current(): PublishedPromptArtifact | undefined;
86
- get(artifactDigest: string): Promise<PromptArtifact | undefined>;
121
+ get(artifactDigest: string, onRefused?: OnArtifactRefused): Promise<PromptArtifact | undefined>;
87
122
  restore(): Promise<void>;
88
123
  sourceState(): "active" | "disabled" | "unavailable";
89
124
  applySnapshot(raw: unknown): Promise<{
@@ -1,22 +1,37 @@
1
1
  import { mkdirSync, promises as fs } from "node:fs";
2
2
  import path from "node:path";
3
+ import { createSafeNotifier, observeThenableRejection } from "../core/safe-notify.js";
3
4
  import { verifyPromptArtifact } from "./artifact.js";
4
- function verifiedRead(bytes, artifactDigest) {
5
+ export function refusalClause(refusal) {
6
+ return refusal.code === "unreadable" ? `unreadable (${refusal.reason})` : `refused by this engine (${refusal.code}: ${refusal.reason})`;
7
+ }
8
+ export function storedArtifactLine(artifactDigest, refusal, consequence) {
9
+ return `center prompt artifact ${artifactDigest.slice(0, 23)}… ${refusal !== undefined ? `is ${refusalClause(refusal)}` : "has no stored copy"} — ${consequence}`;
10
+ }
11
+ function tellRefused(onRefused, refusal) {
12
+ const notifier = createSafeNotifier();
13
+ notifier.notify(() => observeThenableRejection(onRefused?.(refusal), notifier, "artifact-store.onRefused"), "artifact-store.onRefused");
14
+ return undefined;
15
+ }
16
+ function verifiedRead(bytes, artifactDigest, onRefused) {
17
+ const refuse = (refusal) => tellRefused(onRefused, refusal);
5
18
  let parsed;
6
19
  try {
7
20
  parsed = JSON.parse(bytes);
8
21
  }
9
22
  catch {
10
- return undefined;
23
+ return refuse({ code: "shape", reason: "the stored bytes are not JSON" });
11
24
  }
12
25
  const v = verifyPromptArtifact(parsed);
13
- return v.ok && v.artifact.artifactDigest === artifactDigest ? v.artifact : undefined;
26
+ if (!v.ok)
27
+ return refuse({ code: v.code, reason: v.reason });
28
+ return v.artifact.artifactDigest === artifactDigest ? v.artifact : refuse({ code: "digest", reason: "the stored artifact is not the one its digest names" });
14
29
  }
15
30
  export class MemoryPromptArtifactStore {
16
31
  byDigest = new Map();
17
- async get(artifactDigest) {
32
+ async get(artifactDigest, onRefused) {
18
33
  const bytes = this.byDigest.get(artifactDigest);
19
- return bytes === undefined ? undefined : verifiedRead(bytes, artifactDigest);
34
+ return bytes === undefined ? undefined : verifiedRead(bytes, artifactDigest, onRefused);
20
35
  }
21
36
  async put(artifact) {
22
37
  this.byDigest.set(artifact.artifactDigest, JSON.stringify(artifact));
@@ -32,7 +47,7 @@ export class FilePromptArtifactStore {
32
47
  const m = /^sha256:([0-9a-f]{64})$/.exec(artifactDigest);
33
48
  return m ? path.join(this.dir, "artifacts", `${m[1]}.json`) : undefined;
34
49
  }
35
- async get(artifactDigest) {
50
+ async get(artifactDigest, onRefused) {
36
51
  const file = this.fileFor(artifactDigest);
37
52
  if (file === undefined)
38
53
  return undefined;
@@ -40,10 +55,13 @@ export class FilePromptArtifactStore {
40
55
  try {
41
56
  bytes = await fs.readFile(file, "utf8");
42
57
  }
43
- catch {
44
- return undefined;
58
+ catch (err) {
59
+ const code = typeof err === "object" && err !== null ? err.code : undefined;
60
+ if (code === "ENOENT")
61
+ return undefined;
62
+ return tellRefused(onRefused, { code: "unreadable", reason: `the store could not read its file: ${typeof code === "string" ? code : "an error with no code"}` });
45
63
  }
46
- return verifiedRead(bytes, artifactDigest);
64
+ return verifiedRead(bytes, artifactDigest, onRefused);
47
65
  }
48
66
  async put(artifact) {
49
67
  const file = this.fileFor(artifact.artifactDigest);
@@ -99,6 +117,8 @@ export class FilePromptSourceStateStore {
99
117
  export class CenterPromptSource {
100
118
  store;
101
119
  stateStore;
120
+ onError;
121
+ notifier = createSafeNotifier();
102
122
  candidate;
103
123
  state = "unavailable";
104
124
  tail = Promise.resolve();
@@ -110,14 +130,15 @@ export class CenterPromptSource {
110
130
  constructor(opts) {
111
131
  this.store = opts.store;
112
132
  this.stateStore = opts.stateStore ?? new MemoryPromptSourceStateStore();
133
+ this.onError = opts.onError;
113
134
  }
114
135
  current() {
115
136
  return this.candidate;
116
137
  }
117
- async get(artifactDigest) {
138
+ async get(artifactDigest, onRefused) {
118
139
  if (this.candidate?.artifactDigest === artifactDigest)
119
140
  return this.candidate;
120
- return this.store.get(artifactDigest);
141
+ return this.store.get(artifactDigest, onRefused);
121
142
  }
122
143
  async restore() {
123
144
  return this.exclusive(async () => {
@@ -132,9 +153,14 @@ export class CenterPromptSource {
132
153
  this.state = "unavailable";
133
154
  return;
134
155
  }
135
- const stored = await this.store.get(state.artifactDigest);
156
+ const refused = {};
157
+ const stored = await this.store.get(state.artifactDigest, (r) => void (refused.current = r));
136
158
  this.candidate = stored !== undefined ? { ...stored, sourceRevision: state.sourceRevision } : undefined;
137
159
  this.state = stored !== undefined ? "active" : "unavailable";
160
+ if (stored === undefined) {
161
+ const line = new Error(storedArtifactLine(state.artifactDigest, refused.current, "the source boots unavailable: a new session has no center layer until a verified artifact is applied"));
162
+ this.notifier.notify(() => observeThenableRejection(this.onError?.(line, { phase: "prompt-constitution", sessionId: "", classification: "center-source-restore" }), this.notifier, "center-source.onError"), "center-source.onError");
163
+ }
138
164
  });
139
165
  }
140
166
  sourceState() {
@@ -1,7 +1,7 @@
1
1
  import { type OverlayOp, type PromptTrait } from "./catalog/schema.js";
2
2
  import { type PromptOverlay, type SourcedOverlay } from "./overlay.js";
3
3
  /** What an artifact needs from the engine that composes it: the targets it names (each with what kind of target it
4
- * is), the operations it uses, the traits its `when` conditions read, and the bindings its overlays are scoped to. */
4
+ * is), the operations it uses, the traits its `when` conditions read, and the bindings they name. */
5
5
  export interface PromptArtifactRequires {
6
6
  readonly targets: readonly {
7
7
  readonly id: string;
@@ -18,9 +18,9 @@ export interface PromptArtifactEvidence {
18
18
  }
19
19
  /** The digested part of an artifact. */
20
20
  export interface PromptArtifactPayload {
21
- readonly schemaVersion: 2;
21
+ readonly schemaVersion: 3;
22
22
  readonly requires: PromptArtifactRequires;
23
- /** Every overlay is of the `center` class; `scope.binding` absent ⇒ applies under every binding. */
23
+ /** Every overlay is of the `center` class; one with no `when.binding` applies under every binding. */
24
24
  readonly overlays: readonly PromptOverlay[];
25
25
  readonly evidence: PromptArtifactEvidence;
26
26
  }
@@ -33,7 +33,8 @@ export interface PromptArtifact {
33
33
  export interface PublishedPromptArtifact extends PromptArtifact {
34
34
  readonly sourceRevision: string;
35
35
  }
36
- /** Validator bounds: they bound abuse and runaway publishes, not legitimate artifacts. */
36
+ /** Validator bounds: they bound abuse and runaway publishes, not legitimate artifacts. A `…Chars` bound counts Unicode
37
+ * code points (`codePointsOver`, the verifier's one character unit); a `…Bytes` bound counts UTF-8 bytes. */
37
38
  export declare const ARTIFACT_LIMITS: Readonly<{
38
39
  maxOverlays: 64;
39
40
  maxOverlayTextBytes: 32000;
@@ -85,17 +86,17 @@ export type CenterAdoption = {
85
86
  artifact: PromptArtifact;
86
87
  sourceRevision?: string;
87
88
  };
88
- /**
89
- * The center class's overlays for an adopted artifact under a binding: every overlay with no `scope.binding`, and
90
- * every one whose `scope.binding` equals the run's binding, in the artifact's order.
91
- */
92
- export declare function centerOverlaysOf(adoption: CenterAdoption | undefined, binding: string | undefined): readonly SourcedOverlay[];
89
+ /** The center class's overlays for an adopted artifact, in the artifact's order. Which of them apply to a run is their
90
+ * `when` (the binding included), read where every class's is — at the composition. */
91
+ export declare function centerOverlaysOf(adoption: CenterAdoption | undefined): readonly SourcedOverlay[];
93
92
  /** An artifact a source handed a run, re-checked by this engine before the run adopts it; undefined when it fails. */
94
93
  export declare function adoptCenter(artifact: PromptArtifact, sourceRevision: string | undefined): CenterAdoption | undefined;
95
94
  /**
96
95
  * Build a published artifact from overlays and evidence — the one builder (a center's publish pipeline calls it):
97
- * `requires` is derived, the digest computed over the payload's JSON form (what travels), and the result goes
98
- * through the verifier, so the caller holds either a valid artifact or the reason it is not one.
96
+ * `requires` is derived, the digest computed over the payload's JSON form (what travels) with each overlay as the
97
+ * overlay door leaves it (its `when` in normal form, so one meaning is one digest), and the result goes through the
98
+ * verifier, so the caller holds either a valid artifact or the reason it is not one. An overlay the door refuses goes on
99
+ * as it was given: the verifier names it, in its own order of checks.
99
100
  */
100
101
  export declare function buildPromptArtifact(opts: {
101
102
  overlays: readonly PromptOverlay[];
@@ -3,7 +3,7 @@ import { isBandId } from "./bands.js";
3
3
  import { SEMA_DEFAULT_CATALOG } from "./catalog/index.js";
4
4
  import { OVERLAY_OPS, PROMPT_TRAITS, isWordOf } from "./catalog/schema.js";
5
5
  import { canonical } from "./epoch.js";
6
- import { sectionOfMessage, validateOverlay } from "./overlay.js";
6
+ import { codePointsOver, sectionOfMessage, validateOverlay } from "./overlay.js";
7
7
  export const ARTIFACT_LIMITS = Object.freeze({
8
8
  maxOverlays: 64,
9
9
  maxOverlayTextBytes: 32_000,
@@ -37,8 +37,8 @@ export function requiresOf(overlays) {
37
37
  need(o.message, "message");
38
38
  for (const t of [...(o.when?.traits?.allOf ?? []), ...(o.when?.traits?.noneOf ?? [])])
39
39
  traits.add(t);
40
- if (o.scope?.binding !== undefined)
41
- bindings.add(o.scope.binding);
40
+ for (const b of [...(o.when?.binding?.anyOf ?? []), ...(o.when?.binding?.noneOf ?? [])])
41
+ bindings.add(b);
42
42
  }
43
43
  const byText = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
44
44
  return {
@@ -77,24 +77,49 @@ function requiresShaped(v) {
77
77
  strings(v.traits) &&
78
78
  strings(v.bindings));
79
79
  }
80
+ const bindingListOf = (b) => {
81
+ if (b === undefined)
82
+ return undefined;
83
+ const names = b.anyOf !== undefined ? b.anyOf : b.noneOf;
84
+ return { anyOf: b.anyOf !== undefined, names, set: new Set(names) };
85
+ };
86
+ function bindingsMeet(a, b) {
87
+ if (a === undefined || b === undefined)
88
+ return true;
89
+ if (a.anyOf && b.anyOf)
90
+ return a.names.some((x) => b.set.has(x));
91
+ if (a.anyOf)
92
+ return a.names.some((x) => !b.set.has(x));
93
+ if (b.anyOf)
94
+ return b.names.some((x) => !a.set.has(x));
95
+ return true;
96
+ }
80
97
  function replaceConflict(overlays) {
81
- const seen = new Map();
98
+ const groups = new Map();
82
99
  for (const o of overlays) {
83
100
  if (o.op !== "replace")
84
101
  continue;
85
- const key = canonical({ target: o.target, binding: o.scope?.binding ?? null, when: o.when ?? null });
86
- const prior = seen.get(key);
87
- if (prior !== undefined)
88
- return `overlays "${prior}" and "${o.id}" both replace "${o.target}" under the same binding and condition`;
89
- seen.set(key, o.id);
102
+ const { binding, ...rest } = o.when ?? {};
103
+ const key = canonical({ target: o.target, when: rest });
104
+ groups.set(key, [...(groups.get(key) ?? []), { o, list: bindingListOf(binding) }]);
105
+ }
106
+ for (const group of groups.values()) {
107
+ for (let i = 0; i < group.length; i++) {
108
+ for (let j = i + 1; j < group.length; j++) {
109
+ const [{ o: p, list: pl }, { o: q, list: ql }] = [group[i], group[j]];
110
+ if (bindingsMeet(pl, ql)) {
111
+ return `overlays "${p.id}" and "${q.id}" both replace "${p.target}" under one condition that can hold in the same run (write exclusive binding conditions: "noneOf": [x] beside "anyOf": [x])`;
112
+ }
113
+ }
114
+ }
90
115
  }
91
116
  return undefined;
92
117
  }
93
118
  export function verifyPromptArtifact(raw) {
94
119
  if (!isRecord(raw))
95
120
  return { ok: false, code: "shape", reason: "the published artifact is not an object" };
96
- if (typeof raw.sourceRevision !== "string" || raw.sourceRevision.length === 0 || raw.sourceRevision.length > ARTIFACT_LIMITS.maxSourceRevisionChars) {
97
- return { ok: false, code: "shape", reason: "sourceRevision missing or oversized" };
121
+ if (typeof raw.sourceRevision !== "string" || raw.sourceRevision.length === 0 || codePointsOver(raw.sourceRevision, ARTIFACT_LIMITS.maxSourceRevisionChars)) {
122
+ return { ok: false, code: "shape", reason: `sourceRevision missing or over ${ARTIFACT_LIMITS.maxSourceRevisionChars} characters (code points)` };
98
123
  }
99
124
  const v = verifyPromptArtifactBody(raw);
100
125
  return v.ok ? { ok: true, artifact: { ...v.artifact, sourceRevision: raw.sourceRevision } } : v;
@@ -111,8 +136,10 @@ export function verifyPromptArtifactBody(raw) {
111
136
  const payload = raw.payload;
112
137
  if (!isRecord(payload))
113
138
  return fail("shape", "payload is not an object");
114
- if (payload.schemaVersion !== 2)
115
- return fail("schema", `unsupported schemaVersion ${JSON.stringify(payload.schemaVersion)?.slice(0, 40)} (this engine reads schemaVersion 2 only)`);
139
+ if (payload.schemaVersion !== 3) {
140
+ const republish = payload.schemaVersion === 2 ? `; republish under schemaVersion 3 — an overlay's "scope": { "binding": b } becomes "when": { "binding": { "anyOf": [b] } }` : "";
141
+ return fail("schema", `unsupported schemaVersion ${JSON.stringify(payload.schemaVersion)?.slice(0, 40)} (this engine reads schemaVersion 3 only${republish})`);
142
+ }
116
143
  for (const k of Object.keys(payload))
117
144
  if (k !== "schemaVersion" && k !== "requires" && k !== "overlays" && k !== "evidence")
118
145
  return fail("shape", `payload carries ${JSON.stringify(k).slice(0, 60)}`);
@@ -151,11 +178,11 @@ export function verifyPromptArtifactBody(raw) {
151
178
  if (conflict !== undefined)
152
179
  return fail("overlay", conflict);
153
180
  const evidence = payload.evidence;
154
- if (!isRecord(evidence) || typeof evidence.intent !== "string" || evidence.intent.trim() === "" || evidence.intent.length > ARTIFACT_LIMITS.maxIntentChars) {
155
- return fail("evidence", `evidence.intent is a non-blank string of at most ${ARTIFACT_LIMITS.maxIntentChars} characters`);
181
+ if (!isRecord(evidence) || typeof evidence.intent !== "string" || evidence.intent.trim() === "" || codePointsOver(evidence.intent, ARTIFACT_LIMITS.maxIntentChars)) {
182
+ return fail("evidence", `evidence.intent is a non-blank string of at most ${ARTIFACT_LIMITS.maxIntentChars} characters (code points)`);
156
183
  }
157
- if (!Array.isArray(evidence.refs) || evidence.refs.length > ARTIFACT_LIMITS.maxRefs || evidence.refs.some((r) => typeof r !== "string" || r.trim() === "" || r.length > ARTIFACT_LIMITS.maxRefChars)) {
158
- return fail("evidence", `evidence.refs is a list of at most ${ARTIFACT_LIMITS.maxRefs} non-blank references`);
184
+ if (!Array.isArray(evidence.refs) || evidence.refs.length > ARTIFACT_LIMITS.maxRefs || evidence.refs.some((r) => typeof r !== "string" || r.trim() === "" || codePointsOver(r, ARTIFACT_LIMITS.maxRefChars))) {
185
+ return fail("evidence", `evidence.refs is a list of at most ${ARTIFACT_LIMITS.maxRefs} non-blank references of at most ${ARTIFACT_LIMITS.maxRefChars} characters (code points) each`);
159
186
  }
160
187
  for (const k of Object.keys(evidence))
161
188
  if (k !== "intent" && k !== "refs")
@@ -165,7 +192,7 @@ export function verifyPromptArtifactBody(raw) {
165
192
  return fail("evidence", `overlay "${behaviorReplace.id}" replaces core-behavior text; evidence.refs names at least one evaluation or golden`);
166
193
  }
167
194
  const normalized = {
168
- schemaVersion: 2,
195
+ schemaVersion: 3,
169
196
  requires: requiresOf(overlays),
170
197
  overlays,
171
198
  evidence: { intent: evidence.intent, refs: [...evidence.refs] },
@@ -175,16 +202,20 @@ export function verifyPromptArtifactBody(raw) {
175
202
  return { ok: true, artifact: { payload: normalized, artifactDigest: raw.artifactDigest } };
176
203
  }
177
204
  const CENTER_ORIGIN = Object.freeze({ source: "center", by: "center" });
178
- export function centerOverlaysOf(adoption, binding) {
205
+ export function centerOverlaysOf(adoption) {
179
206
  if (adoption === undefined)
180
207
  return [];
181
- return adoption.artifact.payload.overlays.filter((o) => o.scope?.binding === undefined || o.scope.binding === binding).map((overlay) => ({ overlay, origin: CENTER_ORIGIN }));
208
+ return adoption.artifact.payload.overlays.map((overlay) => ({ overlay, origin: CENTER_ORIGIN }));
182
209
  }
183
210
  export function adoptCenter(artifact, sourceRevision) {
184
211
  const v = verifyPromptArtifactBody(artifact);
185
212
  return v.ok ? { artifact: v.artifact, ...(sourceRevision !== undefined ? { sourceRevision } : {}) } : undefined;
186
213
  }
187
214
  export function buildPromptArtifact(opts) {
188
- const payload = JSON.parse(JSON.stringify({ schemaVersion: 2, requires: requiresOf(opts.overlays), overlays: opts.overlays, evidence: opts.evidence }));
215
+ const overlays = opts.overlays.map((o) => {
216
+ const v = validateOverlay(o, { source: "center" });
217
+ return "overlay" in v ? v.overlay : o;
218
+ });
219
+ const payload = JSON.parse(JSON.stringify({ schemaVersion: 3, requires: requiresOf(overlays), overlays, evidence: opts.evidence }));
189
220
  return verifyPromptArtifact({ payload, artifactDigest: sha256Hex(canonical(payload)), sourceRevision: opts.sourceRevision });
190
221
  }
@@ -24,6 +24,8 @@ export interface AssembleInputs {
24
24
  role?: HostRole;
25
25
  form?: HostForm;
26
26
  };
27
+ /** The run's binding (`TaskSpec.promptBinding`), read by every overlay's `when.binding`. */
28
+ binding?: string;
27
29
  tools: readonly AgentTool[];
28
30
  facts: PromptRuntimeFacts;
29
31
  /** The task's working directory (the Cowork role base's `{{workspaceFolder}}`). */
@@ -3,7 +3,7 @@ import { composeCatalog, layoutSystemBlocks } from "./composer.js";
3
3
  import { ABSENT_THIS_RUN } from "./overlay.js";
4
4
  function supersededLine(s) {
5
5
  return s.supersededBy === ABSENT_THIS_RUN
6
- ? `prompt overlay "${s.id}" (${s.source}, ${s.by}) on "${s.target}" changed nothing: the selector left its target, or the catalog message it takes its text from, out of this run`
6
+ ? `prompt overlay "${s.id}" (${s.source}, ${s.by}) on "${s.target}" changed nothing: what it names is not in this run (the selector chose no variant of its target, or of the catalog message it takes its text from; or the lower entry it drops was never added — its own condition left it out, or the layer that added it is not in force)`
7
7
  : `prompt overlay "${s.id}" (${s.source}, ${s.by}) on "${s.target}" is superseded by "${s.supersededBy}", applied after it (source order task < center < deploy; within a source, declaration order)`;
8
8
  }
9
9
  export function assemblePrompt(inputs) {
@@ -17,6 +17,7 @@ export function assemblePrompt(inputs) {
17
17
  const plan = {
18
18
  catalog: SEMA_DEFAULT_CATALOG,
19
19
  ...(inputs.host !== undefined ? { host: inputs.host } : {}),
20
+ ...(inputs.binding !== undefined ? { binding: inputs.binding } : {}),
20
21
  overlays: [...inputs.taskOverlays, ...inputs.centerOverlays, ...inputs.deployOverlays],
21
22
  };
22
23
  const toolNames = inputs.tools.map((t) => t.name);
@@ -1,5 +1,6 @@
1
1
  import { CONSTITUTION_MESSAGES } from "./constitution.js";
2
2
  import { CORE_BEHAVIOR_MESSAGES } from "./core-behavior.js";
3
+ import { MEMORY_MESSAGES } from "./memory.js";
3
4
  import { MODE_MESSAGES } from "./modes.js";
4
5
  import { ROLE_MESSAGES } from "./role.js";
5
6
  import { SECTIONS } from "./sections.js";
@@ -9,7 +10,7 @@ import { RENDERER_IDS, isWordOf } from "./schema.js";
9
10
  export const SEMA_DEFAULT_CATALOG = {
10
11
  id: "sema-default@2",
11
12
  bands: BANDS,
12
- messages: [...ROLE_MESSAGES, ...CONSTITUTION_MESSAGES, ...CORE_BEHAVIOR_MESSAGES, ...MODE_MESSAGES, ...TEAM_MESSAGES],
13
+ messages: [...ROLE_MESSAGES, ...CONSTITUTION_MESSAGES, ...CORE_BEHAVIOR_MESSAGES, ...MODE_MESSAGES, ...TEAM_MESSAGES, ...MEMORY_MESSAGES],
13
14
  sections: SECTIONS,
14
15
  };
15
16
  export function validateCatalog(catalog) {
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Catalog messages of the `memory` band. No section places them: the band's bytes are the memory renderer's
3
+ * input, which the memory phase composes (prepare-memory.ts `composeInjection`); these are the texts it reads.
4
+ *
5
+ * - `core/memory.no-persistent-store`: the run has no memory store configured and no channel to persist through
6
+ * (the roster-wide persistence verdict is false). Without it the model, asked "remember X", answers with a
7
+ * receipt for a save that has nowhere to land. A store that is configured but did not mount this run does not
8
+ * get this sentence (it is categorical, and a mount can succeed next time): it gets the engine's
9
+ * writes-unavailable-right-now notice, with the failed mount as its cause. Name-free: it names no tool, so no
10
+ * toolkit can make it stale.
11
+ */
12
+ import type { CatalogMessage } from "./schema.js";
13
+ export declare const MEMORY_MESSAGES: readonly CatalogMessage[];
@@ -0,0 +1,6 @@
1
+ const NO_PERSISTENT_STORE = `# Memory
2
+
3
+ You have no persistent memory store: what is said in this conversation is not automatically retained for future sessions. If the user asks you to remember something for later, say plainly that you cannot store it — do not claim to have noted or saved it. If you have file tools, you may offer to write it into a project file instead (files persist; conversation memory does not).`;
4
+ export const MEMORY_MESSAGES = [
5
+ { id: "core/memory.no-persistent-store", variants: [{ id: "default", when: {}, text: NO_PERSISTENT_STORE }], attribution: { form: "sema-owned" } },
6
+ ];
@@ -33,6 +33,9 @@ export interface BandRow {
33
33
  * task (`TaskSpec.promptRole`); absent ⇒ `general`. */
34
34
  export declare const HOST_ROLES: readonly ["general", "code", "coordinator", "cowork"];
35
35
  export type HostRole = (typeof HOST_ROLES)[number];
36
+ /** Membership test for {@link HostRole} — the one reading of "is this a role base the engine selects": the task door
37
+ * (`TaskSpec.promptRole`) and the overlay grammar (`when.host.role`) both ask it. */
38
+ export declare function isHostRole(v: unknown): v is HostRole;
36
39
  /** The host form: `minimal` leaves the core-behavior band out (the locked bands stay). Written only by
37
40
  * `TaskSpec.promptForm`; absent ⇒ `standard`. */
38
41
  export declare const HOST_FORMS: readonly ["standard", "minimal"];
@@ -69,15 +72,29 @@ export interface Attribution {
69
72
  export interface Variant {
70
73
  /** Reported in the manifest — one of the answers to "why this sentence". */
71
74
  readonly id: string;
72
- readonly when: VariantWhen;
75
+ readonly when: CatalogVariantWhen;
73
76
  readonly text: string;
74
77
  }
75
- /** The selector condition a variant (or an overlay) is chosen on: a conjunction over traits, host and facts. */
78
+ /** A condition on the run's binding (`TaskSpec.promptBinding`, a deployment's scenario name): exactly one list. A run
79
+ * with no binding is outside every `anyOf` and inside every `noneOf`. */
80
+ export type BindingCondition = {
81
+ readonly anyOf: readonly string[];
82
+ readonly noneOf?: never;
83
+ } | {
84
+ readonly noneOf: readonly string[];
85
+ readonly anyOf?: never;
86
+ };
87
+ /** The selector condition a variant (or an overlay) is chosen on: a conjunction over traits, host, facts and binding. */
76
88
  export interface VariantWhen {
77
89
  readonly traits?: TraitCondition;
78
90
  readonly host?: HostCondition;
79
91
  readonly facts?: FactConjunction;
92
+ readonly binding?: BindingCondition;
80
93
  }
94
+ /** The condition a BUNDLED catalog variant is chosen on: a {@link VariantWhen} without the binding. A binding is a
95
+ * deployment's own vocabulary (scenario names the engine cannot know), so no catalog variant can name one — the type
96
+ * refuses it where the catalog data is written. */
97
+ export type CatalogVariantWhen = Omit<VariantWhen, "binding">;
81
98
  export interface CatalogMessage {
82
99
  /** `core/<area>.<name>`. */
83
100
  readonly id: string;
@@ -3,6 +3,9 @@ export function isWordOf(words, v) {
3
3
  }
4
4
  export const OVERLAY_OPS = Object.freeze(["replace", "append", "add", "drop"]);
5
5
  export const HOST_ROLES = Object.freeze(["general", "code", "coordinator", "cowork"]);
6
+ export function isHostRole(v) {
7
+ return isWordOf(HOST_ROLES, v);
8
+ }
6
9
  export const HOST_FORMS = Object.freeze(["standard", "minimal"]);
7
10
  export const PROMPT_TRAITS = Object.freeze(["lean_prompt", "fable_5_mitigations"]);
8
11
  export const PROMPT_FACT_NAMES = Object.freeze([
@@ -9,6 +9,8 @@ export interface CompositionPlan {
9
9
  readonly role?: HostRole;
10
10
  readonly form?: HostForm;
11
11
  };
12
+ /** The run's binding (`TaskSpec.promptBinding`; absent ⇒ none) — what every overlay's `when.binding` reads. */
13
+ readonly binding?: string;
12
14
  /** Validated overlays, already in application order (`bundled < task < center < deploy`, then declaration order). */
13
15
  readonly overlays?: readonly SourcedOverlay[];
14
16
  }
@@ -91,7 +91,7 @@ function renderAdded(e, fold) {
91
91
  };
92
92
  }
93
93
  export function composeCatalog(plan, inputs) {
94
- const s = selectorsOf(inputs.facts, plan.host);
94
+ const s = selectorsOf(inputs.facts, plan.host, plan.binding);
95
95
  const bases = basesOf(plan.catalog, inputs, s);
96
96
  const selectedText = selectedTextOf(plan.catalog, bases);
97
97
  const overlayText = (o) => {
@@ -10,11 +10,8 @@ export interface PromptOverlay {
10
10
  readonly text?: string;
11
11
  /** A catalog message whose selected variant is the overlay's text; exclusive with `text`. */
12
12
  readonly message?: string;
13
- /** Selects a center artifact's overlay by binding; carried by center artifacts only. */
14
- readonly scope?: {
15
- readonly binding?: string;
16
- };
17
- /** The overlay applies to this run only when this holds (the variant selector's own closed vocabulary). */
13
+ /** The overlay applies to this run only when this holds (the variant selector's own vocabulary, the binding
14
+ * included) — the one applicability condition of every source class. */
18
15
  readonly when?: VariantWhen;
19
16
  }
20
17
  /** The source classes in application order (later wins). `bundled` is the catalog itself and writes no overlay. */
@@ -59,10 +56,23 @@ export declare function receivePromptData(raw: unknown): {
59
56
  } | {
60
57
  readonly notData: string;
61
58
  };
59
+ /** The longest binding a task may name (`TaskSpec.promptBinding`), counted in Unicode code points (`[...s].length`) — not
60
+ * in UTF-16 code units; every binding a `when.binding` list names is held to the same bound. Exported so a host screens
61
+ * with the engine's number and the engine's unit. Code points are the one unit of every character bound of the prompt
62
+ * artifact verifier ({@link codePointsOver}: a binding, an overlay id, `sourceRevision`, `evidence.intent`, a
63
+ * reference), so a character outside the BMP (an emoji, a rare CJK ideograph) counts one, not two; the overlay text
64
+ * budgets are UTF-8 bytes and are named so. */
65
+ export declare const PROMPT_BINDING_MAX_CHARS = 128;
66
+ /** The most bindings one `when.binding` list names — it bounds what a publisher can make the conflict check walk. */
67
+ export declare const PROMPT_BINDING_LIST_MAX = 64;
68
+ /** Whether `s` holds more than `max` Unicode code points — the one length unit of the prompt artifact verifier's
69
+ * character bounds. A string of at most `max` code units fits (a code point is one or two of them); one of more than
70
+ * twice `max` does not; only a string in between is counted — counted, never copied whole. */
71
+ export declare function codePointsOver(s: string, max: number): boolean;
62
72
  /**
63
- * The run's center binding (`TaskSpec.promptBinding`): the key a center artifact's scoped overlays are selected by.
64
- * Its one writer is the task (a deployment names its scenario); absent ⇒ undefined. A value outside the grammar is
65
- * refused by name (`config.prompt_selector_invalid`), never read as absent.
73
+ * The run's binding (`TaskSpec.promptBinding`): the scenario a `when.binding` condition reads, on an overlay of any
74
+ * source class. Its one writer is the task (a deployment names its scenario); absent ⇒ undefined. A value outside the
75
+ * grammar is refused by name (`config.prompt_selector_invalid`), never read as absent.
66
76
  */
67
77
  export declare function resolvePromptBinding(task: {
68
78
  readonly promptBinding?: unknown;
@@ -79,9 +89,9 @@ export interface OverlayValidationContext {
79
89
  readonly lowerAdds?: ReadonlyMap<string, BandId>;
80
90
  }
81
91
  /**
82
- * Validate one overlay for one source class. Returns the overlay (a frozen copy of its known members) or why it is
83
- * a violation: shape, namespace, op, text/message, `when`, `scope`, target existence, band authorization (and the
84
- * band's mods write port), and the locked-text guard.
92
+ * Validate one overlay for one source class. Returns the overlay (a frozen copy of its known members, its `when` in
93
+ * normal form — {@link canonicalWhen}) or why it is a violation: shape, namespace, op, text/message, `when`, target existence, band authorization (and the band's mods
94
+ * write port), and the locked-text guard.
85
95
  */
86
96
  export declare function validateOverlay(raw: unknown, ctx: OverlayValidationContext): {
87
97
  readonly overlay: PromptOverlay;