@sema-agent/core 7.55.0 → 7.56.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 (418) hide show
  1. package/CHANGELOG.md +513 -0
  2. package/dist/agents/cascade.js +1 -1
  3. package/dist/agents/cc-peer-directory.js +1 -0
  4. package/dist/agents/child-model-seat.d.ts +0 -11
  5. package/dist/agents/child-model-seat.js +0 -3
  6. package/dist/agents/list-agents-tool.d.ts +3 -1
  7. package/dist/agents/list-agents-tool.js +7 -7
  8. package/dist/agents/peer-directory.d.ts +22 -4
  9. package/dist/agents/peer-directory.js +24 -2
  10. package/dist/agents/peer-session-drain.js +2 -2
  11. package/dist/agents/repair-loop.js +2 -2
  12. package/dist/agents/send-message-tool.js +4 -4
  13. package/dist/agents/subagent.d.ts +6 -4
  14. package/dist/agents/subagent.js +64 -60
  15. package/dist/agents/tool-filter.d.ts +7 -5
  16. package/dist/agents/tool-filter.js +12 -9
  17. package/dist/agents/verify.d.ts +3 -2
  18. package/dist/agents/verify.js +3 -3
  19. package/dist/brain/anthropic.js +30 -73
  20. package/dist/brain/media-degrade.d.ts +18 -7
  21. package/dist/brain/media-degrade.js +13 -2
  22. package/dist/brain/model-capability.d.ts +37 -0
  23. package/dist/brain/model-capability.js +34 -0
  24. package/dist/brain/model-presets.d.ts +2 -8
  25. package/dist/brain/model-presets.js +1 -6
  26. package/dist/brain/open-responses.js +13 -26
  27. package/dist/brain/openai.js +14 -87
  28. package/dist/brain/reasoning-request.d.ts +96 -0
  29. package/dist/brain/reasoning-request.js +174 -0
  30. package/dist/brain/reasoning.d.ts +11 -11
  31. package/dist/brain/reasoning.js +4 -3
  32. package/dist/brain/request-params.d.ts +7 -4
  33. package/dist/brain/request-params.js +7 -4
  34. package/dist/brain/status-sink.d.ts +20 -0
  35. package/dist/brain/status-sink.js +7 -0
  36. package/dist/core/a2a.d.ts +2 -1
  37. package/dist/core/a2a.js +19 -13
  38. package/dist/core/agent-definition.d.ts +10 -0
  39. package/dist/core/allow-entry.d.ts +20 -13
  40. package/dist/core/allow-entry.js +26 -22
  41. package/dist/core/allow-rule-shadow.d.ts +3 -3
  42. package/dist/core/ask-origin.d.ts +64 -18
  43. package/dist/core/ask-origin.js +63 -23
  44. package/dist/core/ask-question.d.ts +51 -8
  45. package/dist/core/ask-question.js +3 -4
  46. package/dist/core/ask-unresolvable-notice.d.ts +8 -3
  47. package/dist/core/ask-unresolvable-notice.js +16 -2
  48. package/dist/core/checkpoint-generation.d.ts +53 -0
  49. package/dist/core/checkpoint-generation.js +35 -0
  50. package/dist/core/checkpoint-queries.d.ts +180 -0
  51. package/dist/core/checkpoint-queries.js +272 -0
  52. package/dist/core/checkpoint-requirements.d.ts +13 -1
  53. package/dist/core/checkpoint-requirements.js +4 -0
  54. package/dist/core/checkpoint-store.d.ts +88 -24
  55. package/dist/core/checkpoint-store.js +24 -9
  56. package/dist/core/cross-boundary-taint.d.ts +6 -5
  57. package/dist/core/cross-boundary-taint.js +1 -3
  58. package/dist/core/decision-receipt.d.ts +37 -3
  59. package/dist/core/decision-receipt.js +17 -17
  60. package/dist/core/delegation-not-started.d.ts +6 -0
  61. package/dist/core/delegation-not-started.js +17 -0
  62. package/dist/core/engine-attestation.d.ts +93 -0
  63. package/dist/core/engine-attestation.js +70 -0
  64. package/dist/core/engine-capabilities.d.ts +4 -1
  65. package/dist/core/engine-capabilities.js +1 -1
  66. package/dist/core/engine-notice.d.ts +30 -11
  67. package/dist/core/file-history-store.d.ts +90 -14
  68. package/dist/core/file-history-store.js +23 -15
  69. package/dist/core/gate-fold.d.ts +1 -1
  70. package/dist/core/gate-fold.js +22 -10
  71. package/dist/core/gate-lanes.js +73 -64
  72. package/dist/core/gate-outcome.d.ts +26 -5
  73. package/dist/core/gate-outcome.js +26 -0
  74. package/dist/core/governance-codes.d.ts +5 -1
  75. package/dist/core/governance-codes.js +6 -0
  76. package/dist/core/hooks.d.ts +17 -16
  77. package/dist/core/hooks.js +3 -1
  78. package/dist/core/host-floor.d.ts +128 -0
  79. package/dist/core/host-floor.js +206 -0
  80. package/dist/core/host-target.d.ts +38 -3
  81. package/dist/core/host-target.js +38 -5
  82. package/dist/core/mcp.d.ts +5 -1
  83. package/dist/core/mcp.js +67 -41
  84. package/dist/core/memory-engine/delegation-settlement.js +15 -24
  85. package/dist/core/memory-engine/engine.js +4 -4
  86. package/dist/core/memory-engine/layout.d.ts +60 -8
  87. package/dist/core/memory-engine/layout.js +65 -6
  88. package/dist/core/model-seat.d.ts +10 -3
  89. package/dist/core/mods/classic-events.d.ts +67 -0
  90. package/dist/core/mods/classic-events.js +1 -0
  91. package/dist/core/mods/dollar.d.ts +13 -0
  92. package/dist/core/mods/extension-call.d.ts +31 -0
  93. package/dist/core/mods/extension-call.js +43 -0
  94. package/dist/core/mods/kernel/dispatch.d.ts +20 -3
  95. package/dist/core/mods/kernel/dispatch.js +140 -69
  96. package/dist/core/mods/kernel/events.d.ts +47 -11
  97. package/dist/core/mods/kernel/events.js +66 -42
  98. package/dist/core/mods/kernel/index.d.ts +4 -4
  99. package/dist/core/mods/kernel/index.js +2 -2
  100. package/dist/core/mods/kernel/receive.d.ts +39 -5
  101. package/dist/core/mods/kernel/receive.js +290 -17
  102. package/dist/core/mods/kernel/registration.d.ts +33 -2
  103. package/dist/core/mods/kernel/registration.js +6 -7
  104. package/dist/core/mods/kernel/site-rules.d.ts +44 -16
  105. package/dist/core/mods/kernel/site-rules.js +24 -42
  106. package/dist/core/mods/spec-seats.d.ts +69 -0
  107. package/dist/core/mods/spec-seats.js +114 -0
  108. package/dist/core/mods/stations.d.ts +28 -8
  109. package/dist/core/mods/stations.js +29 -13
  110. package/dist/core/mods/tool-call.d.ts +79 -0
  111. package/dist/core/mods/tool-call.js +152 -0
  112. package/dist/core/namespaced-rule-name.d.ts +32 -38
  113. package/dist/core/namespaced-rule-name.js +29 -36
  114. package/dist/core/parent-spec-seat.d.ts +6 -1
  115. package/dist/core/parent-spec-seat.js +2 -0
  116. package/dist/core/park-selfcheck.js +1 -0
  117. package/dist/core/permission-rule-consent.d.ts +19 -0
  118. package/dist/core/permission-rule-consent.js +116 -26
  119. package/dist/core/permission-rule-model.d.ts +14 -3
  120. package/dist/core/permission-rule-model.js +39 -24
  121. package/dist/core/permission-rule-org.d.ts +6 -2
  122. package/dist/core/permission-rule-org.js +7 -3
  123. package/dist/core/permission-rule-provider.d.ts +5 -2
  124. package/dist/core/permission-rule-provider.js +41 -11
  125. package/dist/core/permission-rule-store.d.ts +3 -2
  126. package/dist/core/permission-rule-store.js +11 -2
  127. package/dist/core/permission-rule-sync.js +3 -2
  128. package/dist/core/permission-rule-syntax.d.ts +5 -4
  129. package/dist/core/permission-rule-syntax.js +2 -2
  130. package/dist/core/permission-rules.d.ts +4 -4
  131. package/dist/core/permission-rules.js +11 -20
  132. package/dist/core/persisted-rule-arms.d.ts +2 -2
  133. package/dist/core/persisted-rule-arms.js +10 -12
  134. package/dist/core/principal-name.d.ts +20 -0
  135. package/dist/core/principal-name.js +22 -0
  136. package/dist/core/prompt-section-seat.js +4 -6
  137. package/dist/core/protocol-http-auth.d.ts +26 -5
  138. package/dist/core/protocol-http-auth.js +137 -4
  139. package/dist/core/retired-keys.d.ts +16 -0
  140. package/dist/core/retired-keys.js +16 -4
  141. package/dist/core/rewrite-rejudge.d.ts +60 -0
  142. package/dist/core/rewrite-rejudge.js +36 -0
  143. package/dist/core/roles.d.ts +28 -1
  144. package/dist/core/roles.js +31 -19
  145. package/dist/core/rule-lane-reads.d.ts +20 -15
  146. package/dist/core/rule-lane-reads.js +45 -17
  147. package/dist/core/rule-owner.d.ts +3 -3
  148. package/dist/core/rule-owner.js +3 -2
  149. package/dist/core/runner/announce-once-ledger.d.ts +14 -0
  150. package/dist/core/runner/announce-once-ledger.js +5 -0
  151. package/dist/core/runner/assemble-result.d.ts +1 -0
  152. package/dist/core/runner/assemble-result.js +1 -1
  153. package/dist/core/runner/call-hook-stations.d.ts +3 -2
  154. package/dist/core/runner/caller-roster.d.ts +16 -4
  155. package/dist/core/runner/caller-roster.js +33 -6
  156. package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
  157. package/dist/core/runner/capability-withheld-seat.js +24 -0
  158. package/dist/core/runner/classifier-ladder.d.ts +7 -9
  159. package/dist/core/runner/classifier-ladder.js +3 -3
  160. package/dist/core/runner/construction-doors.js +3 -0
  161. package/dist/core/runner/contracts.d.ts +28 -25
  162. package/dist/core/runner/cross-boundary-lane.d.ts +17 -13
  163. package/dist/core/runner/cross-boundary-lane.js +40 -27
  164. package/dist/core/runner/denial-limit-arms.d.ts +2 -2
  165. package/dist/core/runner/denial-limit-arms.js +1 -1
  166. package/dist/core/runner/file-history-request.d.ts +18 -0
  167. package/dist/core/runner/file-history-request.js +21 -13
  168. package/dist/core/runner/file-history-restore.d.ts +45 -0
  169. package/dist/core/runner/file-history-restore.js +46 -0
  170. package/dist/core/runner/gate-exit.d.ts +3 -3
  171. package/dist/core/runner/gate-exit.js +4 -3
  172. package/dist/core/runner/image.d.ts +7 -17
  173. package/dist/core/runner/image.js +9 -60
  174. package/dist/core/runner/permission-rule-lanes.d.ts +4 -4
  175. package/dist/core/runner/permission-rule-lanes.js +26 -10
  176. package/dist/core/runner/person-path-rows.d.ts +12 -9
  177. package/dist/core/runner/person-path-rows.js +18 -7
  178. package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
  179. package/dist/core/runner/prepare-ask-lane.js +5 -2
  180. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
  181. package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
  182. package/dist/core/runner/prepare-config-doors.d.ts +29 -38
  183. package/dist/core/runner/prepare-config-doors.js +42 -25
  184. package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
  185. package/dist/core/runner/prepare-defer-classify.js +7 -8
  186. package/dist/core/runner/prepare-file-history.d.ts +48 -8
  187. package/dist/core/runner/prepare-file-history.js +7 -39
  188. package/dist/core/runner/prepare-gate-stations.d.ts +1 -1
  189. package/dist/core/runner/prepare-gate-stations.js +10 -1
  190. package/dist/core/runner/prepare-hands-readface.d.ts +2 -8
  191. package/dist/core/runner/prepare-hands-readface.js +15 -14
  192. package/dist/core/runner/prepare-inherited-gate.d.ts +6 -3
  193. package/dist/core/runner/prepare-inherited-gate.js +5 -3
  194. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  195. package/dist/core/runner/prepare-park-ask.js +5 -5
  196. package/dist/core/runner/prepare-policy-chain.d.ts +6 -3
  197. package/dist/core/runner/prepare-policy-chain.js +65 -72
  198. package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
  199. package/dist/core/runner/prepare-prompt-assembly.js +10 -8
  200. package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
  201. package/dist/core/runner/prepare-prompt-inputs.js +3 -5
  202. package/dist/core/runner/prepare-protocol-tools.d.ts +2 -0
  203. package/dist/core/runner/prepare-protocol-tools.js +3 -3
  204. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  205. package/dist/core/runner/prepare-safety-scan.js +0 -17
  206. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
  207. package/dist/core/runner/prepare-suspend-saga.js +5 -6
  208. package/dist/core/runner/prepare-task.d.ts +1 -1
  209. package/dist/core/runner/prepare-task.js +29 -20
  210. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +7 -4
  211. package/dist/core/runner/prepare-tool-disclosure-mount.js +20 -16
  212. package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
  213. package/dist/core/runner/prepare-turn-wiring.js +2 -7
  214. package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -2
  215. package/dist/core/runner/prepare-wiring-manifest.js +3 -3
  216. package/dist/core/runner/resume-admission.js +1 -1
  217. package/dist/core/runner/resume-apply.js +11 -12
  218. package/dist/core/runner/resume-checkpoint-screen.js +3 -0
  219. package/dist/core/runner/resume-claim.d.ts +4 -2
  220. package/dist/core/runner/resume-claim.js +15 -8
  221. package/dist/core/runner/retired-runner-deps.js +1 -0
  222. package/dist/core/runner/rewind-files.d.ts +15 -0
  223. package/dist/core/runner/rewind-files.js +132 -0
  224. package/dist/core/runner/run-attachment-seats.js +2 -1
  225. package/dist/core/runner/run-brain-sinks.d.ts +4 -1
  226. package/dist/core/runner/run-brain-sinks.js +4 -2
  227. package/dist/core/runner/run-harness-handlers.js +12 -4
  228. package/dist/core/runner/run-leg.d.ts +1 -1
  229. package/dist/core/runner/run-leg.js +30 -18
  230. package/dist/core/runner/run-settle-and-teardown.js +2 -1
  231. package/dist/core/runner/run-turn-boundary.js +1 -10
  232. package/dist/core/runner/runtask.d.ts +3 -0
  233. package/dist/core/runner/runtask.js +8 -9
  234. package/dist/core/runner/session-read-file-states.d.ts +15 -0
  235. package/dist/core/runner/session-read-file-states.js +28 -0
  236. package/dist/core/runner/session-rule-policy.js +4 -3
  237. package/dist/core/runner/shell-env-withheld.d.ts +33 -0
  238. package/dist/core/runner/shell-env-withheld.js +51 -0
  239. package/dist/core/runner/skills-seat.js +6 -2
  240. package/dist/core/runner/stream-settle-backstop.js +1 -1
  241. package/dist/core/runner/sub-agent-deny-lists.js +4 -7
  242. package/dist/core/runner/synthetic-tools.d.ts +6 -0
  243. package/dist/core/runner/synthetic-tools.js +4 -1
  244. package/dist/core/runner/tool-disclosure.d.ts +10 -12
  245. package/dist/core/runner/tool-disclosure.js +1 -3
  246. package/dist/core/runner/tool-end-body.d.ts +1 -1
  247. package/dist/core/runner/tool-end-body.js +10 -9
  248. package/dist/core/runner/tool-not-executed.d.ts +5 -0
  249. package/dist/core/runner/tool-not-executed.js +11 -0
  250. package/dist/core/runner/tool-occurrence.d.ts +8 -0
  251. package/dist/core/runner/tool-occurrence.js +7 -0
  252. package/dist/core/runner/turn-attachments.d.ts +33 -15
  253. package/dist/core/runner/turn-attachments.js +62 -37
  254. package/dist/core/runner-deps.d.ts +5 -5
  255. package/dist/core/safety-axis-vocab.d.ts +6 -4
  256. package/dist/core/safety-axis-vocab.js +6 -5
  257. package/dist/core/safety-merge-corpus.js +11 -3
  258. package/dist/core/secret-env.d.ts +26 -3
  259. package/dist/core/secret-env.js +44 -8
  260. package/dist/core/select-model.js +3 -3
  261. package/dist/core/session-policy-store.d.ts +27 -15
  262. package/dist/core/session-policy-store.js +156 -36
  263. package/dist/core/session-store.d.ts +29 -10
  264. package/dist/core/session-store.js +51 -19
  265. package/dist/core/session.d.ts +82 -16
  266. package/dist/core/session.js +53 -0
  267. package/dist/core/shell-read-silence.d.ts +11 -12
  268. package/dist/core/shell-read-silence.js +9 -5
  269. package/dist/core/shell-run-candidates.js +3 -1
  270. package/dist/core/shell-wrapper-projection.js +1 -1
  271. package/dist/core/shell-wrapper-table.d.ts +14 -7
  272. package/dist/core/shell-wrapper-table.js +5 -4
  273. package/dist/core/side-query.js +10 -1
  274. package/dist/core/skill-grant.d.ts +7 -2
  275. package/dist/core/skill-grant.js +5 -0
  276. package/dist/core/skills-directory.js +6 -2
  277. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
  278. package/dist/core/store-contracts/checkpoint-store-contract.js +140 -2
  279. package/dist/core/store-contracts/file-history-store-contract.js +31 -1
  280. package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
  281. package/dist/core/store-contracts/peer-directory-contract.js +14 -2
  282. package/dist/core/store-contracts/session-repo-contract.js +51 -0
  283. package/dist/core/store-contracts/session-store-contract.d.ts +2 -1
  284. package/dist/core/store-contracts/session-store-contract.js +150 -14
  285. package/dist/core/stub-env.d.ts +2 -0
  286. package/dist/core/stub-env.js +1 -0
  287. package/dist/core/synthetic-read-verdict.js +3 -3
  288. package/dist/core/task-event.d.ts +32 -4
  289. package/dist/core/task-notification.d.ts +16 -12
  290. package/dist/core/task-registry.d.ts +9 -0
  291. package/dist/core/task-registry.js +11 -1
  292. package/dist/core/task-result.d.ts +18 -0
  293. package/dist/core/task-spec.d.ts +18 -13
  294. package/dist/core/tighten-task-spec.js +2 -1
  295. package/dist/core/tool-catalog-entries.d.ts +17 -0
  296. package/dist/core/tool-catalog-entries.js +13 -2
  297. package/dist/core/tool-check-reading.d.ts +11 -0
  298. package/dist/core/tool-check-reading.js +11 -6
  299. package/dist/core/tool-model-gate.d.ts +2 -2
  300. package/dist/core/tool-name-aliases.d.ts +16 -16
  301. package/dist/core/tool-name-aliases.js +13 -19
  302. package/dist/core/tool-name-readings.d.ts +5 -35
  303. package/dist/core/tool-name-readings.js +1 -9
  304. package/dist/core/tool-policy.d.ts +68 -198
  305. package/dist/core/tool-policy.js +149 -198
  306. package/dist/core/tool-result-store.js +3 -0
  307. package/dist/core/tool-roster.d.ts +14 -19
  308. package/dist/core/tool-roster.js +11 -44
  309. package/dist/core/tool-spec-members.js +7 -2
  310. package/dist/core/tool-spec.d.ts +19 -11
  311. package/dist/core/tools.js +49 -9
  312. package/dist/core/trace.d.ts +2 -2
  313. package/dist/core/types.d.ts +1 -1
  314. package/dist/core/user-attachment.d.ts +8 -1
  315. package/dist/core/user-attachment.js +64 -4
  316. package/dist/core/wiring-manifest.d.ts +9 -0
  317. package/dist/core/wiring-manifest.js +1 -0
  318. package/dist/core/workflow-journal-store.js +1 -1
  319. package/dist/engine/compaction/compaction.js +3 -2
  320. package/dist/engine/execution-env/node-execution-env.d.ts +7 -3
  321. package/dist/engine/execution-env/node-execution-env.js +11 -2
  322. package/dist/engine/harness/agent-harness.d.ts +25 -1
  323. package/dist/engine/harness/agent-harness.js +54 -7
  324. package/dist/engine/harness/types.d.ts +29 -2
  325. package/dist/engine/llm/types.d.ts +28 -11
  326. package/dist/engine/loop/agent-loop.d.ts +22 -0
  327. package/dist/engine/loop/agent-loop.js +16 -26
  328. package/dist/engine/loop/types.d.ts +16 -6
  329. package/dist/engine/session/memory-repo.d.ts +9 -2
  330. package/dist/engine/session/memory-repo.js +11 -1
  331. package/dist/engine/session/repo-utils.d.ts +6 -5
  332. package/dist/engine/session/repo-utils.js +14 -6
  333. package/dist/index.d.ts +22 -10
  334. package/dist/index.js +15 -7
  335. package/dist/internal/harness-types.d.ts +1 -1
  336. package/dist/internal/llm.d.ts +1 -1
  337. package/dist/orchestration/run-workflow-tool.d.ts +6 -4
  338. package/dist/orchestration/run-workflow-tool.js +5 -3
  339. package/dist/orchestration/workflow.d.ts +3 -0
  340. package/dist/orchestration/workflow.js +36 -18
  341. package/dist/prompt-assembly/artifact.d.ts +2 -2
  342. package/dist/prompt-assembly/artifact.js +5 -3
  343. package/dist/prompt-assembly/assemble.d.ts +3 -2
  344. package/dist/prompt-assembly/assemble.js +1 -1
  345. package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
  346. package/dist/prompt-assembly/catalog/schema.js +34 -2
  347. package/dist/prompt-assembly/catalog/sections.js +0 -1
  348. package/dist/prompt-assembly/composer.d.ts +3 -1
  349. package/dist/prompt-assembly/composer.js +1 -1
  350. package/dist/prompt-assembly/event-registry.js +1 -0
  351. package/dist/prompt-assembly/overlay.js +3 -3
  352. package/dist/prompt-assembly/renderers.d.ts +1 -1
  353. package/dist/prompt-assembly/renderers.js +0 -3
  354. package/dist/prompt-assembly/role-base.d.ts +15 -0
  355. package/dist/prompt-assembly/role-base.js +9 -0
  356. package/dist/prompt-assembly/select.d.ts +39 -14
  357. package/dist/prompt-assembly/select.js +47 -16
  358. package/dist/prompt-assembly/sugar.d.ts +17 -11
  359. package/dist/prompt-assembly/sugar.js +8 -3
  360. package/dist/prompt-assembly/tool-catalog.d.ts +37 -3
  361. package/dist/prompt-assembly/tool-catalog.js +114 -4
  362. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
  363. package/dist/prompt-assembly/turn-snapshot.js +0 -3
  364. package/dist/prompt-assembly/types.d.ts +0 -7
  365. package/dist/scenarios/full-body.d.ts +11 -11
  366. package/dist/scenarios/full-body.js +4 -3
  367. package/dist/shell/lex.js +10 -1
  368. package/dist/stores/file/adoption/adopt.js +4 -3
  369. package/dist/stores/file/adoption/marker.js +3 -2
  370. package/dist/stores/file/checkpoint-store.d.ts +21 -0
  371. package/dist/stores/file/checkpoint-store.js +38 -6
  372. package/dist/stores/file/file-history-store.js +6 -7
  373. package/dist/stores/file/index.js +2 -1
  374. package/dist/stores/file/permission-rule-adopt.js +4 -3
  375. package/dist/stores/file/permission-rule-store.js +11 -5
  376. package/dist/stores/file/session-store.d.ts +29 -6
  377. package/dist/stores/file/session-store.js +42 -50
  378. package/dist/tools/artifact/artifact-tool.js +3 -2
  379. package/dist/tools/fs/bash-program-position.d.ts +19 -2
  380. package/dist/tools/fs/bash-program-position.js +56 -9
  381. package/dist/tools/fs/fs-bash.js +5 -5
  382. package/dist/tools/fs/fs-glob.d.ts +86 -0
  383. package/dist/tools/fs/fs-glob.js +226 -0
  384. package/dist/tools/fs/fs-read.js +14 -14
  385. package/dist/tools/fs/fs-search-tools.js +7 -7
  386. package/dist/tools/fs/pattern-kill-guard.d.ts +22 -0
  387. package/dist/tools/fs/pattern-kill-guard.js +397 -0
  388. package/dist/tools/fs/pdf.d.ts +9 -6
  389. package/dist/tools/fs/pdf.js +3 -3
  390. package/dist/tools/fs/program-read-model.d.ts +57 -7
  391. package/dist/tools/fs/program-read-model.js +11 -1
  392. package/dist/tools/fs/program-read-model.rows.js +78 -21
  393. package/dist/tools/fs/read-deny-home.js +3 -2
  394. package/dist/tools/fs/removal-targets.d.ts +17 -7
  395. package/dist/tools/fs/removal-targets.js +47 -20
  396. package/dist/tools/fs/search.d.ts +10 -0
  397. package/dist/tools/fs/search.js +20 -26
  398. package/dist/tools/fs/shell-read-argv.js +89 -12
  399. package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
  400. package/dist/tools/fs/shell-read-bindings.js +41 -0
  401. package/dist/tools/fs/shell-read-check.js +6 -19
  402. package/dist/tools/fs/shell-read-environment.d.ts +5 -1
  403. package/dist/tools/fs/shell-read-environment.js +3 -2
  404. package/dist/tools/fs/shell-read-establish.d.ts +15 -0
  405. package/dist/tools/fs/shell-read-establish.js +19 -20
  406. package/dist/tools/fs/shell-read-plan.js +24 -16
  407. package/dist/tools/fs/shell-read-start.d.ts +6 -1
  408. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
  409. package/dist/tools/fs/shell-read-write-targets.js +2 -3
  410. package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
  411. package/dist/tools/fs/shell-reentry-text.js +40 -2
  412. package/dist/tools/todo.js +63 -63
  413. package/dist/tools/web.d.ts +19 -14
  414. package/dist/tools/web.js +38 -55
  415. package/package.json +1 -1
  416. package/test/export-surface.snapshot.json +143 -5
  417. package/wire-changes/core@7.56.0.json +3891 -0
  418. package/wire-changes/mods-kernel@7.56.0.json +303 -0
