@sema-agent/core 7.54.1 → 7.55.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 (399) hide show
  1. package/CHANGELOG.md +437 -0
  2. package/dist/agents/agent-transcript-tool.js +2 -1
  3. package/dist/agents/builtin-agents.d.ts +12 -14
  4. package/dist/agents/cc-peer-directory.d.ts +36 -0
  5. package/dist/agents/cc-peer-directory.js +138 -0
  6. package/dist/agents/cc-peer-wire.d.ts +105 -0
  7. package/dist/agents/cc-peer-wire.js +98 -0
  8. package/dist/agents/cc-uds-peer-channel.d.ts +40 -0
  9. package/dist/agents/cc-uds-peer-channel.js +457 -0
  10. package/dist/agents/cross-session-envelope.d.ts +4 -1
  11. package/dist/agents/cross-session-envelope.js +8 -1
  12. package/dist/agents/cross-session-judge.d.ts +2 -2
  13. package/dist/agents/list-agents-tool.js +2 -1
  14. package/dist/agents/observer.d.ts +4 -5
  15. package/dist/agents/observer.js +2 -1
  16. package/dist/agents/peer-admission.d.ts +6 -0
  17. package/dist/agents/peer-admission.js +4 -1
  18. package/dist/agents/peer-directory.d.ts +43 -9
  19. package/dist/agents/peer-directory.js +66 -23
  20. package/dist/agents/peer-notices.d.ts +1 -1
  21. package/dist/agents/peer-session-drain.d.ts +3 -1
  22. package/dist/agents/peer-session-drain.js +1 -0
  23. package/dist/agents/read-inbox-tool.js +2 -1
  24. package/dist/agents/retain-ledger.d.ts +1 -1
  25. package/dist/agents/roster-store.d.ts +1 -1
  26. package/dist/agents/send-message-tool.d.ts +10 -0
  27. package/dist/agents/send-message-tool.js +34 -5
  28. package/dist/agents/subagent.d.ts +34 -79
  29. package/dist/agents/subagent.js +45 -46
  30. package/dist/agents/tool-filter.d.ts +29 -46
  31. package/dist/agents/tool-filter.js +8 -31
  32. package/dist/agents/verify.d.ts +3 -3
  33. package/dist/brain/anthropic.js +14 -9
  34. package/dist/brain/errors.d.ts +15 -0
  35. package/dist/brain/errors.js +10 -0
  36. package/dist/brain/model-presets.d.ts +4 -4
  37. package/dist/brain/open-responses.js +15 -10
  38. package/dist/brain/openai.js +17 -12
  39. package/dist/brain/provider-dialect.d.ts +39 -3
  40. package/dist/brain/provider-dialect.js +28 -0
  41. package/dist/brain/reasoning.d.ts +1 -1
  42. package/dist/brain/request-params.d.ts +55 -5
  43. package/dist/brain/request-params.js +35 -3
  44. package/dist/brain/retry.d.ts +1 -1
  45. package/dist/brain/route-adjudicator.d.ts +15 -0
  46. package/dist/brain/route-adjudicator.js +15 -0
  47. package/dist/brain/stream-engine.js +6 -5
  48. package/dist/brain/stream-shared.d.ts +1 -1
  49. package/dist/core/advisor-verdict.d.ts +120 -0
  50. package/dist/core/advisor-verdict.js +92 -0
  51. package/dist/core/agent-definition.d.ts +7 -8
  52. package/dist/core/allow-entry.d.ts +49 -23
  53. package/dist/core/allow-entry.js +57 -32
  54. package/dist/core/allow-rule-shadow.d.ts +7 -0
  55. package/dist/core/approval-object.d.ts +2 -1
  56. package/dist/core/approval-object.js +6 -6
  57. package/dist/core/ask-origin.d.ts +63 -11
  58. package/dist/core/ask-origin.js +27 -5
  59. package/dist/core/ask-question.js +10 -4
  60. package/dist/core/auto-compaction.d.ts +11 -8
  61. package/dist/core/auto-compaction.js +4 -2
  62. package/dist/core/auto-mode-arming.d.ts +2 -2
  63. package/dist/core/auto-mode-defaults.d.ts +8 -8
  64. package/dist/core/auto-mode-prompt-assets.d.ts +3 -3
  65. package/dist/core/auto-mode-prompt.d.ts +1 -1
  66. package/dist/core/auto-mode.d.ts +17 -9
  67. package/dist/core/auto-mode.js +9 -6
  68. package/dist/core/background-agent-store.d.ts +3 -3
  69. package/dist/core/canonical-json.d.ts +1 -1
  70. package/dist/core/canonical-json.js +2 -2
  71. package/dist/core/checkpoint-requirements.d.ts +6 -3
  72. package/dist/core/checkpoint-requirements.js +4 -14
  73. package/dist/core/checkpoint-store.d.ts +80 -51
  74. package/dist/core/checkpoint-store.js +56 -74
  75. package/dist/core/child-clamp-seats.d.ts +6 -0
  76. package/dist/core/child-clamp-seats.js +3 -16
  77. package/dist/core/compliance.d.ts +2 -2
  78. package/dist/core/context-edit.d.ts +6 -7
  79. package/dist/core/cross-boundary-taint.d.ts +9 -3
  80. package/dist/core/cross-boundary-taint.js +5 -3
  81. package/dist/core/decision-receipt.js +2 -2
  82. package/dist/core/effective-path-target.d.ts +1 -1
  83. package/dist/core/engine-notice.d.ts +19 -5
  84. package/dist/core/exec-output-tail.d.ts +1 -1
  85. package/dist/core/file-history-retention.d.ts +1 -1
  86. package/dist/core/file-history-store.d.ts +59 -9
  87. package/dist/core/file-history-store.js +35 -17
  88. package/dist/core/gate-fold.js +3 -2
  89. package/dist/core/gate-lanes.js +12 -8
  90. package/dist/core/gate-outcome.d.ts +17 -4
  91. package/dist/core/gate-outcome.js +30 -0
  92. package/dist/core/governance-codes.d.ts +13 -3
  93. package/dist/core/governance-codes.js +10 -0
  94. package/dist/core/hands-band.d.ts +1 -1
  95. package/dist/core/hooks.d.ts +4 -4
  96. package/dist/core/host-target.d.ts +49 -0
  97. package/dist/core/host-target.js +59 -0
  98. package/dist/core/image-downsample.d.ts +1 -1
  99. package/dist/core/lsp-diagnostics.d.ts +4 -4
  100. package/dist/core/lsp.d.ts +3 -3
  101. package/dist/core/mailbox-store.d.ts +4 -0
  102. package/dist/core/mcp-connection-pool.d.ts +18 -2
  103. package/dist/core/mcp-connection-pool.js +53 -6
  104. package/dist/core/mcp-failure.d.ts +23 -4
  105. package/dist/core/mcp-failure.js +26 -11
  106. package/dist/core/mcp-listing-drift.d.ts +10 -0
  107. package/dist/core/mcp-listing-drift.js +6 -0
  108. package/dist/core/mcp-server-link.d.ts +173 -0
  109. package/dist/core/mcp-server-link.js +127 -0
  110. package/dist/core/mcp-server-spec.d.ts +4 -2
  111. package/dist/core/mcp-session-end.d.ts +26 -0
  112. package/dist/core/mcp-session-end.js +29 -0
  113. package/dist/core/mcp-startup-request.d.ts +17 -0
  114. package/dist/core/mcp-startup-request.js +17 -0
  115. package/dist/core/mcp.d.ts +13 -13
  116. package/dist/core/mcp.js +187 -162
  117. package/dist/core/media-byte-cap.d.ts +1 -1
  118. package/dist/core/memory-engine/engine.d.ts +9 -9
  119. package/dist/core/memory-engine/provenance-wording.d.ts +1 -1
  120. package/dist/core/memory-engine/tools.js +4 -1
  121. package/dist/core/mode-policy.d.ts +1 -1
  122. package/dist/core/namespaced-rule-name.d.ts +33 -7
  123. package/dist/core/namespaced-rule-name.js +39 -25
  124. package/dist/core/output-style.d.ts +1 -1
  125. package/dist/core/param-rule-reach.d.ts +15 -0
  126. package/dist/core/param-rule-reach.js +30 -0
  127. package/dist/core/parent-spec-seat.d.ts +8 -1
  128. package/dist/core/parent-spec-seat.js +4 -0
  129. package/dist/core/pause-registry.d.ts +16 -5
  130. package/dist/core/pause-registry.js +6 -2
  131. package/dist/core/peer-channel.d.ts +70 -0
  132. package/dist/core/peer-channel.js +1 -0
  133. package/dist/core/permission-rule-consent.d.ts +11 -5
  134. package/dist/core/permission-rule-consent.js +37 -30
  135. package/dist/core/permission-rule-model.d.ts +233 -64
  136. package/dist/core/permission-rule-model.js +384 -85
  137. package/dist/core/permission-rule-provider.d.ts +2 -1
  138. package/dist/core/permission-rule-store.d.ts +20 -25
  139. package/dist/core/permission-rule-store.js +1 -4
  140. package/dist/core/permission-rule-sync.d.ts +11 -2
  141. package/dist/core/permission-rule-sync.js +8 -1
  142. package/dist/core/permission-rules.d.ts +49 -11
  143. package/dist/core/permission-rules.js +123 -89
  144. package/dist/core/persisted-rule-arms.d.ts +4 -2
  145. package/dist/core/present-plan-tool.js +3 -1
  146. package/dist/core/protocol-naming.d.ts +2 -2
  147. package/dist/core/read-only-shell-table.d.ts +1 -1
  148. package/dist/core/read-only-shell.d.ts +1 -1
  149. package/dist/core/roles.d.ts +6 -2
  150. package/dist/core/roles.js +45 -44
  151. package/dist/core/rule-offer-read.d.ts +53 -0
  152. package/dist/core/rule-offer-read.js +146 -0
  153. package/dist/core/rule-owner.d.ts +46 -0
  154. package/dist/core/rule-owner.js +28 -0
  155. package/dist/core/runner/advisor-instructions.d.ts +70 -0
  156. package/dist/core/runner/advisor-instructions.js +61 -0
  157. package/dist/core/runner/advisor-tool.d.ts +33 -0
  158. package/dist/core/runner/advisor-tool.js +112 -0
  159. package/dist/core/runner/announce-once-ledger.d.ts +4 -26
  160. package/dist/core/runner/announce-once-ledger.js +14 -46
  161. package/dist/core/runner/approval-feedback.js +6 -3
  162. package/dist/core/runner/assemble-result.d.ts +1 -1
  163. package/dist/core/runner/caller-roster.d.ts +7 -0
  164. package/dist/core/runner/caller-roster.js +11 -0
  165. package/dist/core/runner/classifier-ladder.d.ts +27 -5
  166. package/dist/core/runner/classifier-ladder.js +12 -11
  167. package/dist/core/runner/compaction-restate.d.ts +10 -6
  168. package/dist/core/runner/compaction-restate.js +9 -3
  169. package/dist/core/runner/contracts.d.ts +13 -9
  170. package/dist/core/runner/conversation-segment.d.ts +28 -0
  171. package/dist/core/runner/conversation-segment.js +42 -0
  172. package/dist/core/runner/cross-boundary-lane.d.ts +6 -5
  173. package/dist/core/runner/cross-boundary-lane.js +26 -15
  174. package/dist/core/runner/decide-continuation.d.ts +7 -4
  175. package/dist/core/runner/decide-continuation.js +2 -2
  176. package/dist/core/runner/denial-limit-arms.d.ts +2 -2
  177. package/dist/core/runner/derived-route-fallback.d.ts +24 -24
  178. package/dist/core/runner/derived-route-fallback.js +9 -7
  179. package/dist/core/runner/gate-exit.d.ts +6 -0
  180. package/dist/core/runner/gate-exit.js +7 -2
  181. package/dist/core/runner/initial-run-state.js +1 -1
  182. package/dist/core/runner/permission-rule-lanes.d.ts +2 -2
  183. package/dist/core/runner/permission-rule-lanes.js +14 -7
  184. package/dist/core/runner/person-path-rows.js +1 -1
  185. package/dist/core/runner/prepare-advisor.d.ts +63 -0
  186. package/dist/core/runner/prepare-advisor.js +77 -0
  187. package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
  188. package/dist/core/runner/prepare-config-doors.d.ts +6 -4
  189. package/dist/core/runner/prepare-config-doors.js +18 -12
  190. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
  191. package/dist/core/runner/prepare-delegation-surface.js +1 -1
  192. package/dist/core/runner/prepare-file-history.js +39 -39
  193. package/dist/core/runner/prepare-gate-stations.d.ts +1 -4
  194. package/dist/core/runner/prepare-gate-stations.js +2 -7
  195. package/dist/core/runner/prepare-inherited-gate.d.ts +5 -4
  196. package/dist/core/runner/prepare-inherited-gate.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.js +7 -7
  198. package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
  199. package/dist/core/runner/prepare-policy-chain.js +4 -10
  200. package/dist/core/runner/prepare-project-context.d.ts +5 -2
  201. package/dist/core/runner/prepare-project-context.js +6 -3
  202. package/dist/core/runner/prepare-prompt-inputs.js +1 -1
  203. package/dist/core/runner/prepare-protocol-tools.d.ts +2 -2
  204. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  205. package/dist/core/runner/prepare-safety-scan.d.ts +9 -12
  206. package/dist/core/runner/prepare-safety-scan.js +8 -6
  207. package/dist/core/runner/prepare-task.js +15 -18
  208. package/dist/core/runner/prepare-tool-disclosure-mount.js +6 -4
  209. package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -3
  210. package/dist/core/runner/prepare-wiring-manifest.js +2 -2
  211. package/dist/core/runner/resume-policy-outcome.js +1 -1
  212. package/dist/core/runner/retired-runner-deps.js +1 -1
  213. package/dist/core/runner/run-attachment-seats.js +0 -1
  214. package/dist/core/runner/run-compaction-machinery.d.ts +1 -1
  215. package/dist/core/runner/run-compaction-machinery.js +2 -12
  216. package/dist/core/runner/run-git-lane.d.ts +1 -0
  217. package/dist/core/runner/run-harness-handlers.js +2 -3
  218. package/dist/core/runner/run-leg.js +13 -0
  219. package/dist/core/runner/run-settle-and-teardown.js +1 -1
  220. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  221. package/dist/core/runner/run-turn-boundary.d.ts +1 -0
  222. package/dist/core/runner/run-turn-boundary.js +14 -12
  223. package/dist/core/runner/session-rule-policy.d.ts +22 -21
  224. package/dist/core/runner/session-rule-policy.js +13 -28
  225. package/dist/core/runner/side-call-account.d.ts +32 -8
  226. package/dist/core/runner/side-call-account.js +23 -1
  227. package/dist/core/runner/strict-output-schema.d.ts +1 -1
  228. package/dist/core/runner/sub-agent-deny-lists.js +4 -3
  229. package/dist/core/runner/synthetic-tools.d.ts +10 -13
  230. package/dist/core/runner/synthetic-tools.js +4 -2
  231. package/dist/core/runner/tool-disclosure.js +2 -1
  232. package/dist/core/runner/tool-face-overlay.d.ts +2 -1
  233. package/dist/core/runner/tool-face-overlay.js +17 -1
  234. package/dist/core/runner/tool-progress-lane.d.ts +2 -2
  235. package/dist/core/runner/tool-turn-repair-seat.d.ts +3 -2
  236. package/dist/core/runner/turn-attachments.d.ts +60 -67
  237. package/dist/core/runner-deps.d.ts +37 -37
  238. package/dist/core/runtime-caps.d.ts +4 -4
  239. package/dist/core/safety-merge-corpus.js +17 -1
  240. package/dist/core/same-plain-value.d.ts +40 -0
  241. package/dist/core/same-plain-value.js +49 -0
  242. package/dist/core/scheduler.d.ts +1 -1
  243. package/dist/core/sensitive-path-policy.d.ts +1 -1
  244. package/dist/core/sensitive-path-policy.js +1 -1
  245. package/dist/core/session-policy-store.d.ts +77 -22
  246. package/dist/core/session-policy-store.js +56 -14
  247. package/dist/core/session-reconcile.d.ts +2 -2
  248. package/dist/core/session-store.d.ts +80 -30
  249. package/dist/core/session-store.js +118 -76
  250. package/dist/core/session.d.ts +24 -46
  251. package/dist/core/shared-memory/tools.js +3 -1
  252. package/dist/core/shared-memory/types.d.ts +1 -1
  253. package/dist/core/side-query.d.ts +10 -0
  254. package/dist/core/side-query.js +5 -0
  255. package/dist/core/store-contracts/checkpoint-store-contract.js +29 -2
  256. package/dist/core/store-contracts/file-history-store-contract.js +43 -3
  257. package/dist/core/store-contracts/session-policy-store-contract.d.ts +7 -0
  258. package/dist/core/store-contracts/session-policy-store-contract.js +130 -0
  259. package/dist/core/store-contracts/session-repo-contract.js +125 -0
  260. package/dist/core/store-contracts/session-store-contract.d.ts +4 -7
  261. package/dist/core/store-contracts/session-store-contract.js +215 -41
  262. package/dist/core/store-contracts/workflow-journal-store-contract.js +26 -1
  263. package/dist/core/surrogate-safe-slice.d.ts +3 -3
  264. package/dist/core/task-event.d.ts +10 -4
  265. package/dist/core/task-notification.d.ts +3 -3
  266. package/dist/core/task-registry-agent.d.ts +4 -4
  267. package/dist/core/task-registry-shared.d.ts +4 -4
  268. package/dist/core/task-registry-shared.js +1 -0
  269. package/dist/core/task-registry.d.ts +4 -4
  270. package/dist/core/task-registry.js +3 -1
  271. package/dist/core/task-result.d.ts +3 -3
  272. package/dist/core/task-spec.d.ts +52 -52
  273. package/dist/core/task-stream.d.ts +6 -6
  274. package/dist/core/task-tool-shape.d.ts +4 -4
  275. package/dist/core/tighten-task-spec.d.ts +1 -1
  276. package/dist/core/tool-catalog-entries.d.ts +19 -2
  277. package/dist/core/tool-catalog-entries.js +57 -42
  278. package/dist/core/tool-conformance.js +1 -0
  279. package/dist/core/tool-errors.d.ts +2 -2
  280. package/dist/core/tool-face-reader.js +5 -3
  281. package/dist/core/tool-face.d.ts +21 -2
  282. package/dist/core/tool-face.js +1 -1
  283. package/dist/core/tool-model-gate.d.ts +3 -3
  284. package/dist/core/tool-name-aliases.d.ts +29 -8
  285. package/dist/core/tool-name-aliases.js +27 -3
  286. package/dist/core/tool-name-readings.d.ts +5 -8
  287. package/dist/core/tool-name-readings.js +1 -2
  288. package/dist/core/tool-policy.d.ts +24 -5
  289. package/dist/core/tool-policy.js +34 -10
  290. package/dist/core/tool-registry.d.ts +14 -7
  291. package/dist/core/tool-registry.js +17 -7
  292. package/dist/core/tool-result-store.js +2 -1
  293. package/dist/core/tool-roster.d.ts +47 -18
  294. package/dist/core/tool-roster.js +53 -32
  295. package/dist/core/tool-spec-members.js +1 -1
  296. package/dist/core/tool-spec.d.ts +35 -21
  297. package/dist/core/tools.js +3 -2
  298. package/dist/core/trace.d.ts +4 -0
  299. package/dist/core/untrusted-egress.d.ts +1 -1
  300. package/dist/core/untrusted-text.d.ts +1 -1
  301. package/dist/core/warm-resume.d.ts +1 -1
  302. package/dist/core/wiring-manifest.d.ts +49 -0
  303. package/dist/core/wiring-manifest.js +13 -5
  304. package/dist/core/workflow-journal-store.d.ts +62 -0
  305. package/dist/core/workflow-journal-store.js +77 -1
  306. package/dist/core/workflow-run-store.d.ts +6 -0
  307. package/dist/core/workflow-run-store.js +2 -0
  308. package/dist/engine/compaction/compaction.d.ts +9 -9
  309. package/dist/engine/execution-env/node-execution-env.d.ts +19 -5
  310. package/dist/engine/execution-env/node-execution-env.js +43 -22
  311. package/dist/engine/harness/agent-harness.d.ts +14 -8
  312. package/dist/engine/harness/agent-harness.js +33 -26
  313. package/dist/engine/harness/types.d.ts +29 -7
  314. package/dist/engine/harness/types.js +12 -1
  315. package/dist/engine/llm/types.d.ts +9 -9
  316. package/dist/engine/llm/validation.d.ts +4 -1
  317. package/dist/engine/llm/validation.js +13 -1
  318. package/dist/engine/loop/agent-loop.d.ts +1 -1
  319. package/dist/engine/loop/agent-loop.js +10 -8
  320. package/dist/engine/loop/types.d.ts +15 -13
  321. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  322. package/dist/engine/session/memory-repo.d.ts +10 -11
  323. package/dist/engine/session/memory-repo.js +13 -8
  324. package/dist/engine/session/parent-chain-guard.d.ts +3 -2
  325. package/dist/engine/session/parent-chain-guard.js +1 -1
  326. package/dist/engine/session/repo-utils.d.ts +31 -3
  327. package/dist/engine/session/repo-utils.js +42 -5
  328. package/dist/index.d.ts +27 -15
  329. package/dist/index.js +21 -11
  330. package/dist/internal/harness-types.d.ts +1 -1
  331. package/dist/internal/harness.d.ts +3 -3
  332. package/dist/internal/harness.js +2 -2
  333. package/dist/internal/llm.d.ts +1 -1
  334. package/dist/internal/llm.js +1 -1
  335. package/dist/orchestration/goal.js +2 -2
  336. package/dist/orchestration/run-workflow-tool.d.ts +2 -2
  337. package/dist/orchestration/run-workflow-tool.js +5 -3
  338. package/dist/orchestration/workflow-size-guideline.d.ts +4 -4
  339. package/dist/orchestration/workflow-types.d.ts +18 -0
  340. package/dist/orchestration/workflow-types.js +6 -0
  341. package/dist/orchestration/workflow.d.ts +2 -13
  342. package/dist/orchestration/workflow.js +21 -31
  343. package/dist/prompt-assembly/event-registry.js +1 -0
  344. package/dist/prompt-assembly/tool-catalog.d.ts +3 -0
  345. package/dist/prompt-assembly/tool-catalog.js +3 -0
  346. package/dist/prompts/default.d.ts +14 -14
  347. package/dist/scenarios/full-body.d.ts +4 -4
  348. package/dist/stores/file/adoption/adopt.d.ts +4 -3
  349. package/dist/stores/file/adoption/adopt.js +24 -31
  350. package/dist/stores/file/adoption/marker.d.ts +6 -13
  351. package/dist/stores/file/adoption/marker.js +6 -15
  352. package/dist/stores/file/checkpoint-store.d.ts +3 -0
  353. package/dist/stores/file/checkpoint-store.js +9 -3
  354. package/dist/stores/file/file-history-store.d.ts +20 -19
  355. package/dist/stores/file/file-history-store.js +152 -135
  356. package/dist/stores/file/fs-atomic.d.ts +15 -0
  357. package/dist/stores/file/fs-atomic.js +89 -14
  358. package/dist/stores/file/index.d.ts +1 -1
  359. package/dist/stores/file/mailbox-store.d.ts +3 -0
  360. package/dist/stores/file/mailbox-store.js +3 -1
  361. package/dist/stores/file/permission-rule-adopt.d.ts +1 -1
  362. package/dist/stores/file/permission-rule-adopt.js +15 -16
  363. package/dist/stores/file/permission-rule-store.d.ts +18 -2
  364. package/dist/stores/file/permission-rule-store.js +21 -14
  365. package/dist/stores/file/session-policy-store.d.ts +3 -3
  366. package/dist/stores/file/session-store.d.ts +26 -11
  367. package/dist/stores/file/session-store.js +77 -40
  368. package/dist/stores/file/workflow-journal-store.d.ts +10 -4
  369. package/dist/stores/file/workflow-journal-store.js +28 -13
  370. package/dist/tools/artifact/artifact-text.d.ts +1 -1
  371. package/dist/tools/artifact/artifact-tool.js +2 -1
  372. package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -3
  373. package/dist/tools/fs/fs-bash.d.ts +2 -2
  374. package/dist/tools/fs/fs-bash.js +3 -1
  375. package/dist/tools/fs/fs-shared.d.ts +8 -8
  376. package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
  377. package/dist/tools/fs/notebook.d.ts +2 -2
  378. package/dist/tools/fs/read-deny.d.ts +29 -7
  379. package/dist/tools/fs/read-deny.js +63 -22
  380. package/dist/tools/fs/removal-targets.d.ts +1 -1
  381. package/dist/tools/fs/safety.d.ts +9 -9
  382. package/dist/tools/fs/search.d.ts +6 -6
  383. package/dist/tools/fs/shell-read-check.js +6 -1
  384. package/dist/tools/fs/shell-read-judge.d.ts +4 -0
  385. package/dist/tools/fs/shell-read-judge.js +5 -2
  386. package/dist/tools/loop-tick.d.ts +2 -2
  387. package/dist/tools/scheduler-tools.js +5 -1
  388. package/dist/tools/task-list.d.ts +7 -7
  389. package/dist/tools/task-list.js +5 -1
  390. package/dist/tools/todo.js +2 -1
  391. package/dist/tools/web.d.ts +1 -1
  392. package/dist/tools/web.js +40 -37
  393. package/dist/tools/worktree.d.ts +3 -3
  394. package/dist/tools/worktree.js +3 -1
  395. package/package.json +1 -1
  396. package/test/export-surface.snapshot.json +5756 -5638
  397. package/wire-changes/core@7.55.0.json +3318 -0
  398. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +0 -39
  399. package/dist/core/store-contracts/session-store-incarnation-contract.js +0 -95
@@ -3,6 +3,7 @@ import { createHash } from "node:crypto";
3
3
  import { chmod, mkdtemp, mkdir, rm, stat, symlink, writeFile, readFile } from "node:fs/promises";
4
4
  import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
+ import { isGovernanceRetryClass } from "../governance-codes.js";
6
7
  import { NodeExecutionEnv } from "../../internal/harness.js";
7
8
  import { beginContract } from "./contract-harness.js";
8
9
  function probeOver(inner) {
@@ -34,6 +35,13 @@ function envWithFailingRead(inner, fail) {
34
35
  },
35
36
  });
36
37
  }
38
+ const NAMES_A_FAULT = { track_failed: true, snapshot_failed: true, restore_failed: true, adopt_failed: true, unsupported_format: true, not_found: false, conflict: false, aborted: false };
39
+ function assertPermanenceWord(error, what) {
40
+ const p = error?.permanence;
41
+ assert.ok(p === undefined || isGovernanceRetryClass(p), `${what}: permanence must be absent or a GovernanceRetryClass word, got ${typeof p === "string" ? `"${p}"` : typeof p}`);
42
+ if (p !== undefined)
43
+ assert.ok(error !== undefined && NAMES_A_FAULT[error.code] === true, `${what}: ${error?.code} is not a fault and carries no permanence`);
44
+ }
37
45
  async function withTree(fn) {
38
46
  const dir = await mkdtemp(join(tmpdir(), "fh-kit-"));
39
47
  try {
@@ -52,9 +60,20 @@ export async function fileHistoryStoreContract(make, runAssertion) {
52
60
  const r = await store.restore("sc", "e1", env, dir);
53
61
  assert.equal(r.outcome, "failed");
54
62
  assert.equal(r.error?.code, "not_found");
63
+ assertPermanenceWord(r.error, "a boundary never made (not a fault)");
55
64
  assert.deepEqual(r.filesChanged, []);
56
65
  assert.deepEqual(r.refused, []);
57
66
  assert.equal(await store.reap("sc", []), 0);
67
+ if (store.exportHistory !== undefined)
68
+ assert.deepEqual(await store.exportHistory("sc"), { ok: true, history: null }, "nothing to export is `ok` with `history: null` — the one meaning of null");
69
+ await writeFile(join(dir, "f.txt"), "v1");
70
+ assert.deepEqual(await store.trackEdit("sc", join(dir, "f.txt"), env, dir), { ok: true, minted: true });
71
+ assert.deepEqual(await store.snapshot("sc", "b1", env, dir), { ok: true });
72
+ await writeFile(join(dir, "f.txt"), "v2-edited");
73
+ const never = await store.restore("sc", "never-made", env, dir);
74
+ assert.equal(never.error?.code, "not_found");
75
+ assert.deepEqual([never.filesChanged, never.identicalSkipped, never.refused, never.failed], [[], [], [], []], "a boundary never made has an empty ledger");
76
+ assert.equal(await readFile(join(dir, "f.txt"), "utf8"), "v2-edited", "a boundary never made touches nothing");
58
77
  });
59
78
  });