@@ -4,9 +4,62 @@ export { BaseSessionStorage, leafIdAfterEntry, } from "../internal/harness.js";
4
4
  export { SessionError } from "../internal/harness.js";
5
5
  import { SessionError } from "../internal/harness.js";
6
6
  import { readMember, thrownIs } from "./thrown-value.js";
7
+ import { receiptRefusal, receiveOwnProperties } from "./own-property-receipt.js";
8
+ import { principalNameFault, principalNameOf } from "./principal-name.js";
7
9
  export function isSessionConflict(err) {
8
10
  return thrownIs(err, SessionError) && readMember(err, "code") === "conflict";
9
11
  }
12
+ export const SESSION_LIST_LIMITS = Object.freeze({ default: 50, max: 1000 });
13
+ const LIST_QUERY_MEMBERS = ["owner", "includeUnowned", "after", "limit"];
14
+ export function readSessionListQuery(raw, extensions = []) {
15
+ if (raw === undefined)
16
+ return { includeUnowned: false, limit: SESSION_LIST_LIMITS.default, extensions: {} };
17
+ if (typeof raw !== "object" || raw === null)
18
+ throw receiptRefusal(`SessionStore.list: the query must be an object, got ${raw === null ? "null" : typeof raw}`);
19
+ const got = receiveOwnProperties(raw, { subject: "SessionListQuery", unit: "member", moment: "at SessionStore.list" });
20
+ const extended = {};
21
+ for (const name of Object.keys(got)) {
22
+ if (LIST_QUERY_MEMBERS.includes(name))
23
+ continue;
24
+ if (!extensions.includes(name))
25
+ throw receiptRefusal(`SessionStore.list: this store does not filter by "${name}" — refused rather than listing the rows that filter would exclude`);
26
+ extended[name] = got[name];
27
+ }
28
+ const { owner: ownerGiven, includeUnowned, after, limit } = got;
29
+ const owner = ownerGiven === undefined ? undefined : principalNameOf(ownerGiven);
30
+ if (ownerGiven !== undefined && owner === undefined) {
31
+ throw receiptRefusal(`SessionListQuery.owner must be a principal name (a string with a visible character, no control or separator character, and no format character but a joiner) when present, got ${principalNameFault(ownerGiven)}`);
32
+ }
33
+ if (includeUnowned !== undefined && typeof includeUnowned !== "boolean")
34
+ throw receiptRefusal(`SessionListQuery.includeUnowned must be a boolean when present, got ${typeof includeUnowned}`);
35
+ if (includeUnowned !== undefined && owner === undefined)
36
+ throw receiptRefusal("SessionListQuery.includeUnowned is a member of the owner filter and was given without owner — refused rather than read as every owner's list");
37
+ if (limit !== undefined && !(typeof limit === "number" && Number.isSafeInteger(limit) && limit >= 1 && limit <= SESSION_LIST_LIMITS.max)) {
38
+ throw receiptRefusal(`SessionListQuery.limit must be an integer in 1..${SESSION_LIST_LIMITS.max} when present`);
39
+ }
40
+ return {
41
+ ...(owner !== undefined ? { owner } : {}),
42
+ includeUnowned: includeUnowned === true,
43
+ ...(after !== undefined ? { after: readListCursor(after) } : {}),
44
+ limit: limit ?? SESSION_LIST_LIMITS.default,
45
+ extensions: extended,
46
+ };
47
+ }
48
+ function readListCursor(raw) {
49
+ if (typeof raw !== "object" || raw === null)
50
+ throw receiptRefusal(`SessionListQuery.after must be an object when present, got ${raw === null ? "null" : typeof raw}`);
51
+ const got = receiveOwnProperties(raw, { subject: "SessionListQuery.after", unit: "member", moment: "at SessionStore.list" });
52
+ for (const name of Object.keys(got)) {
53
+ if (name !== "lastActiveAt" && name !== "sessionId")
54
+ throw receiptRefusal(`SessionListQuery.after carries "${name}" — a cursor is a lastActiveAt and a sessionId`);
55
+ }
56
+ const { lastActiveAt, sessionId } = got;
57
+ if (typeof lastActiveAt !== "number" || !Number.isFinite(lastActiveAt))
58
+ throw receiptRefusal("SessionListQuery.after.lastActiveAt must be a finite number");
59
+ if (typeof sessionId !== "string")
60
+ throw receiptRefusal(`SessionListQuery.after.sessionId must be a string, got ${typeof sessionId}`);
61
+ return { lastActiveAt, sessionId };
62
+ }
10
63
  export function hasSessionFork(store) {
11
64
  return typeof store.fork === "function";
12
65
  }