60
79
  run("first-touch idempotence: a second trackEdit for the same path is a cheap no-op (ZERO env reads)", async () => {
@@ -186,6 +205,8 @@ export async function fileHistoryStoreContract(make, runAssertion) {
186
205
  assert.equal(busy.ok, false);
187
206
  if (!busy.ok)
188
207
  assert.equal(busy.error.code, "conflict");
208
+ if (!busy.ok)
209
+ assertPermanenceWord(busy.error, "the target law's conflict");
189
210
  assert.equal(await store.canRestore("fresh", "b"), true);
190
211
  });
191
212
  });
@@ -301,10 +322,14 @@ export async function fileHistoryStoreContract(make, runAssertion) {
301
322
  });
302
323
  const t = await store.trackEdit("sc", join(dir, "f.txt"), hostile, dir);
303
324
  assert.equal(typeof t.ok, "boolean");
325
+ if (!t.ok)
326
+ assertPermanenceWord(t.error, "trackEdit over a hostile env");
304
327
  const sHealthy = await store.trackEdit("sc2", join(dir, "f.txt"), env, dir);
305
328
  assert.deepEqual(sHealthy, { ok: true, minted: true });
306
329
  const snap = await store.snapshot("sc2", "b", hostile, dir);
307
330
  assert.equal(snap.ok, false);