@@ -1,7 +1,7 @@
1
1
  import type { ShellReadVerdict } from "../tools/fs/shell-read-check.js";
2
2
  import { type ShellReadDisposition } from "../tools/fs/shell-read-judge.js";
3
3
  import type { GatePass } from "./runner/gate-exit.js";
4
- import type { AskGuard } from "./tool-policy.js";
4
+ import type { PermissionResult } from "./tool-policy.js";
5
5
  /** One station answer, received and read: what a reader acts on (`kind`) and the answer it acts on (`verdict` — the engine's own
6
6
  * copy, received once). `unbelieved`: the station's answer was not received or contradicted its own finding, and `verdict` is the
7
7
  * engine's refusal of it (its sentence names the station's words only where they are words of the closed sets — nothing a station
@@ -35,17 +35,16 @@ export interface StationAnswerCall {
35
35
  * ({@link silencedBy}).
36
36
  */
37
37
  export declare function readStationAnswer(answer: ShellReadVerdict, call: StationAnswerCall): StationAnswerReading;
38
- /** Whose silence a station's answer carries, each its own class on the question it keeps or mints: unread rows are a rule that could
39
- * not be read (the rule lane's class); the finding `unanswered` is, unless rows are unread, the environment's silence — shell text the
40
- * engine cannot read (a guard) — and the marks only pick whose. Either is a question only a person answers; `probe` follows the finding. */
41
- export declare function silencedBy(verdict: ShellReadVerdict, d: {
42
- readonly guards?: readonly AskGuard[];
43
- readonly ruleUnreadable?: unknown;
44
- }): {
45
- ruleUnreadable?: "persisted_rows";
46
- guards?: readonly AskGuard[];
47
- requiresRealApproval?: true;
48
- };
38
+ /** The ask `d` with the silence a station's answer carries, each its own class on the question it keeps or mints: unread rows are a
39
+ * rule that could not be read (the rule lane's class); the finding `unanswered` is, unless rows are unread, the environment's silence —
40
+ * shell text the engine cannot read (a guard, attested on the ask's record for `call` beside the guards it already carries) — and the
41
+ * marks only pick whose. Either is a question only a person answers; `probe` follows the finding. No silence ⇒ `d` itself. */
42
+ export declare function silencedBy<D extends Extract<PermissionResult, {
43
+ action: "ask";
44
+ }>>(d: D, verdict: ShellReadVerdict, call: {
45
+ readonly toolCallId: string;
46
+ readonly toolName: string;
47
+ }): D;
49
48
  /** The station's SILENCE ({@link readStationAnswer}), applied once, before any disposition arm: a station — a host-mounted one
50
49
  * included — may spell `vouch`, `mandated` or `ordinary` beside it, and no word vouches for what nobody judged. A deny stands; an
51
50
  * ask keeps its words and gains the silence's class; an allow becomes the silence's question (`ask.message`), one only a person
@@ -3,6 +3,7 @@ import { dispositionsOfRow, SHELL_READ_FINDINGS } from "../tools/fs/shell-read-j
3
3
  import { receiveContract } from "./decision-receipt.js";
4
4
  import { egressThrownText } from "./untrusted-egress.js";
5
5
  import { receive } from "./mods/kernel/receive.js";
6
+ import { attest, attestationOf } from "./engine-attestation.js";
6
7
  const DISPOSITIONS = ["refuse", "mandated", "ordinary", "vouch"];
7
8
  const MEMBER_KINDS = {
8
9
  disposition: "word",
@@ -71,21 +72,24 @@ export function readStationAnswer(answer, call) {
71
72
  call.report?.(new Error(`shell read station answered out of its contract for tool "${call.toolName}"${call.toolCallId !== undefined ? ` (toolCallId ${call.toolCallId})` : ""} — ${sentence}`));
72
73
  return { kind: "refuse", verdict: { disposition: "refuse", finding: "none", sentence }, unbelieved: true };
73
74
  }
74
- export function silencedBy(verdict, d) {
75
+ export function silencedBy(d, verdict, call) {
75
76
  const environment = verdict.environmentSilent === true || (verdict.finding === "unanswered" && verdict.rowsUnread !== true);
77
+ if (!environment && verdict.rowsUnread !== true)
78
+ return d;
76
79
  const rows = verdict.rowsUnread === true && d.ruleUnreadable === undefined ? { ruleUnreadable: "persisted_rows" } : {};
77
- const guards = environment && d.guards?.includes("unverifiable_shell_text") !== true ? { guards: [...(d.guards ?? []), "unverifiable_shell_text"] } : {};
78
- return environment || verdict.rowsUnread === true ? { ...rows, ...guards, requiresRealApproval: true } : {};
80
+ const out = { ...d, ...rows, requiresRealApproval: true };
81
+ const held = attestationOf(d, call)?.guards ?? [];
82
+ return environment && !held.includes("unverifiable_shell_text") ? attest(out, call, { guards: [...held, "unverifiable_shell_text"] }) : out;
79
83
  }
80
84
  export function applyUnansweredSilence(pass, verdict, ask) {
81
85
  if (verdict.details !== undefined)
82
86
  pass.shellReadDetails = verdict.details;
83
87
  if (pass.decision.action === "ask") {
84
- pass.decision = { ...pass.decision, ...silencedBy(verdict, pass.decision) };
88
+ pass.decision = silencedBy(pass.decision, verdict, pass.req);
85
89
  return;
86
90
  }
87
91
  if (pass.decision.action !== "allow")
88
92
  return;
89
- pass.decision = { action: "ask", message: ask.message, decisionReason: "safety", ...silencedBy(verdict, {}), ...(ask.account !== undefined ? { probeReason: ask.account } : {}) };
93
+ pass.decision = silencedBy({ action: "ask", message: ask.message, decisionReason: "safety", ...(ask.account !== undefined ? { probeReason: ask.account } : {}) }, verdict, pass.req);
90
94
  pass.tightenedBy = ask.tightenedBy;
91
95
  }
@@ -276,7 +276,7 @@ function unwrap(words) {
276
276
  if (row.viaShell === true)
277
277
  return `\`${name}\` evaluates its operands as shell input this reader does not read`;
278
278
  if (row.executes !== undefined) {
279
- const hit = words.slice(1).find((w) => w.expands !== false || row.executes.includes(w.text));
279
+ const hit = words.slice(1).find((w) => w.expands !== false || row.executes.some((x) => w.text === x || (x.length === 2 && /^-[^-]/.test(w.text) && w.text.includes(x[1]))));
280
280
  return hit === undefined ? words : hit.expands !== false ? `an expansion where \`${name}\` reads its arguments` : `\`${name} ${hit.text}\` runs a command this reader does not read`;
281
281
  }
282
282
  const valueFlags = new Set(row.valueFlags);
@@ -394,6 +394,8 @@ function unwrap(words) {
394
394
  if (r !== true)
395
395
  return r;
396
396
  i++;
397
+ if (durationTaken && positionalTaken)
398
+ break;
397
399
  }
398
400
  if (i >= words.length)
399
401
  return words;
@@ -30,7 +30,7 @@ const runsCommand = (spec) => spec.effect === "executes" || spec.value?.kind ===
30
30
  function projectRow(row) {
31
31
  const sorted = (xs) => [...new Set(xs)].sort();
32
32
  const all = spellings(row);
33
- const viaShell = row.reentry?.kind === "shell" || row.launcher?.shellText !== undefined || row.body === "first-operand" || row.body === "assignment-values" || (row.operands?.rest?.kind === "program-text" && row.operands.rest.language === "arithmetic");
33
+ const viaShell = (row.reentry?.kind === "shell" && row.reentry.text !== "history") || row.launcher?.shellText !== undefined || row.body === "first-operand" || row.body === "assignment-values" || (row.operands?.rest?.kind === "program-text" && row.operands.rest.language === "arithmetic");
34
34
  if (row.launcher === undefined) {
35
35
  if (viaShell)
36
36
  return { valueFlags: [], flags: [], commandString: [], viaShell: true };
@@ -181,6 +181,12 @@ export declare const SHELL_WRAPPER_TABLE: {
181
181
  readonly commandString: readonly [];
182
182
  readonly viaShell: true;
183
183
  };
184
+ readonly fc: {
185
+ readonly valueFlags: readonly [];
186
+ readonly flags: readonly [];
187
+ readonly commandString: readonly [];
188
+ readonly executes: readonly ["-e", "-s"];
189
+ };
184
190
  readonly source: {
185
191
  readonly valueFlags: readonly [];
186
192
  readonly flags: readonly [];
@@ -289,14 +295,14 @@ export declare const SHELL_WRAPPER_TABLE: {
289
295
  readonly viaShell: true;
290
296
  };
291
297
  readonly chroot: {
292
- readonly valueFlags: readonly [];
293
- readonly flags: readonly [];
298
+ readonly valueFlags: readonly ["--groups", "--userspec", "-G", "-g", "-u"];
299
+ readonly flags: readonly ["--help", "--skip-chdir", "--version"];
294
300
  readonly commandString: readonly [];
295
301
  readonly positional: RegExp;
296
302
  };
297
303
  readonly setpriv: {
298
- readonly valueFlags: readonly [];
299
- readonly flags: readonly [];
304
+ readonly valueFlags: readonly ["--ambient-caps", "--apparmor-profile", "--bounding-set", "--egid", "--euid", "--groups", "--inh-caps", "--pdeathsig", "--regid", "--reuid", "--rgid", "--ruid", "--securebits", "--selinux-label"];
305
+ readonly flags: readonly ["--clear-groups", "--dump", "--help", "--init-groups", "--keep-groups", "--nnp", "--no-new-privs", "--reset-env", "--version", "-V", "-d", "-h"];
300
306
  readonly commandString: readonly [];
301
307
  };
302
308
  readonly numactl: {
@@ -305,8 +311,9 @@ export declare const SHELL_WRAPPER_TABLE: {
305
311
  readonly commandString: readonly [];
306
312
  };
307
313
  readonly prlimit: {
308
- readonly valueFlags: readonly [];
309
- readonly flags: readonly [];
314
+ readonly valueFlags: readonly ["--output", "--pid", "-o", "-p"];
315
+ readonly flags: readonly ["--as", "--core", "--cpu", "--data", "--fsize", "--help", "--locks", "--memlock", "--msgqueue", "--nice", "--nofile", "--noheadings", "--nproc", "--raw", "--rss", "--rtprio", "--rttime", "--sigpending", "--stack", "--verbose", "--version", "-V", "-h"];
316
+ readonly attached: readonly ["-c", "-d", "-e", "-f", "-i", "-l", "-m", "-n", "-q", "-r", "-s", "-t", "-u", "-v", "-x", "-y"];
310
317
  readonly commandString: readonly [];
311
318
  };
312
319
  readonly "systemd-run": {
@@ -316,7 +323,7 @@ export declare const SHELL_WRAPPER_TABLE: {
316
323
  };
317
324
  readonly valgrind: {
318
325
  readonly valueFlags: readonly [];
319
- readonly flags: readonly [];
326
+ readonly flags: readonly ["--alignment", "--allow-mismatched-debuginfo", "--avg-transtab-entry-size", "--cachegrind-out-file", "--callgrind-out-file", "--child-silent-after-fork", "--default-suppressions", "--demangle", "--dhat-out-file", "--dsymutil", "--error-exitcode", "--error-limit", "--error-markers", "--errors-for-leak-kinds", "--exit-on-first-error", "--expensive-definedness-checks", "--fair-sched", "--free-fill", "--freelist-big-blocks", "--freelist-vol", "--fullpath-after", "--gen-suppressions", "--help", "--help-debug", "--help-dyn-options", "--ignore-range-below-sp", "--ignore-ranges", "--input-fd", "--keep-debuginfo", "--keep-stacktraces", "--kernel-variant", "--leak-check", "--leak-check-heuristics", "--leak-resolution", "--log-fd", "--log-file", "--main-stacksize", "--malloc-fill", "--massif-out-file", "--max-stackframe", "--max-threads", "--merge-recursive-frames", "--num-callers", "--num-transtab-sectors", "--partial-loads-ok", "--quiet", "--read-inline-info", "--read-var-info", "--redzone-size", "--resync-filter", "--run-cxx-freeres", "--run-libc-freeres", "--show-below-main", "--show-emwarns", "--show-error-list", "--show-leak-kinds", "--show-mismatched-frees", "--show-possibly-lost", "--show-reachable", "--sigill-diagnostics", "--sim-hints", "--smc-check", "--soname-synonyms", "--suppressions", "--time-stamp", "--tool", "--trace-children", "--trace-children-skip", "--trace-children-skip-by-arg", "--track-fds", "--track-origins", "--undef-value-errors", "--unw-stack-scan-frames", "--unw-stack-scan-thresh", "--valgrind-stacksize", "--verbose", "--version", "--vgdb", "--vgdb-error", "--vgdb-poll", "--vgdb-shadow-registers", "--vgdb-stop-at", "--workaround-gcc296-bugs", "--xml", "--xml-fd", "--xml-file", "--xml-user-comment", "--xtree-leak", "--xtree-leak-file", "--xtree-memory", "--xtree-memory-file", "-h", "-q", "-s", "-v"];
320
327
  readonly commandString: readonly [];
321
328
  };
322
329
  readonly firejail: {
@@ -27,6 +27,7 @@ export const SHELL_WRAPPER_TABLE = {
27
27
  unshare: { valueFlags: ["--boottime", "--map-group", "--map-user", "--monotonic", "--propagation", "--root", "--setgid", "--setgroups", "--setuid", "--wd", "-G", "-R", "-S", "-w"], flags: ["--cgroup", "--fork", "--help", "--ipc", "--keep-caps", "--kill-child", "--map-current-user", "--map-root-user", "--mount", "--mount-proc", "--net", "--pid", "--time", "--user", "--uts", "--version", "-C", "-T", "-U", "-c", "-f", "-i", "-m", "-n", "-p", "-r", "-u"], commandString: [] },
28
28
  nsenter: { valueFlags: ["--setgid", "--setuid", "--target", "-G", "-S", "-t"], flags: ["--all", "--cgroup", "--help", "--ipc", "--mount", "--net", "--no-fork", "--pid", "--preserve-credentials", "--root", "--time", "--user", "--uts", "--version", "--wd", "-C", "-F", "-T", "-U", "-Z", "-a", "-i", "-m", "-n", "-p", "-u"], attached: ["-r", "-w"], commandString: [] },
29
29
  eval: { valueFlags: [], flags: [], commandString: [], viaShell: true },
30
+ fc: { valueFlags: [], flags: [], commandString: [], executes: ["-e", "-s"] },
30
31
  source: { valueFlags: [], flags: [], commandString: [], viaShell: true },
31
32
  ".": { valueFlags: [], flags: [], commandString: [], viaShell: true },
32
33
  trap: { valueFlags: [], flags: [], commandString: [], viaShell: true },
@@ -45,12 +46,12 @@ export const SHELL_WRAPPER_TABLE = {
45
46
  busybox: { valueFlags: [], flags: [], commandString: [] },
46
47
  su: { valueFlags: ["--group", "--shell", "--supp-group", "--whitelist-environment", "-G", "-g", "-s", "-w"], flags: ["--fast", "--help", "--login", "--no-pty", "--preserve-environment", "--pty", "--version", "-P", "-T", "-V", "-f", "-h", "-l", "-m", "-p"], commandString: ["--command", "--session-command", "-c"], viaShell: true },
47
48
  runuser: { valueFlags: ["--group", "--shell", "--supp-group", "--user", "--whitelist-environment", "-G", "-g", "-s", "-u", "-w"], flags: ["--fast", "--help", "--login", "--no-pty", "--preserve-environment", "--pty", "--version", "-P", "-T", "-V", "-f", "-h", "-l", "-m", "-p"], commandString: ["--command", "--session-command", "-c"], viaShell: true },
48
- chroot: { valueFlags: [], flags: [], commandString: [], positional: /^/ },
49
- setpriv: { valueFlags: [], flags: [], commandString: [] },
49
+ chroot: { valueFlags: ["--groups", "--userspec", "-G", "-g", "-u"], flags: ["--help", "--skip-chdir", "--version"], commandString: [], positional: /^/ },
50
+ setpriv: { valueFlags: ["--ambient-caps", "--apparmor-profile", "--bounding-set", "--egid", "--euid", "--groups", "--inh-caps", "--pdeathsig", "--regid", "--reuid", "--rgid", "--ruid", "--securebits", "--selinux-label"], flags: ["--clear-groups", "--dump", "--help", "--init-groups", "--keep-groups", "--nnp", "--no-new-privs", "--reset-env", "--version", "-V", "-d", "-h"], commandString: [] },
50
51
  numactl: { valueFlags: [], flags: [], commandString: [] },
51
- prlimit: { valueFlags: [], flags: [], commandString: [] },
52
+ prlimit: { valueFlags: ["--output", "--pid", "-o", "-p"], flags: ["--as", "--core", "--cpu", "--data", "--fsize", "--help", "--locks", "--memlock", "--msgqueue", "--nice", "--nofile", "--noheadings", "--nproc", "--raw", "--rss", "--rtprio", "--rttime", "--sigpending", "--stack", "--verbose", "--version", "-V", "-h"], attached: ["-c", "-d", "-e", "-f", "-i", "-l", "-m", "-n", "-q", "-r", "-s", "-t", "-u", "-v", "-x", "-y"], commandString: [] },
52
53
  "systemd-run": { valueFlags: [], flags: [], commandString: [] },
53
- valgrind: { valueFlags: [], flags: [], commandString: [] },
54
+ valgrind: { valueFlags: [], flags: ["--alignment", "--allow-mismatched-debuginfo", "--avg-transtab-entry-size", "--cachegrind-out-file", "--callgrind-out-file", "--child-silent-after-fork", "--default-suppressions", "--demangle", "--dhat-out-file", "--dsymutil", "--error-exitcode", "--error-limit", "--error-markers", "--errors-for-leak-kinds", "--exit-on-first-error", "--expensive-definedness-checks", "--fair-sched", "--free-fill", "--freelist-big-blocks", "--freelist-vol", "--fullpath-after", "--gen-suppressions", "--help", "--help-debug", "--help-dyn-options", "--ignore-range-below-sp", "--ignore-ranges", "--input-fd", "--keep-debuginfo", "--keep-stacktraces", "--kernel-variant", "--leak-check", "--leak-check-heuristics", "--leak-resolution", "--log-fd", "--log-file", "--main-stacksize", "--malloc-fill", "--massif-out-file", "--max-stackframe", "--max-threads", "--merge-recursive-frames", "--num-callers", "--num-transtab-sectors", "--partial-loads-ok", "--quiet", "--read-inline-info", "--read-var-info", "--redzone-size", "--resync-filter", "--run-cxx-freeres", "--run-libc-freeres", "--show-below-main", "--show-emwarns", "--show-error-list", "--show-leak-kinds", "--show-mismatched-frees", "--show-possibly-lost", "--show-reachable", "--sigill-diagnostics", "--sim-hints", "--smc-check", "--soname-synonyms", "--suppressions", "--time-stamp", "--tool", "--trace-children", "--trace-children-skip", "--trace-children-skip-by-arg", "--track-fds", "--track-origins", "--undef-value-errors", "--unw-stack-scan-frames", "--unw-stack-scan-thresh", "--valgrind-stacksize", "--verbose", "--version", "--vgdb", "--vgdb-error", "--vgdb-poll", "--vgdb-shadow-registers", "--vgdb-stop-at", "--workaround-gcc296-bugs", "--xml", "--xml-fd", "--xml-file", "--xml-user-comment", "--xtree-leak", "--xtree-leak-file", "--xtree-memory", "--xtree-memory-file", "-h", "-q", "-s", "-v"], commandString: [] },
54
55
  firejail: { valueFlags: [], flags: [], commandString: [] },
55
56
  bwrap: { valueFlags: [], flags: [], commandString: [] },
56
57
  ash: { valueFlags: [], flags: [], commandString: [], viaShell: true },
@@ -1,5 +1,8 @@
1
1
  import { ensureToolTurnInvariants } from "../internal/llm.js";
2
2
  import { resolveTaskModel } from "./roles.js";
3
+ import { runRenderer } from "../prompt-assembly/renderers.js";
4
+ import { roleBaseOf } from "../prompt-assembly/role-base.js";
5
+ import { effectiveTraitsOf, readModelPromptTraits, selectorsOf } from "../prompt-assembly/select.js";
3
6
  import { readDegradation } from "../brain/degrading.js";
4
7
  import { extractErrorCode } from "../brain/errors.js";
5
8
  import { cacheFamilyOf, uncachedInputTokensOf } from "./runner/usage-accounting.js";
@@ -9,6 +12,11 @@ function normalizeSideQueryUsage(model, u) {
9
12
  const input = uncachedInputTokensOf(cacheFamilyOf(model), u);
10
13
  return input === u.input ? u : { ...u, input };
11
14
  }
15
+ const NO_RUN_FACTS = Object.freeze({ policyEnabled: false, hooksEnabled: false, isolationEnabled: false, withinTaskCompactionEnabled: false, supervisorEnabled: false, orchestrationEnabled: false, awarenessEnabled: false, goalEnabled: false, worktreeIsolated: false, teammateEnabled: false });
16
+ function sideQueryRoleBase(role, model, tools) {
17
+ const selectors = selectorsOf(NO_RUN_FACTS, { role }, undefined, effectiveTraitsOf(readModelPromptTraits(model), undefined));
18
+ return runRenderer("role-base", { facts: NO_RUN_FACTS, toolNames: (tools ?? []).map((t) => t.name) }, selectors)?.text;
19
+ }
12
20
  export async function runSideQuery(spec, deps) {
13
21
  if (!Array.isArray(spec.messages) || spec.messages.length === 0) {
14
22
  throw new Error("sideQuery requires a non-empty messages array");
@@ -40,7 +48,8 @@ export async function runSideQuery(spec, deps) {
40
48
  }
41
49
  return { role: "user", content: m.content, timestamp };
42
50
  };
43
- const effectiveSystemPrompt = spec.systemPrompt ?? resolved.systemPrompt;
51
+ const decided = roleBaseOf([spec.systemPrompt !== undefined ? { systemPrompt: spec.systemPrompt } : undefined, resolved]);
52
+ const effectiveSystemPrompt = decided?.systemPrompt ?? (decided?.promptRole !== undefined ? sideQueryRoleBase(decided.promptRole, resolved.model, spec.tools) : undefined);
44
53
  const context = {
45
54
  ...(effectiveSystemPrompt !== undefined ? { systemPrompt: effectiveSystemPrompt } : {}),
46
55
  messages: ensureToolTurnInvariants(spec.messages.map(normalize)).messages,
@@ -199,6 +199,10 @@ export declare class SkillGrantTable {
199
199
  private readonly rowKeys;
200
200
  private mounted;
201
201
  private activations;
202
+ private scopes;
203
+ /** State the leg's skill scopes (the mods bus's record): every activation this table writes opens its skill's scope there. A
204
+ * table never told opens nothing (the resume's pre-claim probe). */
205
+ bindScopes(scopes: Pick<import("./mods/spec-seats.js").SkillScopes, "open">): void;
202
206
  /** State where the run's mounted names are read (prepare, once the roster is assembled): a skill's entries are read against
203
207
  * them when seeded and whenever the skill is activated. A table never told reads the grammar alone. */
204
208
  bindMountedNames(mounted: MountedToolNames): void;
@@ -211,8 +215,9 @@ export declare class SkillGrantTable {
211
215
  * capture re-supplied beside the frozen rows that already carry it. Keyed on the ROW, not the skill: a definition that
212
216
  * changed across a park writes its new rows when it is activated again. */
213
217
  private hold;
214
- /** Write a skill's rows (rows already held are not held twice) — or none, refused with {@link SKILL_DENY_UNREADABLE_CODE},
215
- * when its `disallowed-tools` holds an entry that reads as nothing. Returns the allow entries that read as nothing. */
218
+ /** Write a skill's rows (rows already held are not held twice) and open its scope — or neither, refused with
219
+ * {@link SKILL_DENY_UNREADABLE_CODE}, when its `disallowed-tools` holds an entry that reads as nothing. Returns the allow
220
+ * entries that read as nothing. */
216
221
  activate(skill: SkillGrantSource, origin: SkillGrantOrigin): readonly SkillInertEntry[];
217
222
  /** Seed portable records (a delegated child's capture, a resumed leg's frozen rows): a record the model wrote keeps that
218
223
  * word, every other record seeds under `origin` — `user` is written only from this leg's own spec, never seeded. Each
@@ -180,6 +180,10 @@ export class SkillGrantTable {
180
180
  rowKeys = new Set();
181
181
  mounted;
182
182
  activations;
183
+ scopes;
184
+ bindScopes(scopes) {
185
+ this.scopes = scopes;
186
+ }
183
187
  bindMountedNames(mounted) {
184
188
  this.mounted = mounted;
185
189
  }
@@ -203,6 +207,7 @@ export class SkillGrantTable {
203
207
  throw skillDenyUnreadable(skill.name, unreadDeny, "activation");
204
208
  for (const row of rows)
205
209
  this.hold(row);
210
+ this.scopes?.open(skill.name);
206
211
  return inert;
207
212
  }
208
213
  seed(records, origin) {
@@ -23,13 +23,15 @@ const SEQUENCE_NOT_AN_ENTRY = "its `- name` entries are not indented alike";
23
23
  const SEQUENCE_NOT_A_NAME = "an entry is not a plain or quoted name (nested, mapping, anchor, tag, flow and block-scalar entries, and number, boolean and null entries, are not read)";
24
24
  const VALUE_IS_A_MAPPING = "its value is a mapping or holds one (`key: value` or `{…}`), not a list of names";
25
25
  const VALUE_NOT_A_STRING = "its value is a number or a boolean, not a list of names";
26
+ const YAML_ALIAS = 'it holds a YAML alias (an unquoted `*<name>`), which the reader does not resolve — quote a value or entry that is meant as a tool-name pattern ("*Edit")';
27
+ const YAML_ALIAS_START = /^\*[^\s,[\]{}]/;
26
28
  const QUOTE_NOT_CLOSED = "a quote in it does not close";
27
29
  const flowNotClosed = (opener) => `its \`${opener}\` does not close within its lines`;
28
30
  const strayLine = (line) => `a flush-left line that is no key follows it (line ${line})`;
29
31
  const NOT_A_KEY_LINE = "a flush-left frontmatter line is a key — letters, digits, `_` and `-`, or one quoted scalar — then `:` and a blank or nothing; a comment starts with `#`";
30
32
  const PARENS_NOT_ONE_ZONE = "an entry's parentheses are not one group that closes it (a `(` that does not close, a `)` with no `(`, or text after the closing `)` — `\\(` / `\\)` are a rule's own text)";
31
33
  const KEY_REPEATED = "the key is written more than once";
32
- const notARule = (position) => `entry ${position} is no rule spelling (a tool name of ASCII letters, digits and \`_ - . : *\`, starting with a letter, a digit or \`_\`, then at most one parenthesized rule)`;
34
+ const notARule = (position) => `entry ${position} is no rule spelling (a tool name of ASCII letters, digits and \`_ - . : *\`, starting with a letter, a digit, \`_\` or \`*\`, then at most one parenthesized rule)`;
33
35
  const keyInsideValue = (inner) => `an indented line under its value is a mapping key, \`${inlineUntrusted(inner, 64)}\` — YAML reads no key there (under a value that is not a mapping, or indented less than the nested block it follows)`;
34
36
  const toolListOutOfPlace = (inner) => `it holds the tool-list key \`${inner}\` — a tool list is read only as a key of the frontmatter, written flush left; anywhere else its list would be lost`;
35
37
  const isBlank = (ch) => ch === " " || ch === "\t";
@@ -434,7 +436,9 @@ function scanValue(text, seat) {
434
436
  const written = trimBlanks(plain);
435
437
  plain = "";
436
438
  breaks = 0;
437
- if (opened.length > 0 || seat === "item") {
439
+ if (YAML_ALIAS_START.test(written))
440
+ cut.fail(YAML_ALIAS);
441
+ else if (opened.length > 0 || seat === "item") {
438
442
  if (YAML_NULL.test(written) || YAML_BOOL_OR_NUMBER.test(written))
439
443
  cut.fail(SEQUENCE_NOT_A_NAME);
440
444
  }
@@ -39,7 +39,17 @@ export declare const POLICY_ALLOW: Extract<ResumeOutcome, {
39
39
  * The full cross-backend contract for {@link CheckpointStore} — the SAME assertions for every
40
40
  * backend. Entries are a 1:1 port of the vitest originals (assertion semantics unchanged).
41
41
  */
42
- export declare function checkpointStoreContract(make: () => CheckpointStore, runAssertion?: ContractAssertionRunner): Promise<void>;
42
+ export declare function checkpointStoreContract(make: () => CheckpointStore, runAssertion?: ContractAssertionRunner,
43
+ /** `sessionHeads` = the map the binding's stores read their session heads from (their `deleteSessionCheckpoints` guard): the kit writes
44
+ * the heads its delete case needs into it. Required only to judge a store that offers `deleteSessionCheckpoints`.
45
+ * `holders` = the set of current claim holders the binding's store judges by (its `holderIsCurrent`, or the claim table it
46
+ * reads): the kit adds and removes holders in it. Required only to judge a store that declares `fence.holder`. */
47
+ options?: {
48
+ readonly sessionHeads?: Map<string, {
49
+ readonly owner: string | null;
50
+ }>;
51
+ readonly holders?: Set<string>;
52
+ }): Promise<void>;
43
53
  /** Seed the canonical listByScope fixture into `store` and return the sorted summaries. Compare the
44
54
  * result against {@link EXPECTED_LISTBYSCOPE_SUMMARIES} (deep equality) to prove the backend's
45
55
  * summary projection matches the reference — the caller owns the comparison (harness-specific). */
@@ -48,7 +48,7 @@ export const POLICY_ALLOWED = Object.freeze({
48
48
  origin: "policy",
49
49
  });
50
50
  export const POLICY_ALLOW = { ...ALLOW, hostDecision: { decidedBy: "policy", approver: "unattended_class_answer" }, gateOutcome: POLICY_ALLOWED };
51
- export async function checkpointStoreContract(make, runAssertion) {
51
+ export async function checkpointStoreContract(make, runAssertion, options = {}) {
52
52
  const { run, settle } = beginContract(runAssertion);
53
53
  run("kit prerequisites: reopen/setPendingSteer/listByScope are implemented (REQUIRED by this kit)", async () => {
54
54
  const probe = make();
@@ -308,7 +308,7 @@ export async function checkpointStoreContract(make, runAssertion) {
308
308
  const stored = (await store.get(bitless.token)).pendingAction;
309
309
  assert.equal("hasBidiControls" in stored, false, "projection backfill must not write back to the row");
310
310
  });
311
- run("the approval object's members: a row's identity + face group and its targets (capabilityId/contractId/shapeDigest/implementationRevision + family/pathTarget/hostTarget/names + targets) survive the round-trip verbatim; an absent group stays absent", async () => {
311
+ run("the approval object's members: a row's identity + face group and its targets (capabilityId/contractId/shapeDigest/implementationRevision + family/pathTarget/hostTarget/names + targets + engineMounted) survive the round-trip verbatim; an absent group stays absent", async () => {
312
312
  const store = make();
313
313
  const carried = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-carried" });
314
314
  const group = {
@@ -320,6 +320,7 @@ export async function checkpointStoreContract(make, runAssertion) {
320
320
  pathTarget: { param: "destination", aliases: ["path"], access: "create", skillScopeEligible: true },
321
321
  names: ["AcmeWrite", "acme_write"],
322
322
  targets: { protective: "/srv/work/out.txt", declared: "/srv/work/out.txt" },
323
+ engineMounted: true,
323
324
  };
324
325
  Object.assign(carried.pendingAction, group);
325
326
  const bare = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-bare" });
@@ -463,6 +464,143 @@ export async function checkpointStoreContract(make, runAssertion) {
463
464
  assert.deepEqual(checkpointIndexFacts(back), checkpointIndexFacts(cp), `the index facts of the row ${cp.gate.kind} came back different`);
464
465
  }
465
466
  });
467
+ run("listOverdue (when offered): pending rows of every scope past the cutoff, one facet per call (facetOf, the question card included), deadline-then-token order, the bound cut with a cursor that resumes without a gap or a repeat, a bad query refused", async () => {
468
+ const store = make();
469
+ if (store.listOverdue === undefined)
470
+ return;
471
+ const human = (over) => createCheckpointFixture(over);
472
+ const question = createCheckpointFixture({ token: "ov-q", deadline: 1_000, gate: { kind: "human", reason: "ask", toolName: "Write", approvalCard: "question" } });
473
+ const rows = [
474
+ human({ token: "ov-a2", deadline: 1_500 }),
475
+ human({ token: "ov-a1", deadline: 1_000, scope: "tenant-b" }),
476
+ human({ token: "ov-a0", deadline: 1_000 }),
477
+ question,
478
+ createCheckpointFixture({ token: "ov-r", deadline: 900, gate: { kind: "resource_limit", reason: "turns" }, pendingAction: { kind: "resource_limit", reason: "turns" } }),
479
+ createCheckpointFixture({ token: "ov-t", deadline: 900, gate: { kind: "task_done" }, pendingAction: { kind: "task_done" } }),
480
+ human({ token: "ov-late", deadline: 5_000 }),
481
+ human({ token: "ov-none" }),
482
+ human({ token: "ov-done", deadline: 1_000 }),
483
+ ];
484
+ for (const cp of rows)
485
+ await store.put(cp.token, cp);
486
+ await store.resolve("ov-done", "tenant-a", ALLOW);
487
+ const tokensOf = (page) => page.rows.map((r) => r.token);
488
+ assert.deepEqual(tokensOf(await store.listOverdue({ cutoff: 2_000, facet: "A" })), ["ov-a0", "ov-a1", "ov-a2"], "facet A = the approval rows past the cutoff, every scope, (deadline, token) ascending");
489
+ assert.deepEqual(tokensOf(await store.listOverdue({ cutoff: 2_000, facet: "B" })), ["ov-r", "ov-q"], "facet B = the abandonment rows, the question parked under an approval gate kind among them");
490
+ assert.deepEqual(tokensOf(await store.listOverdue({ cutoff: 2_000, facet: "-" })), ["ov-t"], "facet - = the non-gate park");
491
+ const first = await store.listOverdue({ cutoff: 2_000, facet: "A", limit: 2 });
492
+ assert.deepEqual(tokensOf(first), ["ov-a0", "ov-a1"], "the bound cuts the page");
493
+ assert.ok(first.next !== undefined, "a cut page carries the next cursor");
494
+ const second = await store.listOverdue({ cutoff: 2_000, facet: "A", limit: 2, after: first.next });
495
+ assert.deepEqual(tokensOf(second), ["ov-a2"], "the cursor resumes after the last row of the page before");
496
+ assert.equal(second.next, undefined, "the last page carries no cursor");
497
+ assert.equal((await store.get(question.token)).status, "pending", "read-only: the listing mutates nothing");
498
+ for (const bad of [{ cutoff: 2_000, facet: "A", limit: 0 }, { cutoff: 2_000, facet: "A", limit: 501 }, { cutoff: 2_000, facet: "A", limit: 1.5 }, { cutoff: Number.NaN, facet: "A" }, { cutoff: 2_000, facet: "C" }, { cutoff: 2_000, facet: "A", scope: "tenant-a" }]) {
499
+ await assert.rejects(store.listOverdue(bad), (e) => e.code === "config.invalid_argument", `refused loudly: ${JSON.stringify(bad)}`);
500
+ }
501
+ });
502
+ run("listBySession (when offered): one session's rows in any status narrowed by status / gate kinds / tool call / scope / createdSince, oldest or newest first, the bound cut with a resuming cursor, an empty gate-kind list matching nothing, a bad query refused", async () => {
503
+ const store = make();
504
+ if (store.listBySession === undefined)
505
+ return;
506
+ const s = "sess-list";
507
+ const rows = [
508
+ createCheckpointFixture({ token: "ls-1", sessionId: s, createdAt: 1 }),
509
+ createCheckpointFixture({ token: "ls-2", sessionId: s, createdAt: 2, pendingAction: { kind: "tool_approval", origin: "policy", toolCallId: "call-9", toolName: "Write", args: {}, boundInputHash: "h9", boundGrantAbsence: "not_bindable", batchToolCallIds: ["call-9"], completedCallIds: [] } }),
510
+ createCheckpointFixture({ token: "ls-3", sessionId: s, createdAt: 3, gate: { kind: "resource_limit", reason: "turns" }, pendingAction: { kind: "resource_limit", reason: "turns" } }),
511
+ createCheckpointFixture({ token: "ls-4", sessionId: s, createdAt: 4, scope: "tenant-b" }),
512
+ createCheckpointFixture({ token: "ls-x", sessionId: "another-session", createdAt: 1 }),
513
+ ];
514
+ for (const cp of rows)
515
+ await store.put(cp.token, cp);
516
+ await store.resolve("ls-1", "tenant-a", ALLOW);
517
+ await store.expire("ls-3", "tenant-a");
518
+ const tokensOf = (page) => page.rows.map((r) => {
519
+ const read = r;
520
+ assert.equal(read.kind, "row", "a readable row is answered as a row");
521
+ return read.checkpoint.token;
522
+ });
523
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s })), ["ls-1", "ls-2", "ls-3", "ls-4"], "every status, every scope, oldest first; another session's row never");
524
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, order: "newest" })), ["ls-4", "ls-3", "ls-2", "ls-1"]);
525
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, status: "pending", gateKinds: ["human"], order: "newest", limit: 1 })), ["ls-4"], "the session's newest pending row of a gate kind");
526
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, status: "pending", gateKinds: ["human"], scope: "tenant-a" })), ["ls-2"]);
527
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, gateKinds: [] })), [], "an empty gate-kind list matches no row");
528
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, status: "expired", limit: 1 })), ["ls-3"]);
529
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, toolCallId: "call-3" })), ["ls-1", "ls-4"], "the rows that parked this tool call (a park with no tool call never matches)");
530
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, toolCallId: "call-9", status: "pending" })), ["ls-2"]);
531
+ assert.deepEqual(tokensOf(await store.listBySession({ sessionId: s, createdSince: 3 })), ["ls-3", "ls-4"]);
532
+ const first = await store.listBySession({ sessionId: s, limit: 3, order: "newest" });
533
+ assert.deepEqual(tokensOf(first), ["ls-4", "ls-3", "ls-2"]);
534
+ assert.ok(first.next !== undefined, "a cut page carries the next cursor");
535
+ const second = await store.listBySession({ sessionId: s, limit: 3, order: "newest", after: first.next });
536
+ assert.deepEqual(tokensOf(second), ["ls-1"]);
537
+ assert.equal(second.next, undefined);
538
+ const read = (await store.listBySession({ sessionId: s, toolCallId: "call-9" })).rows[0];
539
+ read.checkpoint.status = "expired";
540
+ assert.equal((await store.get("ls-2")).status, "pending", "a listed row is a copy: mutating it changes nothing stored");
541
+ for (const bad of [{ sessionId: "" }, { sessionId: s, limit: 101 }, { sessionId: s, limit: 0 }, { sessionId: s, status: "done" }, { sessionId: s, gateKinds: ["nope"] }, { sessionId: s, order: "latest" }, { sessionId: s, createdSince: Number.POSITIVE_INFINITY }, { sessionId: s, owner: "x" }]) {
542
+ await assert.rejects(store.listBySession(bad), (e) => e.code === "config.invalid_argument", `refused loudly: ${JSON.stringify(bad)}`);
543
+ }
544
+ });
545
+ run("deleteSessionCheckpoints (when offered): every row of the session, in any status, only when the session row exists and its owner equals the guard's (null-safe); otherwise nothing is deleted and the answer is 0", async () => {
546
+ const store = make();
547
+ if (store.deleteSessionCheckpoints === undefined)
548
+ return;
549
+ const heads = options.sessionHeads;
550
+ assert.ok(heads !== undefined, "a store offering deleteSessionCheckpoints is judged with the binding's `sessionHeads` (the map its session head read reads)");
551
+ heads.set("del-owned", { owner: "alice" });
552
+ heads.set("del-anon", { owner: null });
553
+ const owned = [
554
+ createCheckpointFixture({ sessionId: "del-owned" }),
555
+ createCheckpointFixture({ sessionId: "del-owned" }),
556
+ createCheckpointFixture({ sessionId: "del-owned" }),
557
+ createCheckpointFixture({ sessionId: "del-owned", scope: "tenant-b" }),
558
+ ];
559
+ const anon = createCheckpointFixture({ sessionId: "del-anon" });
560
+ const orphan = createCheckpointFixture({ sessionId: "del-no-row" });
561
+ const bystander = createCheckpointFixture({ sessionId: "del-other" });
562
+ for (const cp of [...owned, anon, orphan, bystander])
563
+ await store.put(cp.token, cp);
564
+ await store.resolve(owned[1].token, owned[1].scope, ALLOW);
565
+ await store.expire(owned[2].token, owned[2].scope);
566
+ assert.equal(await store.deleteSessionCheckpoints("del-owned", { owner: "bob" }), 0, "another owner deletes nothing");
567
+ assert.equal(await store.deleteSessionCheckpoints("del-owned", { owner: null }), 0, "the anonymous guard does not match an owned session");
568
+ assert.equal(await store.deleteSessionCheckpoints("del-no-row", { owner: null }), 0, "no session row deletes nothing");
569
+ assert.ok((await store.get(orphan.token)) !== null && (await store.get(owned[0].token)) !== null, "a refused guard leaves every row");
570
+ assert.equal(await store.deleteSessionCheckpoints("del-owned", { owner: "alice" }), 4, "the owner's guard deletes the session's rows — pending, resolved, expired, in every scope");
571
+ for (const cp of owned)
572
+ assert.equal(await store.get(cp.token), null);
573
+ assert.equal(await store.deleteSessionCheckpoints("del-anon", { owner: null }), 1, "the anonymous guard deletes an anonymous session's rows");
574
+ assert.ok((await store.get(bystander.token)) !== null, "another session's row stays");
575
+ assert.equal(await store.deleteSessionCheckpoints("del-owned", { owner: "alice" }), 0, "a second delete finds nothing");
576
+ const deleteWith = store.deleteSessionCheckpoints;
577
+ for (const guard of [{}, { owner: undefined }, { owner: 7 }]) {
578
+ const call = () => Reflect.apply(deleteWith, store, ["del-other", guard]);
579
+ await assert.rejects(call, (e) => e.code === "config.invalid_argument", `refused loudly: ${JSON.stringify(guard)}`);
580
+ }
581
+ assert.ok((await store.get(bystander.token)) !== null, "a refused guard deletes nothing");
582
+ });
583
+ run("claim holder (when declared `fence.holder`): a resolve carrying `expect.holder` wins only while the holder is current; a superseded holder loses with the row pending at the same rev (claimTerminal, when offered, answers that reading); no holder is unchanged", async () => {
584
+ const store = make();
585
+ if (store.fence?.holder !== true)
586
+ return;
587
+ const holders = options.holders;
588
+ assert.ok(holders !== undefined, "a store declaring `fence.holder` is judged with the binding's `holders` (the set its holder judge reads)");
589
+ holders.add("kit-holder-current");
590
+ const [a, b, c] = [createCheckpointFixture(), createCheckpointFixture(), createCheckpointFixture()];
591
+ for (const cp of [a, b, c])
592
+ await store.put(cp.token, cp);
593
+ assert.equal(await store.resolve(a.token, a.scope, ALLOW, { rev: 0, holder: "kit-holder-gone" }), false, "a holder that is not current loses");
594
+ const after = (await store.get(a.token));
595
+ assert.deepEqual([after.status, after.rev ?? 0], ["pending", 0], "the row is untouched by the lost CAS");
596
+ if (store.claimTerminal !== undefined) {
597
+ assert.deepEqual(await store.claimTerminal(a.token, a.scope, { kind: "resolve", outcome: ALLOW, expect: { rev: 0, holder: "kit-holder-gone" } }), { claimed: false, current: { status: "pending", rev: 0 } });
598
+ }
599
+ assert.equal(await store.resolve(a.token, a.scope, ALLOW, { rev: 0, holder: "kit-holder-current" }), true, "the current holder wins");
600
+ holders.delete("kit-holder-current");
601
+ assert.equal(await store.resolve(b.token, b.scope, ALLOW, { rev: 0, holder: "kit-holder-current" }), false, "a holder released after an earlier win is judged afresh");
602
+ assert.equal(await store.resolve(c.token, c.scope, ALLOW, { rev: 0 }), true, "no holder: the CAS is as before");
603
+ });
466
604
  await settle();
467
605
  }
468
606
  const sortByToken = (a, b) => a.token.localeCompare(b.token);