331
+ if (!snap.ok)
332
+ assertPermanenceWord(snap.error, "snapshot over a hostile env");
308
333
  const r = await store.restore("sc2", "nope", hostile, dir);
309
334
  assert.equal(r.outcome, "failed");
310
335
  });
@@ -364,7 +389,8 @@ export async function fileHistoryStoreContract(make, runAssertion) {
364
389
  await shed.write(join(dir, "keep.txt"), "turn-1");
365
390
  assert.deepEqual(await src.snapshot("sc", "b1", env, dir), { ok: true });
366
391
  assert.equal(await src.reap("sc", ["b0", "b1"]), 0);
367
- const envelope = await src.exportHistory("sc");
392
+ const exported = await src.exportHistory("sc");
393
+ const envelope = exported.ok ? exported.history : null;
368
394
  assert.ok(envelope !== null, "a scope with history must export an envelope");
369
395
  assert.equal(envelope.formatVersion, "file-history/1");
370
396
  assert.ok(envelope.tracked.every((t) => (t.keyKind === "root-relative" || t.keyKind === "absolute") && (t.keyFamily === "posix" || t.keyFamily === "win")), "every tracked row must carry its key's self-description (kind + family) — a bare key cannot be judged after it crosses machines");
@@ -409,6 +435,17 @@ export async function fileHistoryStoreContract(make, runAssertion) {
409
435
  });
410
436
  const m = await store.importHistory("sc", missingBlob, async () => undefined);
411
437
  assert.equal(m.ok, false);
438
+ if (!m.ok)
439
+ assert.equal(m.error.code, "adopt_failed", "a source that cannot supply a referenced blob");
440
+ if (!m.ok)
441
+ assertPermanenceWord(m.error, "importHistory whose source has no such blob");
442
+ assert.equal(await store.canRestore("sc", "b2"), false);
443
+ const thrown = await store.importHistory("sc", missingBlob, async () => {
444
+ throw Object.assign(new Error("source blob unreadable"), { code: "EACCES" });
445
+ });
446
+ assert.equal(thrown.ok, false);
447
+ if (!thrown.ok)
448
+ assertPermanenceWord(thrown.error, "importHistory whose source fetch threw");
412
449
  assert.equal(await store.canRestore("sc", "b2"), false);
413
450
  const duplicated = wireEnvelope({
414
451
  formatVersion: "file-history/1",
@@ -507,7 +544,9 @@ export async function fileHistoryStoreContract(make, runAssertion) {
507
544
  await shed.write(join(dir, "f.txt"), "source-pristine");
508
545
  assert.deepEqual(await src.trackEdit("sc", join(dir, "f.txt"), env, dir), { ok: true, minted: true });
509
546
  assert.deepEqual(await src.snapshot("sc", "b1", env, dir), { ok: true });
510
- const envelope = (await src.exportHistory("sc"));
547
+ const exported = await src.exportHistory("sc");
548
+ assert.ok(exported.ok && exported.history !== null, "a scope with history must export an envelope");
549
+ const envelope = exported.history;
511
550
  const dst = make();
512
551
  assert.deepEqual(await dst.importHistory("target", envelope, shed.fetch), { ok: true });
513
552
  assert.deepEqual(await dst.importHistory("target", envelope, shed.fetch), { ok: true }, "the same envelope arriving twice is a re-publish, not a conflict");
@@ -515,7 +554,8 @@ export async function fileHistoryStoreContract(make, runAssertion) {
515
554
  assert.deepEqual(await dst.trackEdit("target", join(dir, "local.txt"), env, dir), { ok: true, minted: true });
516
555
  assert.deepEqual(await dst.snapshot("target", "b-local", env, dir), { ok: true });
517
556
  const other = await src.exportHistory("sc");
518
- const conflict = await dst.importHistory("target", other, shed.fetch);
557
+ assert.ok(other.ok && other.history !== null);
558
+ const conflict = await dst.importHistory("target", other.history, shed.fetch);
519
559
  assert.equal(conflict.ok, false);
520
560
  if (!conflict.ok)
521
561
  assert.equal(conflict.error.code, "conflict");
@@ -0,0 +1,7 @@
1
+ import { type SessionPolicyStore } from "../session-policy-store.js";
2
+ import { type ContractAssertionRunner } from "./contract-harness.js";
3
+ /**
4
+ * The full cross-backend contract for the operator part of a {@link SessionPolicyStore} row. Every case builds its store via
5
+ * `make()`; with no runner the cases run sequentially and the first failure throws.
6
+ */
7
+ export declare function sessionPolicyStoreContract(make: () => SessionPolicyStore, runAssertion?: ContractAssertionRunner): Promise<void>;
@@ -0,0 +1,130 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { randomUUID } from "node:crypto";
3
+ import { SESSION_RULES_GRAMMAR, SessionPolicyError, readSessionRulesShape } from "../session-policy-store.js";
4
+ import { beginContract } from "./contract-harness.js";
5
+ const OWNER = "kit-owner";
6
+ const G = SESSION_RULES_GRAMMAR;
7
+ const A = "KitToolA";
8
+ const B = "KitToolB";
9
+ const fresh = () => `kit-2151-${randomUUID()}`;
10
+ const isLoosenRefusal = (e) => e instanceof SessionPolicyError && e.code === "loosen_forbidden";
11
+ async function partOf(store, sessionId) {
12
+ const row = await store.getRules(sessionId, OWNER);
13
+ assert.ok(row !== null, "the row the case wrote must read back");
14
+ const read = readSessionRulesShape(row);
15
+ assert.ok(!("fault" in read), `the stored row must read as rules: ${"fault" in read ? read.sentence : ""}`);
16
+ return read.rules.operatorRules;
17
+ }
18
+ export async function sessionPolicyStoreContract(make, runAssertion) {
19
+ const { run, settle } = beginContract(runAssertion);
20
+ run("a non-operator write over no row stamps an EMPTY operator part — the owner takes its own deny back", async () => {
21
+ const store = make();
22
+ const sid = fresh();
23
+ await store.putRules(sid, OWNER, { toolDeny: [A] });
24
+ assert.deepEqual(await partOf(store, sid), { grammar: G });
25
+ const back = await store.putRules(sid, OWNER, {});
26
+ assert.equal(back.toolDeny, undefined);
27
+ assert.deepEqual(await partOf(store, sid), { grammar: G });
28
+ });
29
+ run("an operator write's whole value is the operator's: no part on the row, and the owner's drop of its entry is refused with nothing written", async () => {
30
+ const store = make();
31
+ const sid = fresh();
32
+ const stored = await store.putRules(sid, OWNER, { toolDeny: [A] }, { operator: true });
33
+ assert.equal(await partOf(store, sid), undefined);
34
+ await assert.rejects(store.putRules(sid, OWNER, {}), isLoosenRefusal);
35
+ const row = await store.getRules(sid, OWNER);
36
+ assert.equal(row?.rev, stored.rev);
37
+ assert.deepEqual(row?.toolDeny, [A]);
38
+ });
39
+ run("an owner write over an operator row carries the operator's buckets as its part — above them it adds and takes back, below them it is refused", async () => {
40
+ const store = make();
41
+ const sid = fresh();
42
+ await store.putRules(sid, OWNER, { toolDeny: [A] }, { operator: true });
43
+ await store.putRules(sid, OWNER, { toolDeny: [A, B] });
44
+ assert.deepEqual(await partOf(store, sid), { toolDeny: [A], grammar: G });
45
+ await store.putRules(sid, OWNER, { toolDeny: [A] });
46
+ assert.deepEqual((await store.getRules(sid, OWNER))?.toolDeny, [A]);
47
+ await assert.rejects(store.putRules(sid, OWNER, {}), isLoosenRefusal);
48
+ assert.deepEqual(await partOf(store, sid), { toolDeny: [A], grammar: G });
49
+ });
50
+ run("the same entry from both writers: once an operator write carries it, the owner's drop is refused", async () => {
51
+ const store = make();
52
+ const sid = fresh();
53
+ await store.putRules(sid, OWNER, { toolDeny: [A] });
54
+ await store.putRules(sid, OWNER, { toolDeny: [A] }, { operator: true });
55
+ await assert.rejects(store.putRules(sid, OWNER, {}), isLoosenRefusal);
56
+ assert.deepEqual((await store.getRules(sid, OWNER))?.toolDeny, [A]);
57
+ });
58
+ run("a part handed in with the value is no authority: the stored part is the judge's", async () => {
59
+ const store = make();
60
+ const sid = fresh();
61
+ await store.putRules(sid, OWNER, { toolDeny: [A] }, { operator: true });
62
+ const forged = { toolDeny: [A, B], operatorRules: { grammar: G } };
63
+ await store.putRules(sid, OWNER, forged);
64
+ assert.deepEqual(await partOf(store, sid), { toolDeny: [A], grammar: G });
65
+ await assert.rejects(store.putRules(sid, OWNER, {}), isLoosenRefusal);
66
+ });
67
+ run("allowDirs: the owner's own confinement is the owner's to remove; an operator's is not", async () => {
68
+ const store = make();
69
+ const own = fresh();
70
+ await store.putRules(own, OWNER, { allowDirs: ["/work/src"] });
71
+ await store.putRules(own, OWNER, {});
72
+ assert.equal((await store.getRules(own, OWNER))?.allowDirs, undefined);
73
+ const operators = fresh();
74
+ await store.putRules(operators, OWNER, { allowDirs: ["/work"] }, { operator: true });
75
+ await store.putRules(operators, OWNER, { allowDirs: ["/work/src"] });
76
+ await store.putRules(operators, OWNER, { allowDirs: ["/work"] });
77
+ await assert.rejects(store.putRules(operators, OWNER, {}), isLoosenRefusal);
78
+ assert.deepEqual((await store.getRules(operators, OWNER))?.allowDirs, ["/work"]);
79
+ });
80
+ run("a stored row replayed by a non-operator onto a fresh or erased key keeps who may take what back (an import's replay)", async () => {
81
+ const store = make();
82
+ const exported = async (sid) => {
83
+ const records = store.listBySession !== undefined ? await store.listBySession(sid) : [{ principal: OWNER, rules: (await store.getRules(sid, OWNER)) }];
84
+ assert.equal(records.length, 1);
85
+ return records[0].rules;
86
+ };
87
+ const opSid = fresh();
88
+ await store.putRules(opSid, OWNER, { toolDeny: [A] }, { operator: true });
89
+ const opRow = await exported(opSid);
90
+ const replayed = fresh();
91
+ await store.putRules(replayed, OWNER, opRow);
92
+ await assert.rejects(store.putRules(replayed, OWNER, {}), isLoosenRefusal);
93
+ const mixedSid = fresh();
94
+ await store.putRules(mixedSid, OWNER, { toolDeny: [A] }, { operator: true });
95
+ await store.putRules(mixedSid, OWNER, { toolDeny: [A, B] });
96
+ const mixed = await exported(mixedSid);
97
+ const target = fresh();
98
+ await store.putRules(target, OWNER, mixed);
99
+ assert.deepEqual(await partOf(store, target), { toolDeny: [A], grammar: G });
100
+ await store.putRules(target, OWNER, { toolDeny: [A] });
101
+ await assert.rejects(store.putRules(target, OWNER, {}), isLoosenRefusal);
102
+ if (store.deleteBySession !== undefined) {
103
+ await store.deleteBySession(replayed);
104
+ await store.putRules(replayed, OWNER, opRow);
105
+ await assert.rejects(store.putRules(replayed, OWNER, {}), isLoosenRefusal);
106
+ }
107
+ });
108
+ run("a value built on the no-row record (`{ rev: 0 }`, what a read of an unwritten key answers) is written now: the owner takes back what it added through it", async () => {
109
+ const store = make();
110
+ const sid = fresh();
111
+ await store.putRules(sid, OWNER, { rev: 0, toolDeny: [A] });
112
+ assert.deepEqual(await partOf(store, sid), { grammar: G });
113
+ const back = await store.putRules(sid, OWNER, {});
114
+ assert.equal(back.toolDeny, undefined);
115
+ });
116
+ run("the part rides the session's enumeration (an export carries who may take what back)", async () => {
117
+ const store = make();
118
+ if (store.listBySession === undefined)
119
+ return;
120
+ const sid = fresh();
121
+ await store.putRules(sid, OWNER, { toolDeny: [A] }, { operator: true });
122
+ await store.putRules(sid, OWNER, { toolDeny: [A, B] });
123
+ const records = await store.listBySession(sid);
124
+ assert.equal(records.length, 1);
125
+ const read = readSessionRulesShape(records[0].rules);
126
+ assert.ok(!("fault" in read));
127
+ assert.deepEqual(read.rules.operatorRules, { toolDeny: [A], grammar: G });
128
+ });
129
+ await settle();
130
+ }
@@ -1,4 +1,5 @@
1
1
  import { strict as assert } from "node:assert";
2
+ import { TtlSessionStore } from "../session-store.js";
2
3
  import { beginContract } from "./contract-harness.js";
3
4
  const SESSION_ENTRY = (id, parentId) => ({
4
5
  id,
@@ -72,5 +73,129 @@ export async function sessionRepoContract(make, runAssertion) {
72
73
  assert.deepEqual(after.map((e) => e.parentId), [null, "e1"]);
73
74
  assert.equal((await repo.exportEntries(A)).length, 2, "importing into C must not disturb the donor session");
74
75
  });
76
+ run("the owner cell round-trips: create({id, owner}) records it and open() reads it back; a create naming no owner records null", async () => {
77
+ const repo = make();
78
+ const owned = await repo.create({ id: A, owner: "kit-alice" });
79
+ assert.equal((await owned.getMetadata()).owner, "kit-alice");
80
+ assert.equal((await (await repo.open({ id: A, createdAt: "" })).getMetadata()).owner, "kit-alice", "the owner is in the row, not in the handle");
81
+ const B = "0198fa00-0000-7000-8000-00000000000b";
82
+ await repo.create({ id: B });
83
+ assert.equal((await (await repo.open({ id: B, createdAt: "" })).getMetadata()).owner ?? null, null, "no owner named ⇒ anonymous");
84
+ });
85
+ run("create({id, owner: Y}) onto an existing row owned by X reopens it untouched: the row keeps X, and the session it returns carries X", async () => {
86
+ const repo = make();
87
+ const first = await repo.create({ id: A, owner: "kit-x" });
88
+ const token = (await first.getMetadata()).incarnation;
89
+ const again = await repo.create({ id: A, owner: "kit-y" });
90
+ assert.equal((await again.getMetadata()).owner, "kit-x", "a caller whose create met an existing row reads that row's owner, never its own");
91
+ assert.equal((await again.getMetadata()).incarnation, token, "and the same occupant");
92
+ assert.equal((await (await repo.open({ id: A, createdAt: "" })).getMetadata()).owner, "kit-x");
93
+ });
94
+ run("fork records the owner its options name in the fork row, else (absent or null) the source row's; importEntries onto an existing row keeps its metadata whole, onto an absent id records the importer", async () => {
95
+ const repo = make();
96
+ const src = await repo.create({ id: A, owner: "kit-alice" });
97
+ await src.getStorage().appendEntry(SESSION_ENTRY("e1", null));
98
+ const B = "0198fa00-0000-7000-8000-00000000000b";
99
+ const C = "0198fa00-0000-7000-8000-00000000000c";
100
+ const D = "0198fa00-0000-7000-8000-00000000000d";
101
+ assert.equal((await (await repo.fork({ id: A, createdAt: "" }, { id: B, owner: "kit-bob" })).getMetadata()).owner, "kit-bob");
102
+ assert.equal((await (await repo.fork({ id: A, createdAt: "" }, { id: C })).getMetadata()).owner, "kit-alice", "a fork whose options name no owner records the source row's — a copy of an owned history is never anonymous");
103
+ assert.equal((await (await repo.fork({ id: A, createdAt: "" }, { id: D, owner: null })).getMetadata()).owner, "kit-alice", "an explicit null never downgrades the copy either");
104
+ if (repo.importEntries === undefined)
105
+ return;
106
+ const token = (await src.getMetadata()).incarnation;
107
+ await repo.importEntries(A, "kit-carol", [SESSION_ENTRY("e1", null)]);
108
+ const imported = await (await repo.open({ id: A, createdAt: "" })).getMetadata();
109
+ assert.equal(imported.owner, "kit-alice", "the owner is recorded only by the write that creates the row: an import never re-stamps it");
110
+ assert.equal(imported.incarnation, token, "the log is replaced, not the occupant");
111
+ await repo.importEntries(A, undefined, []);
112
+ assert.equal((await (await repo.open({ id: A, createdAt: "" })).getMetadata()).owner, "kit-alice", "an import with no principal never makes an owned row anonymous");
113
+ const cells = (m) => [m.createdAt, m.forkedFrom, m.forkedFromIncarnation, m.incarnation, m.owner, JSON.stringify(m.placement)];
114
+ const forkRow = await (await repo.open({ id: B, createdAt: "" })).getMetadata();
115
+ await repo.importEntries(B, "kit-carol", [SESSION_ENTRY("e1", null)]);
116
+ assert.deepEqual(cells(await (await repo.open({ id: B, createdAt: "" })).getMetadata()), cells(forkRow), "the log is replaced, never the row: a fork row keeps its creation time and its lineage");
117
+ const E = "0198fa00-0000-7000-8000-00000000000e";
118
+ await repo.importEntries(E, "kit-carol", [SESSION_ENTRY("e1", null)]);
119
+ assert.equal((await (await repo.open({ id: E, createdAt: "" })).getMetadata()).owner, "kit-carol", "an import onto an absent id is a new row of the importer");
120
+ });
121
+ run("the metadata a repo hands out is not the row: writing to it (from open() or list()) changes no identity cell the row answers", async () => {
122
+ const repo = make();
123
+ const sess = await repo.create({ id: A, owner: "kit-alice" });
124
+ const token = (await sess.getMetadata()).incarnation;
125
+ for (const meta of [await sess.getMetadata(), await (await repo.open({ id: A, createdAt: "" })).getMetadata(), ...(await repo.list()).filter((m) => m.id === A)]) {
126
+ try {
127
+ Object.assign(meta, { owner: "kit-mallory", incarnation: "kit-forged" });
128
+ }
129
+ catch {
130
+ }
131
+ }
132
+ const again = await (await repo.open({ id: A, createdAt: "" })).getMetadata();
133
+ assert.equal(again.owner, "kit-alice", "the owner changes only through a write of the row");
134
+ assert.equal(again.incarnation, token);
135
+ if (repo.headOf !== undefined)
136
+ assert.deepEqual(await repo.headOf(A), { owner: "kit-alice", incarnation: token, leafId: null });
137
+ });
138
+ run("headOf (where the repo answers it): no row ⇒ undefined; a row ⇒ its owner, occupant and active leaf, as open() reads them", async () => {
139
+ const repo = make();
140
+ if (repo.headOf === undefined)
141
+ return;
142
+ assert.equal(await repo.headOf(A), undefined);
143
+ const sess = await repo.create({ id: A, owner: "kit-alice" });
144
+ await sess.getStorage().appendEntry(SESSION_ENTRY("e1", null));
145
+ await sess.getStorage().appendEntry(SESSION_ENTRY("e2", "e1"));
146
+ await sess.getStorage().setLeafId("e1");
147
+ const opened = await repo.open({ id: A, createdAt: "" });
148
+ assert.deepEqual(await repo.headOf(A), { owner: "kit-alice", incarnation: (await opened.getMetadata()).incarnation, leafId: await opened.getStorage().getLeafId() });
149
+ await repo.delete({ id: A, createdAt: "" });
150
+ assert.equal(await repo.headOf(A), undefined, "a deleted row has no head");
151
+ });
152
+ run("the store over a repo: a row replaced behind a cached session (deleted, then created again naming another owner) — the store's next acquire serves the new row, never the cached transcript", async () => {
153
+ const repo = make();
154
+ const store = new TtlSessionStore({ repo });
155
+ try {
156
+ if (store.headOf === undefined)
157
+ return;
158
+ await store.register(A, "kit-alice");
159
+ await (await store.acquire(A)).session.getStorage().appendEntry(SESSION_ENTRY("alice-only", null));
160
+ await repo.delete({ id: A, createdAt: "" });
161
+ await repo.create({ id: A, owner: "kit-bob" });
162
+ const served = (await store.acquire(A)).session;
163
+ assert.equal((await served.getMetadata()).owner, "kit-bob", "the session handed out is the row the owner is read from");
164
+ assert.deepEqual((await served.getStorage().getEntries()).map((e) => e.id), [], "the previous owner's transcript is not served under the new owner");
165
+ }
166
+ finally {
167
+ store.dispose();
168
+ }
169
+ });
170
+ run("the store over a repo answers headOf exactly when the repo does: present ⇒ the repo's own answer; absent ⇒ no headOf on the store, which still works", async () => {
171
+ const repo = make();
172
+ const store = new TtlSessionStore({ repo });
173
+ try {
174
+ assert.equal(typeof store.headOf, typeof repo.headOf === "function" ? "function" : "undefined");
175
+ const { sessionId } = await store.acquire(A);
176
+ if (store.headOf !== undefined)
177
+ assert.deepEqual(await store.headOf(sessionId), await repo.headOf(sessionId), "the store's head is the repo's, verbatim");
178
+ }
179
+ finally {
180
+ store.dispose();
181
+ }
182
+ const bare = {
183
+ create: (o) => repo.create(o),
184
+ open: (m) => repo.open(m),
185
+ list: () => repo.list(),
186
+ delete: (m) => repo.delete(m),
187
+ fork: (s, o) => repo.fork(s, o),
188
+ };
189
+ const over = new TtlSessionStore({ repo: bare });
190
+ try {
191
+ assert.equal(over.headOf, undefined, "no head read on the repo ⇒ none on the store (an authorizer takes its fail-closed arm)");
192
+ const B = "0198fa00-0000-7000-8000-00000000000b";
193
+ await over.acquire(B);
194
+ await over.register(B, "kit-alice");
195
+ }
196
+ finally {
197
+ over.dispose();
198
+ }
199
+ });
75
200
  await settle();
76
201
  }
@@ -1,6 +1,5 @@
1
1
  import { type SessionStore } from "../session.js";
2
2
  import { type ContractAssertionRunner } from "./contract-harness.js";
3
- import { type SessionStoreIncarnationContractHooks } from "./session-store-incarnation-contract.js";
4
3
  /**
5
4
  * The cross-backend contract for {@link SessionStore} as the interface states it TODAY — what a store must
6
5
  * do so that the engine, a deployment's authorizer and a shell read the same answers off it whether the
@@ -18,15 +17,13 @@ import { type SessionStoreIncarnationContractHooks } from "./session-store-incar
18
17
  * What the kit cannot see from outside, it does not claim: idle eviction and `pin` (the interface has no
19
18
  * clock seam), what `release` does to an ordinary session's live set (`size` is best-effort and a durable
20
19
  * `list` may still show the history), a durable backend's crash recovery, and a delete that fails half-way.
21
- * One stated obligation is not a case yet because the bundled store does not keep it: an ownership row
22
- * outliving its session across a restart (`TtlSessionStore` holds owners in process memory).
23
20
  *
24
21
  * Semantics since @sema-agent/core 7.55.0.
25
22
  */
26
23
  export interface SessionStoreContractHooks {
27
- /** The same hook as the incarnation kit's ({@link SessionStoreIncarnationContractHooks.deleteSession}): one
28
- * function serves both kits. */
29
- deleteSession: SessionStoreIncarnationContractHooks["deleteSession"];
24
+ /** Delete `sessionId`'s session ROW through the backend's own deletion path (history and all), so that a later
25
+ * `acquire(sessionId)` creates a new session under the same id. */
26
+ deleteSession: (store: SessionStore, sessionId: string) => Promise<void>;
30
27
  /** A SECOND, independent store instance over the same rows as `store` — what a restarted process
31
28
  * constructs. After calling it the kit no longer uses `store`, so a backend that holds an exclusive lock
32
29
  * may release `store`'s first (the kit still disposes `store` at the end of the case: that dispose must
@@ -40,7 +37,7 @@ export interface SessionStoreContractHooks {
40
37
  /** Optional faces (plus the replica hook, and `requireExisting` — absent = the store rejects the option, its other
41
38
  * legal mode) a run verified or found absent; reported per judging case, after its assertions held. */
42
39
  onOptionalMember?: (report: {
43
- member: "requireExisting" | "forget" | "fork" | "list" | "noteTaskRun" | "listPlaced" | "placements" | "ownership" | "incarnationOf" | "replica";
40
+ member: "requireExisting" | "forget" | "fork" | "list" | "noteTaskRun" | "listPlaced" | "placements" | "headOf" | "ownership" | "replica";
44
41
  status: "verified" | "absent";
45
42
  }) => void;
46
43
  }