@sema-agent/core 7.54.1 → 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 (621) hide show
  1. package/CHANGELOG.md +950 -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/cascade.js +1 -1
  5. package/dist/agents/cc-peer-directory.d.ts +36 -0
  6. package/dist/agents/cc-peer-directory.js +139 -0
  7. package/dist/agents/cc-peer-wire.d.ts +105 -0
  8. package/dist/agents/cc-peer-wire.js +98 -0
  9. package/dist/agents/cc-uds-peer-channel.d.ts +40 -0
  10. package/dist/agents/cc-uds-peer-channel.js +457 -0
  11. package/dist/agents/child-model-seat.d.ts +0 -11
  12. package/dist/agents/child-model-seat.js +0 -3
  13. package/dist/agents/cross-session-envelope.d.ts +4 -1
  14. package/dist/agents/cross-session-envelope.js +8 -1
  15. package/dist/agents/cross-session-judge.d.ts +2 -2
  16. package/dist/agents/list-agents-tool.d.ts +3 -1
  17. package/dist/agents/list-agents-tool.js +9 -8
  18. package/dist/agents/observer.d.ts +4 -5
  19. package/dist/agents/observer.js +2 -1
  20. package/dist/agents/peer-admission.d.ts +6 -0
  21. package/dist/agents/peer-admission.js +4 -1
  22. package/dist/agents/peer-directory.d.ts +65 -13
  23. package/dist/agents/peer-directory.js +90 -25
  24. package/dist/agents/peer-notices.d.ts +1 -1
  25. package/dist/agents/peer-session-drain.d.ts +3 -1
  26. package/dist/agents/peer-session-drain.js +3 -2
  27. package/dist/agents/read-inbox-tool.js +2 -1
  28. package/dist/agents/repair-loop.js +2 -2
  29. package/dist/agents/retain-ledger.d.ts +1 -1
  30. package/dist/agents/roster-store.d.ts +1 -1
  31. package/dist/agents/send-message-tool.d.ts +10 -0
  32. package/dist/agents/send-message-tool.js +37 -8
  33. package/dist/agents/subagent.d.ts +39 -82
  34. package/dist/agents/subagent.js +109 -106
  35. package/dist/agents/tool-filter.d.ts +31 -46
  36. package/dist/agents/tool-filter.js +13 -33
  37. package/dist/agents/verify.d.ts +6 -5
  38. package/dist/agents/verify.js +3 -3
  39. package/dist/brain/anthropic.js +43 -81
  40. package/dist/brain/errors.d.ts +15 -0
  41. package/dist/brain/errors.js +10 -0
  42. package/dist/brain/media-degrade.d.ts +18 -7
  43. package/dist/brain/media-degrade.js +13 -2
  44. package/dist/brain/model-capability.d.ts +37 -0
  45. package/dist/brain/model-capability.js +34 -0
  46. package/dist/brain/model-presets.d.ts +6 -12
  47. package/dist/brain/model-presets.js +1 -6
  48. package/dist/brain/open-responses.js +27 -35
  49. package/dist/brain/openai.js +30 -98
  50. package/dist/brain/provider-dialect.d.ts +39 -3
  51. package/dist/brain/provider-dialect.js +28 -0
  52. package/dist/brain/reasoning-request.d.ts +96 -0
  53. package/dist/brain/reasoning-request.js +174 -0
  54. package/dist/brain/reasoning.d.ts +12 -12
  55. package/dist/brain/reasoning.js +4 -3
  56. package/dist/brain/request-params.d.ts +62 -9
  57. package/dist/brain/request-params.js +42 -7
  58. package/dist/brain/retry.d.ts +1 -1
  59. package/dist/brain/route-adjudicator.d.ts +15 -0
  60. package/dist/brain/route-adjudicator.js +15 -0
  61. package/dist/brain/status-sink.d.ts +20 -0
  62. package/dist/brain/status-sink.js +7 -0
  63. package/dist/brain/stream-engine.js +6 -5
  64. package/dist/brain/stream-shared.d.ts +1 -1
  65. package/dist/core/a2a.d.ts +2 -1
  66. package/dist/core/a2a.js +19 -13
  67. package/dist/core/advisor-verdict.d.ts +120 -0
  68. package/dist/core/advisor-verdict.js +92 -0
  69. package/dist/core/agent-definition.d.ts +17 -8
  70. package/dist/core/allow-entry.d.ts +69 -36
  71. package/dist/core/allow-entry.js +81 -52
  72. package/dist/core/allow-rule-shadow.d.ts +10 -3
  73. package/dist/core/approval-object.d.ts +2 -1
  74. package/dist/core/approval-object.js +6 -6
  75. package/dist/core/ask-origin.d.ts +127 -29
  76. package/dist/core/ask-origin.js +87 -25
  77. package/dist/core/ask-question.d.ts +51 -8
  78. package/dist/core/ask-question.js +12 -7
  79. package/dist/core/ask-unresolvable-notice.d.ts +8 -3
  80. package/dist/core/ask-unresolvable-notice.js +16 -2
  81. package/dist/core/auto-compaction.d.ts +11 -8
  82. package/dist/core/auto-compaction.js +4 -2
  83. package/dist/core/auto-mode-arming.d.ts +2 -2
  84. package/dist/core/auto-mode-defaults.d.ts +8 -8
  85. package/dist/core/auto-mode-prompt-assets.d.ts +3 -3
  86. package/dist/core/auto-mode-prompt.d.ts +1 -1
  87. package/dist/core/auto-mode.d.ts +17 -9
  88. package/dist/core/auto-mode.js +9 -6
  89. package/dist/core/background-agent-store.d.ts +3 -3
  90. package/dist/core/canonical-json.d.ts +1 -1
  91. package/dist/core/canonical-json.js +2 -2
  92. package/dist/core/checkpoint-generation.d.ts +53 -0
  93. package/dist/core/checkpoint-generation.js +35 -0
  94. package/dist/core/checkpoint-queries.d.ts +180 -0
  95. package/dist/core/checkpoint-queries.js +272 -0
  96. package/dist/core/checkpoint-requirements.d.ts +19 -4
  97. package/dist/core/checkpoint-requirements.js +8 -14
  98. package/dist/core/checkpoint-store.d.ts +165 -72
  99. package/dist/core/checkpoint-store.js +79 -82
  100. package/dist/core/child-clamp-seats.d.ts +6 -0
  101. package/dist/core/child-clamp-seats.js +3 -16
  102. package/dist/core/compliance.d.ts +2 -2
  103. package/dist/core/context-edit.d.ts +6 -7
  104. package/dist/core/cross-boundary-taint.d.ts +11 -4
  105. package/dist/core/cross-boundary-taint.js +3 -3
  106. package/dist/core/decision-receipt.d.ts +37 -3
  107. package/dist/core/decision-receipt.js +19 -19
  108. package/dist/core/delegation-not-started.d.ts +6 -0
  109. package/dist/core/delegation-not-started.js +17 -0
  110. package/dist/core/effective-path-target.d.ts +1 -1
  111. package/dist/core/engine-attestation.d.ts +93 -0
  112. package/dist/core/engine-attestation.js +70 -0
  113. package/dist/core/engine-capabilities.d.ts +4 -1
  114. package/dist/core/engine-capabilities.js +1 -1
  115. package/dist/core/engine-notice.d.ts +45 -12
  116. package/dist/core/exec-output-tail.d.ts +1 -1
  117. package/dist/core/file-history-retention.d.ts +1 -1
  118. package/dist/core/file-history-store.d.ts +147 -21
  119. package/dist/core/file-history-store.js +58 -32
  120. package/dist/core/gate-fold.d.ts +1 -1
  121. package/dist/core/gate-fold.js +24 -11
  122. package/dist/core/gate-lanes.js +81 -68
  123. package/dist/core/gate-outcome.d.ts +40 -6
  124. package/dist/core/gate-outcome.js +56 -0
  125. package/dist/core/governance-codes.d.ts +17 -3
  126. package/dist/core/governance-codes.js +16 -0
  127. package/dist/core/hands-band.d.ts +1 -1
  128. package/dist/core/hooks.d.ts +21 -20
  129. package/dist/core/hooks.js +3 -1
  130. package/dist/core/host-floor.d.ts +128 -0
  131. package/dist/core/host-floor.js +206 -0
  132. package/dist/core/host-target.d.ts +84 -0
  133. package/dist/core/host-target.js +92 -0
  134. package/dist/core/image-downsample.d.ts +1 -1
  135. package/dist/core/lsp-diagnostics.d.ts +4 -4
  136. package/dist/core/lsp.d.ts +3 -3
  137. package/dist/core/mailbox-store.d.ts +4 -0
  138. package/dist/core/mcp-connection-pool.d.ts +18 -2
  139. package/dist/core/mcp-connection-pool.js +53 -6
  140. package/dist/core/mcp-failure.d.ts +23 -4
  141. package/dist/core/mcp-failure.js +26 -11
  142. package/dist/core/mcp-listing-drift.d.ts +10 -0
  143. package/dist/core/mcp-listing-drift.js +6 -0
  144. package/dist/core/mcp-server-link.d.ts +173 -0
  145. package/dist/core/mcp-server-link.js +127 -0
  146. package/dist/core/mcp-server-spec.d.ts +4 -2
  147. package/dist/core/mcp-session-end.d.ts +26 -0
  148. package/dist/core/mcp-session-end.js +29 -0
  149. package/dist/core/mcp-startup-request.d.ts +17 -0
  150. package/dist/core/mcp-startup-request.js +17 -0
  151. package/dist/core/mcp.d.ts +17 -13
  152. package/dist/core/mcp.js +240 -189
  153. package/dist/core/media-byte-cap.d.ts +1 -1
  154. package/dist/core/memory-engine/delegation-settlement.js +15 -24
  155. package/dist/core/memory-engine/engine.d.ts +9 -9
  156. package/dist/core/memory-engine/engine.js +4 -4
  157. package/dist/core/memory-engine/layout.d.ts +60 -8
  158. package/dist/core/memory-engine/layout.js +65 -6
  159. package/dist/core/memory-engine/provenance-wording.d.ts +1 -1
  160. package/dist/core/memory-engine/tools.js +4 -1
  161. package/dist/core/mode-policy.d.ts +1 -1
  162. package/dist/core/model-seat.d.ts +10 -3
  163. package/dist/core/mods/classic-events.d.ts +67 -0
  164. package/dist/core/mods/classic-events.js +1 -0
  165. package/dist/core/mods/dollar.d.ts +13 -0
  166. package/dist/core/mods/extension-call.d.ts +31 -0
  167. package/dist/core/mods/extension-call.js +43 -0
  168. package/dist/core/mods/kernel/dispatch.d.ts +20 -3
  169. package/dist/core/mods/kernel/dispatch.js +140 -69
  170. package/dist/core/mods/kernel/events.d.ts +47 -11
  171. package/dist/core/mods/kernel/events.js +66 -42
  172. package/dist/core/mods/kernel/index.d.ts +4 -4
  173. package/dist/core/mods/kernel/index.js +2 -2
  174. package/dist/core/mods/kernel/receive.d.ts +39 -5
  175. package/dist/core/mods/kernel/receive.js +290 -17
  176. package/dist/core/mods/kernel/registration.d.ts +33 -2
  177. package/dist/core/mods/kernel/registration.js +6 -7
  178. package/dist/core/mods/kernel/site-rules.d.ts +44 -16
  179. package/dist/core/mods/kernel/site-rules.js +24 -42
  180. package/dist/core/mods/spec-seats.d.ts +69 -0
  181. package/dist/core/mods/spec-seats.js +114 -0
  182. package/dist/core/mods/stations.d.ts +28 -8
  183. package/dist/core/mods/stations.js +29 -13
  184. package/dist/core/mods/tool-call.d.ts +79 -0
  185. package/dist/core/mods/tool-call.js +152 -0
  186. package/dist/core/namespaced-rule-name.d.ts +44 -24
  187. package/dist/core/namespaced-rule-name.js +50 -43
  188. package/dist/core/output-style.d.ts +1 -1
  189. package/dist/core/param-rule-reach.d.ts +15 -0
  190. package/dist/core/param-rule-reach.js +30 -0
  191. package/dist/core/parent-spec-seat.d.ts +13 -1
  192. package/dist/core/parent-spec-seat.js +6 -0
  193. package/dist/core/park-selfcheck.js +1 -0
  194. package/dist/core/pause-registry.d.ts +16 -5
  195. package/dist/core/pause-registry.js +6 -2
  196. package/dist/core/peer-channel.d.ts +70 -0
  197. package/dist/core/peer-channel.js +1 -0
  198. package/dist/core/permission-rule-consent.d.ts +30 -5
  199. package/dist/core/permission-rule-consent.js +147 -50
  200. package/dist/core/permission-rule-model.d.ts +245 -65
  201. package/dist/core/permission-rule-model.js +421 -107
  202. package/dist/core/permission-rule-org.d.ts +6 -2
  203. package/dist/core/permission-rule-org.js +7 -3
  204. package/dist/core/permission-rule-provider.d.ts +6 -2
  205. package/dist/core/permission-rule-provider.js +41 -11
  206. package/dist/core/permission-rule-store.d.ts +22 -26
  207. package/dist/core/permission-rule-store.js +12 -6
  208. package/dist/core/permission-rule-sync.d.ts +11 -2
  209. package/dist/core/permission-rule-sync.js +11 -3
  210. package/dist/core/permission-rule-syntax.d.ts +5 -4
  211. package/dist/core/permission-rule-syntax.js +2 -2
  212. package/dist/core/permission-rules.d.ts +53 -15
  213. package/dist/core/permission-rules.js +125 -100
  214. package/dist/core/persisted-rule-arms.d.ts +6 -4
  215. package/dist/core/persisted-rule-arms.js +10 -12
  216. package/dist/core/present-plan-tool.js +3 -1
  217. package/dist/core/principal-name.d.ts +20 -0
  218. package/dist/core/principal-name.js +22 -0
  219. package/dist/core/prompt-section-seat.js +4 -6
  220. package/dist/core/protocol-http-auth.d.ts +26 -5
  221. package/dist/core/protocol-http-auth.js +137 -4
  222. package/dist/core/protocol-naming.d.ts +2 -2
  223. package/dist/core/read-only-shell-table.d.ts +1 -1
  224. package/dist/core/read-only-shell.d.ts +1 -1
  225. package/dist/core/retired-keys.d.ts +16 -0
  226. package/dist/core/retired-keys.js +16 -4
  227. package/dist/core/rewrite-rejudge.d.ts +60 -0
  228. package/dist/core/rewrite-rejudge.js +36 -0
  229. package/dist/core/roles.d.ts +34 -3
  230. package/dist/core/roles.js +66 -53
  231. package/dist/core/rule-lane-reads.d.ts +20 -15
  232. package/dist/core/rule-lane-reads.js +45 -17
  233. package/dist/core/rule-offer-read.d.ts +53 -0
  234. package/dist/core/rule-offer-read.js +146 -0
  235. package/dist/core/rule-owner.d.ts +46 -0
  236. package/dist/core/rule-owner.js +29 -0
  237. package/dist/core/runner/advisor-instructions.d.ts +70 -0
  238. package/dist/core/runner/advisor-instructions.js +61 -0
  239. package/dist/core/runner/advisor-tool.d.ts +33 -0
  240. package/dist/core/runner/advisor-tool.js +112 -0
  241. package/dist/core/runner/announce-once-ledger.d.ts +18 -26
  242. package/dist/core/runner/announce-once-ledger.js +19 -46
  243. package/dist/core/runner/approval-feedback.js +6 -3
  244. package/dist/core/runner/assemble-result.d.ts +2 -1
  245. package/dist/core/runner/assemble-result.js +1 -1
  246. package/dist/core/runner/call-hook-stations.d.ts +3 -2
  247. package/dist/core/runner/caller-roster.d.ts +21 -2
  248. package/dist/core/runner/caller-roster.js +38 -0
  249. package/dist/core/runner/capability-withheld-seat.d.ts +5 -0
  250. package/dist/core/runner/capability-withheld-seat.js +24 -0
  251. package/dist/core/runner/classifier-ladder.d.ts +33 -13
  252. package/dist/core/runner/classifier-ladder.js +15 -14
  253. package/dist/core/runner/compaction-restate.d.ts +10 -6
  254. package/dist/core/runner/compaction-restate.js +9 -3
  255. package/dist/core/runner/construction-doors.js +3 -0
  256. package/dist/core/runner/contracts.d.ts +40 -33
  257. package/dist/core/runner/conversation-segment.d.ts +28 -0
  258. package/dist/core/runner/conversation-segment.js +42 -0
  259. package/dist/core/runner/cross-boundary-lane.d.ts +21 -16
  260. package/dist/core/runner/cross-boundary-lane.js +58 -34
  261. package/dist/core/runner/decide-continuation.d.ts +7 -4
  262. package/dist/core/runner/decide-continuation.js +2 -2
  263. package/dist/core/runner/denial-limit-arms.d.ts +4 -4
  264. package/dist/core/runner/denial-limit-arms.js +1 -1
  265. package/dist/core/runner/derived-route-fallback.d.ts +24 -24
  266. package/dist/core/runner/derived-route-fallback.js +9 -7
  267. package/dist/core/runner/file-history-request.d.ts +18 -0
  268. package/dist/core/runner/file-history-request.js +21 -13
  269. package/dist/core/runner/file-history-restore.d.ts +45 -0
  270. package/dist/core/runner/file-history-restore.js +46 -0
  271. package/dist/core/runner/gate-exit.d.ts +9 -3
  272. package/dist/core/runner/gate-exit.js +11 -5
  273. package/dist/core/runner/image.d.ts +7 -17
  274. package/dist/core/runner/image.js +9 -60
  275. package/dist/core/runner/initial-run-state.js +1 -1
  276. package/dist/core/runner/permission-rule-lanes.d.ts +6 -6
  277. package/dist/core/runner/permission-rule-lanes.js +38 -15
  278. package/dist/core/runner/person-path-rows.d.ts +12 -9
  279. package/dist/core/runner/person-path-rows.js +19 -8
  280. package/dist/core/runner/prepare-advisor.d.ts +63 -0
  281. package/dist/core/runner/prepare-advisor.js +77 -0
  282. package/dist/core/runner/prepare-ask-lane.d.ts +2 -2
  283. package/dist/core/runner/prepare-ask-lane.js +5 -2
  284. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -6
  285. package/dist/core/runner/prepare-caps-and-workflow.js +5 -4
  286. package/dist/core/runner/prepare-config-doors.d.ts +31 -38
  287. package/dist/core/runner/prepare-config-doors.js +57 -34
  288. package/dist/core/runner/prepare-defer-classify.d.ts +4 -3
  289. package/dist/core/runner/prepare-defer-classify.js +7 -8
  290. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
  291. package/dist/core/runner/prepare-delegation-surface.js +1 -1
  292. package/dist/core/runner/prepare-file-history.d.ts +48 -8
  293. package/dist/core/runner/prepare-file-history.js +41 -73
  294. package/dist/core/runner/prepare-gate-stations.d.ts +2 -5
  295. package/dist/core/runner/prepare-gate-stations.js +12 -8
  296. package/dist/core/runner/prepare-hands-readface.d.ts +2 -8
  297. package/dist/core/runner/prepare-hands-readface.js +15 -14
  298. package/dist/core/runner/prepare-inherited-gate.d.ts +11 -7
  299. package/dist/core/runner/prepare-inherited-gate.js +8 -6
  300. package/dist/core/runner/prepare-memory-engine-session.js +1 -1
  301. package/dist/core/runner/prepare-park-ask.js +12 -12
  302. package/dist/core/runner/prepare-policy-chain.d.ts +7 -4
  303. package/dist/core/runner/prepare-policy-chain.js +68 -81
  304. package/dist/core/runner/prepare-project-context.d.ts +5 -2
  305. package/dist/core/runner/prepare-project-context.js +6 -3
  306. package/dist/core/runner/prepare-prompt-assembly.d.ts +5 -7
  307. package/dist/core/runner/prepare-prompt-assembly.js +10 -8
  308. package/dist/core/runner/prepare-prompt-inputs.d.ts +11 -9
  309. package/dist/core/runner/prepare-prompt-inputs.js +4 -6
  310. package/dist/core/runner/prepare-protocol-tools.d.ts +4 -2
  311. package/dist/core/runner/prepare-protocol-tools.js +5 -4
  312. package/dist/core/runner/prepare-safety-scan.d.ts +9 -12
  313. package/dist/core/runner/prepare-safety-scan.js +8 -23
  314. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
  315. package/dist/core/runner/prepare-suspend-saga.js +5 -6
  316. package/dist/core/runner/prepare-task.d.ts +1 -1
  317. package/dist/core/runner/prepare-task.js +41 -35
  318. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +7 -4
  319. package/dist/core/runner/prepare-tool-disclosure-mount.js +24 -18
  320. package/dist/core/runner/prepare-turn-wiring.d.ts +0 -4
  321. package/dist/core/runner/prepare-turn-wiring.js +2 -7
  322. package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -4
  323. package/dist/core/runner/prepare-wiring-manifest.js +3 -3
  324. package/dist/core/runner/resume-admission.js +1 -1
  325. package/dist/core/runner/resume-apply.js +11 -12
  326. package/dist/core/runner/resume-checkpoint-screen.js +3 -0
  327. package/dist/core/runner/resume-claim.d.ts +4 -2
  328. package/dist/core/runner/resume-claim.js +15 -8
  329. package/dist/core/runner/resume-policy-outcome.js +1 -1
  330. package/dist/core/runner/retired-runner-deps.js +2 -1
  331. package/dist/core/runner/rewind-files.d.ts +15 -0
  332. package/dist/core/runner/rewind-files.js +132 -0
  333. package/dist/core/runner/run-attachment-seats.js +2 -2
  334. package/dist/core/runner/run-brain-sinks.d.ts +4 -1
  335. package/dist/core/runner/run-brain-sinks.js +4 -2
  336. package/dist/core/runner/run-compaction-machinery.d.ts +1 -1
  337. package/dist/core/runner/run-compaction-machinery.js +2 -12
  338. package/dist/core/runner/run-git-lane.d.ts +1 -0
  339. package/dist/core/runner/run-harness-handlers.js +14 -7
  340. package/dist/core/runner/run-leg.d.ts +1 -1
  341. package/dist/core/runner/run-leg.js +43 -18
  342. package/dist/core/runner/run-settle-and-teardown.js +3 -2
  343. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  344. package/dist/core/runner/run-turn-boundary.d.ts +1 -0
  345. package/dist/core/runner/run-turn-boundary.js +15 -22
  346. package/dist/core/runner/runtask.d.ts +3 -0
  347. package/dist/core/runner/runtask.js +8 -9
  348. package/dist/core/runner/session-read-file-states.d.ts +15 -0
  349. package/dist/core/runner/session-read-file-states.js +28 -0
  350. package/dist/core/runner/session-rule-policy.d.ts +22 -21
  351. package/dist/core/runner/session-rule-policy.js +17 -31
  352. package/dist/core/runner/shell-env-withheld.d.ts +33 -0
  353. package/dist/core/runner/shell-env-withheld.js +51 -0
  354. package/dist/core/runner/side-call-account.d.ts +32 -8
  355. package/dist/core/runner/side-call-account.js +23 -1
  356. package/dist/core/runner/skills-seat.js +6 -2
  357. package/dist/core/runner/stream-settle-backstop.js +1 -1
  358. package/dist/core/runner/strict-output-schema.d.ts +1 -1
  359. package/dist/core/runner/sub-agent-deny-lists.js +4 -6
  360. package/dist/core/runner/synthetic-tools.d.ts +16 -13
  361. package/dist/core/runner/synthetic-tools.js +8 -3
  362. package/dist/core/runner/tool-disclosure.d.ts +10 -12
  363. package/dist/core/runner/tool-disclosure.js +3 -4
  364. package/dist/core/runner/tool-end-body.d.ts +1 -1
  365. package/dist/core/runner/tool-end-body.js +10 -9
  366. package/dist/core/runner/tool-face-overlay.d.ts +2 -1
  367. package/dist/core/runner/tool-face-overlay.js +17 -1
  368. package/dist/core/runner/tool-not-executed.d.ts +5 -0
  369. package/dist/core/runner/tool-not-executed.js +11 -0
  370. package/dist/core/runner/tool-occurrence.d.ts +8 -0
  371. package/dist/core/runner/tool-occurrence.js +7 -0
  372. package/dist/core/runner/tool-progress-lane.d.ts +2 -2
  373. package/dist/core/runner/tool-turn-repair-seat.d.ts +3 -2
  374. package/dist/core/runner/turn-attachments.d.ts +91 -80
  375. package/dist/core/runner/turn-attachments.js +62 -37
  376. package/dist/core/runner-deps.d.ts +42 -42
  377. package/dist/core/runtime-caps.d.ts +4 -4
  378. package/dist/core/safety-axis-vocab.d.ts +6 -4
  379. package/dist/core/safety-axis-vocab.js +6 -5
  380. package/dist/core/safety-merge-corpus.js +26 -2
  381. package/dist/core/same-plain-value.d.ts +40 -0
  382. package/dist/core/same-plain-value.js +49 -0
  383. package/dist/core/scheduler.d.ts +1 -1
  384. package/dist/core/secret-env.d.ts +26 -3
  385. package/dist/core/secret-env.js +44 -8
  386. package/dist/core/select-model.js +3 -3
  387. package/dist/core/sensitive-path-policy.d.ts +1 -1
  388. package/dist/core/sensitive-path-policy.js +1 -1
  389. package/dist/core/session-policy-store.d.ts +102 -35
  390. package/dist/core/session-policy-store.js +210 -48
  391. package/dist/core/session-reconcile.d.ts +2 -2
  392. package/dist/core/session-store.d.ts +106 -37
  393. package/dist/core/session-store.js +165 -91
  394. package/dist/core/session.d.ts +101 -57
  395. package/dist/core/session.js +53 -0
  396. package/dist/core/shared-memory/tools.js +3 -1
  397. package/dist/core/shared-memory/types.d.ts +1 -1
  398. package/dist/core/shell-read-silence.d.ts +11 -12
  399. package/dist/core/shell-read-silence.js +9 -5
  400. package/dist/core/shell-run-candidates.js +3 -1
  401. package/dist/core/shell-wrapper-projection.js +1 -1
  402. package/dist/core/shell-wrapper-table.d.ts +14 -7
  403. package/dist/core/shell-wrapper-table.js +5 -4
  404. package/dist/core/side-query.d.ts +10 -0
  405. package/dist/core/side-query.js +15 -1
  406. package/dist/core/skill-grant.d.ts +7 -2
  407. package/dist/core/skill-grant.js +5 -0
  408. package/dist/core/skills-directory.js +6 -2
  409. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +11 -1
  410. package/dist/core/store-contracts/checkpoint-store-contract.js +168 -3
  411. package/dist/core/store-contracts/file-history-store-contract.js +74 -4
  412. package/dist/core/store-contracts/peer-directory-contract.d.ts +7 -2
  413. package/dist/core/store-contracts/peer-directory-contract.js +14 -2
  414. package/dist/core/store-contracts/session-policy-store-contract.d.ts +7 -0
  415. package/dist/core/store-contracts/session-policy-store-contract.js +130 -0
  416. package/dist/core/store-contracts/session-repo-contract.js +176 -0
  417. package/dist/core/store-contracts/session-store-contract.d.ts +6 -8
  418. package/dist/core/store-contracts/session-store-contract.js +361 -51
  419. package/dist/core/store-contracts/workflow-journal-store-contract.js +26 -1
  420. package/dist/core/stub-env.d.ts +2 -0
  421. package/dist/core/stub-env.js +1 -0
  422. package/dist/core/surrogate-safe-slice.d.ts +3 -3
  423. package/dist/core/synthetic-read-verdict.js +3 -3
  424. package/dist/core/task-event.d.ts +42 -8
  425. package/dist/core/task-notification.d.ts +19 -15
  426. package/dist/core/task-registry-agent.d.ts +4 -4
  427. package/dist/core/task-registry-shared.d.ts +4 -4
  428. package/dist/core/task-registry-shared.js +1 -0
  429. package/dist/core/task-registry.d.ts +13 -4
  430. package/dist/core/task-registry.js +14 -2
  431. package/dist/core/task-result.d.ts +21 -3
  432. package/dist/core/task-spec.d.ts +69 -64
  433. package/dist/core/task-stream.d.ts +6 -6
  434. package/dist/core/task-tool-shape.d.ts +4 -4
  435. package/dist/core/tighten-task-spec.d.ts +1 -1
  436. package/dist/core/tighten-task-spec.js +2 -1
  437. package/dist/core/tool-catalog-entries.d.ts +36 -2
  438. package/dist/core/tool-catalog-entries.js +69 -43
  439. package/dist/core/tool-check-reading.d.ts +11 -0
  440. package/dist/core/tool-check-reading.js +11 -6
  441. package/dist/core/tool-conformance.js +1 -0
  442. package/dist/core/tool-errors.d.ts +2 -2
  443. package/dist/core/tool-face-reader.js +5 -3
  444. package/dist/core/tool-face.d.ts +21 -2
  445. package/dist/core/tool-face.js +1 -1
  446. package/dist/core/tool-model-gate.d.ts +5 -5
  447. package/dist/core/tool-name-aliases.d.ts +29 -8
  448. package/dist/core/tool-name-aliases.js +22 -4
  449. package/dist/core/tool-name-readings.d.ts +10 -43
  450. package/dist/core/tool-name-readings.js +2 -11
  451. package/dist/core/tool-policy.d.ts +85 -196
  452. package/dist/core/tool-policy.js +150 -175
  453. package/dist/core/tool-registry.d.ts +14 -7
  454. package/dist/core/tool-registry.js +17 -7
  455. package/dist/core/tool-result-store.js +5 -1
  456. package/dist/core/tool-roster.d.ts +57 -33
  457. package/dist/core/tool-roster.js +56 -68
  458. package/dist/core/tool-spec-members.js +8 -3
  459. package/dist/core/tool-spec.d.ts +53 -31
  460. package/dist/core/tools.js +51 -10
  461. package/dist/core/trace.d.ts +6 -2
  462. package/dist/core/types.d.ts +1 -1
  463. package/dist/core/untrusted-egress.d.ts +1 -1
  464. package/dist/core/untrusted-text.d.ts +1 -1
  465. package/dist/core/user-attachment.d.ts +8 -1
  466. package/dist/core/user-attachment.js +64 -4
  467. package/dist/core/warm-resume.d.ts +1 -1
  468. package/dist/core/wiring-manifest.d.ts +58 -0
  469. package/dist/core/wiring-manifest.js +14 -5
  470. package/dist/core/workflow-journal-store.d.ts +62 -0
  471. package/dist/core/workflow-journal-store.js +78 -2
  472. package/dist/core/workflow-run-store.d.ts +6 -0
  473. package/dist/core/workflow-run-store.js +2 -0
  474. package/dist/engine/compaction/compaction.d.ts +9 -9
  475. package/dist/engine/compaction/compaction.js +3 -2
  476. package/dist/engine/execution-env/node-execution-env.d.ts +26 -8
  477. package/dist/engine/execution-env/node-execution-env.js +54 -24
  478. package/dist/engine/harness/agent-harness.d.ts +39 -9
  479. package/dist/engine/harness/agent-harness.js +87 -33
  480. package/dist/engine/harness/types.d.ts +58 -9
  481. package/dist/engine/harness/types.js +12 -1
  482. package/dist/engine/llm/types.d.ts +36 -19
  483. package/dist/engine/llm/validation.d.ts +4 -1
  484. package/dist/engine/llm/validation.js +13 -1
  485. package/dist/engine/loop/agent-loop.d.ts +23 -1
  486. package/dist/engine/loop/agent-loop.js +26 -34
  487. package/dist/engine/loop/types.d.ts +31 -19
  488. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  489. package/dist/engine/session/memory-repo.d.ts +18 -12
  490. package/dist/engine/session/memory-repo.js +24 -9
  491. package/dist/engine/session/parent-chain-guard.d.ts +3 -2
  492. package/dist/engine/session/parent-chain-guard.js +1 -1
  493. package/dist/engine/session/repo-utils.d.ts +32 -3
  494. package/dist/engine/session/repo-utils.js +50 -5
  495. package/dist/index.d.ts +47 -23
  496. package/dist/index.js +35 -17
  497. package/dist/internal/harness-types.d.ts +1 -1
  498. package/dist/internal/harness.d.ts +3 -3
  499. package/dist/internal/harness.js +2 -2
  500. package/dist/internal/llm.d.ts +2 -2
  501. package/dist/internal/llm.js +1 -1
  502. package/dist/orchestration/goal.js +2 -2
  503. package/dist/orchestration/run-workflow-tool.d.ts +8 -6
  504. package/dist/orchestration/run-workflow-tool.js +10 -6
  505. package/dist/orchestration/workflow-size-guideline.d.ts +4 -4
  506. package/dist/orchestration/workflow-types.d.ts +18 -0
  507. package/dist/orchestration/workflow-types.js +6 -0
  508. package/dist/orchestration/workflow.d.ts +5 -13
  509. package/dist/orchestration/workflow.js +57 -49
  510. package/dist/prompt-assembly/artifact.d.ts +2 -2
  511. package/dist/prompt-assembly/artifact.js +5 -3
  512. package/dist/prompt-assembly/assemble.d.ts +3 -2
  513. package/dist/prompt-assembly/assemble.js +1 -1
  514. package/dist/prompt-assembly/catalog/schema.d.ts +43 -12
  515. package/dist/prompt-assembly/catalog/schema.js +34 -2
  516. package/dist/prompt-assembly/catalog/sections.js +0 -1
  517. package/dist/prompt-assembly/composer.d.ts +3 -1
  518. package/dist/prompt-assembly/composer.js +1 -1
  519. package/dist/prompt-assembly/event-registry.js +2 -0
  520. package/dist/prompt-assembly/overlay.js +3 -3
  521. package/dist/prompt-assembly/renderers.d.ts +1 -1
  522. package/dist/prompt-assembly/renderers.js +0 -3
  523. package/dist/prompt-assembly/role-base.d.ts +15 -0
  524. package/dist/prompt-assembly/role-base.js +9 -0
  525. package/dist/prompt-assembly/select.d.ts +39 -14
  526. package/dist/prompt-assembly/select.js +47 -16
  527. package/dist/prompt-assembly/sugar.d.ts +17 -11
  528. package/dist/prompt-assembly/sugar.js +8 -3
  529. package/dist/prompt-assembly/tool-catalog.d.ts +39 -2
  530. package/dist/prompt-assembly/tool-catalog.js +117 -4
  531. package/dist/prompt-assembly/turn-snapshot.d.ts +4 -10
  532. package/dist/prompt-assembly/turn-snapshot.js +0 -3
  533. package/dist/prompt-assembly/types.d.ts +0 -7
  534. package/dist/prompts/default.d.ts +14 -14
  535. package/dist/scenarios/full-body.d.ts +15 -15
  536. package/dist/scenarios/full-body.js +4 -3
  537. package/dist/shell/lex.js +10 -1
  538. package/dist/stores/file/adoption/adopt.d.ts +4 -3
  539. package/dist/stores/file/adoption/adopt.js +27 -33
  540. package/dist/stores/file/adoption/marker.d.ts +6 -13
  541. package/dist/stores/file/adoption/marker.js +9 -17
  542. package/dist/stores/file/checkpoint-store.d.ts +24 -0
  543. package/dist/stores/file/checkpoint-store.js +45 -7
  544. package/dist/stores/file/file-history-store.d.ts +20 -19
  545. package/dist/stores/file/file-history-store.js +157 -141
  546. package/dist/stores/file/fs-atomic.d.ts +15 -0
  547. package/dist/stores/file/fs-atomic.js +89 -14
  548. package/dist/stores/file/index.d.ts +1 -1
  549. package/dist/stores/file/index.js +2 -1
  550. package/dist/stores/file/mailbox-store.d.ts +3 -0
  551. package/dist/stores/file/mailbox-store.js +3 -1
  552. package/dist/stores/file/permission-rule-adopt.d.ts +1 -1
  553. package/dist/stores/file/permission-rule-adopt.js +18 -18
  554. package/dist/stores/file/permission-rule-store.d.ts +18 -2
  555. package/dist/stores/file/permission-rule-store.js +31 -18
  556. package/dist/stores/file/session-policy-store.d.ts +3 -3
  557. package/dist/stores/file/session-store.d.ts +54 -16
  558. package/dist/stores/file/session-store.js +114 -85
  559. package/dist/stores/file/workflow-journal-store.d.ts +10 -4
  560. package/dist/stores/file/workflow-journal-store.js +28 -13
  561. package/dist/tools/artifact/artifact-text.d.ts +1 -1
  562. package/dist/tools/artifact/artifact-tool.js +5 -3
  563. package/dist/tools/fs/bash-program-position.d.ts +19 -2
  564. package/dist/tools/fs/bash-program-position.js +56 -9
  565. package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -3
  566. package/dist/tools/fs/fs-bash.d.ts +2 -2
  567. package/dist/tools/fs/fs-bash.js +8 -6
  568. package/dist/tools/fs/fs-glob.d.ts +86 -0
  569. package/dist/tools/fs/fs-glob.js +226 -0
  570. package/dist/tools/fs/fs-read.js +14 -14
  571. package/dist/tools/fs/fs-search-tools.js +7 -7
  572. package/dist/tools/fs/fs-shared.d.ts +8 -8
  573. package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
  574. package/dist/tools/fs/notebook.d.ts +2 -2
  575. package/dist/tools/fs/pattern-kill-guard.d.ts +22 -0
  576. package/dist/tools/fs/pattern-kill-guard.js +397 -0
  577. package/dist/tools/fs/pdf.d.ts +9 -6
  578. package/dist/tools/fs/pdf.js +3 -3
  579. package/dist/tools/fs/program-read-model.d.ts +57 -7
  580. package/dist/tools/fs/program-read-model.js +11 -1
  581. package/dist/tools/fs/program-read-model.rows.js +78 -21
  582. package/dist/tools/fs/read-deny-home.js +3 -2
  583. package/dist/tools/fs/read-deny.d.ts +29 -7
  584. package/dist/tools/fs/read-deny.js +63 -22
  585. package/dist/tools/fs/removal-targets.d.ts +18 -8
  586. package/dist/tools/fs/removal-targets.js +47 -20
  587. package/dist/tools/fs/safety.d.ts +9 -9
  588. package/dist/tools/fs/search.d.ts +16 -6
  589. package/dist/tools/fs/search.js +20 -26
  590. package/dist/tools/fs/shell-read-argv.js +89 -12
  591. package/dist/tools/fs/shell-read-bindings.d.ts +31 -0
  592. package/dist/tools/fs/shell-read-bindings.js +41 -0
  593. package/dist/tools/fs/shell-read-check.js +12 -20
  594. package/dist/tools/fs/shell-read-environment.d.ts +5 -1
  595. package/dist/tools/fs/shell-read-environment.js +3 -2
  596. package/dist/tools/fs/shell-read-establish.d.ts +15 -0
  597. package/dist/tools/fs/shell-read-establish.js +19 -20
  598. package/dist/tools/fs/shell-read-judge.d.ts +4 -0
  599. package/dist/tools/fs/shell-read-judge.js +5 -2
  600. package/dist/tools/fs/shell-read-plan.js +24 -16
  601. package/dist/tools/fs/shell-read-start.d.ts +6 -1
  602. package/dist/tools/fs/shell-read-write-targets.d.ts +5 -1
  603. package/dist/tools/fs/shell-read-write-targets.js +2 -3
  604. package/dist/tools/fs/shell-reentry-text.d.ts +9 -0
  605. package/dist/tools/fs/shell-reentry-text.js +40 -2
  606. package/dist/tools/loop-tick.d.ts +2 -2
  607. package/dist/tools/scheduler-tools.js +5 -1
  608. package/dist/tools/task-list.d.ts +7 -7
  609. package/dist/tools/task-list.js +5 -1
  610. package/dist/tools/todo.js +65 -64
  611. package/dist/tools/web.d.ts +20 -15
  612. package/dist/tools/web.js +55 -69
  613. package/dist/tools/worktree.d.ts +3 -3
  614. package/dist/tools/worktree.js +3 -1
  615. package/package.json +1 -1
  616. package/test/export-surface.snapshot.json +5894 -5638
  617. package/wire-changes/core@7.55.0.json +3318 -0
  618. package/wire-changes/core@7.56.0.json +3891 -0
  619. package/wire-changes/mods-kernel@7.56.0.json +303 -0
  620. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +0 -39
  621. package/dist/core/store-contracts/session-store-incarnation-contract.js +0 -95
@@ -1,10 +1,9 @@
1
1
  import { strict as assert } from "node:assert";
2
2
  import { uuidv7 } from "../../internal/harness.js";
3
- import { hasSessionFork, isSessionConflict } from "../session.js";
3
+ import { SESSION_LIST_LIMITS, hasSessionFork, isSessionConflict } from "../session.js";
4
4
  import { readMember } from "../thrown-value.js";
5
5
  import { receiptRefusal, receiveOwnProperties } from "../own-property-receipt.js";
6
6
  import { beginContract } from "./contract-harness.js";
7
- import { registerSessionIncarnationCases } from "./session-store-incarnation-contract.js";
8
7
  const AT = "2026-10-18T00:00:00.000Z";
9
8
  const AT_MS = Date.parse(AT);
10
9
  const STAMP = { api: "openai-completions", provider: "contract-kit", model: "contract-kit-model" };
@@ -12,9 +11,9 @@ const ZERO_USAGE = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalToke
12
11
  function userEntry(id, parentId, text = `t-${id}`) {
13
12
  return { id, parentId, type: "message", timestamp: AT, message: { role: "user", content: [{ type: "text", text }], timestamp: AT_MS } };
14
13
  }
15
- function assistantEntry(id, parentId, toolCall) {
16
- const content = toolCall !== undefined ? [{ type: "toolCall", id: toolCall, name: "Read", arguments: { file_path: "/kit/x" } }] : [{ type: "text", text: `a-${id}` }];
17
- return { id, parentId, type: "message", timestamp: AT, message: { role: "assistant", content, ...STAMP, usage: ZERO_USAGE, stopReason: toolCall !== undefined ? "toolUse" : "stop", timestamp: AT_MS } };
14
+ function assistantEntry(id, parentId, ...toolCalls) {
15
+ const content = toolCalls.length > 0 ? toolCalls.map((call) => ({ type: "toolCall", id: call, name: "Read", arguments: { file_path: "/kit/x" } })) : [{ type: "text", text: `a-${id}` }];
16
+ return { id, parentId, type: "message", timestamp: AT, message: { role: "assistant", content, ...STAMP, usage: ZERO_USAGE, stopReason: toolCalls.length > 0 ? "toolUse" : "stop", timestamp: AT_MS } };
18
17
  }
19
18
  function toolResultEntry(id, parentId, toolCallId) {
20
19
  return { id, parentId, type: "message", timestamp: AT, message: { role: "toolResult", toolCallId, toolName: "Read", content: [{ type: "text", text: "ok" }], isError: false, timestamp: AT_MS } };
@@ -89,8 +88,22 @@ function assertNewestFirst(rows) {
89
88
  function registerUntyped(store, sessionId, owner) {
90
89
  return Reflect.apply(store.register, store, [sessionId, owner]);
91
90
  }
92
- const hasOwnership = (s) => typeof s.ownerOf === "function" && typeof s.register === "function";
91
+ const hasHead = (s) => typeof s.headOf === "function";
92
+ const hasOwnership = (s) => hasHead(s) && typeof s.register === "function";
93
93
  const declaresPlacements = (s) => s.placements?.subagent !== undefined;
94
+ const headOf = (store, sessionId) => store.headOf(sessionId);
95
+ async function ownerOf(store, sessionId, what) {
96
+ const head = await headOf(store, sessionId);
97
+ assert.ok(head !== undefined, `${what}: the row exists, so headOf must answer it`);
98
+ return head.owner;
99
+ }
100
+ async function tokenOf(store, sessionId, what) {
101
+ const head = await headOf(store, sessionId);
102
+ assert.ok(head !== undefined, `${what}: the row exists, so headOf must answer it`);
103
+ assert.equal(typeof head.incarnation, "string", `${what}: a session row created through this store must carry an incarnation`);
104
+ assert.notEqual(head.incarnation, "", `${what}: an empty token names nobody`);
105
+ return head.incarnation;
106
+ }
94
107
  async function honorsRequireExisting(store) {
95
108
  const probe = freshId();
96
109
  await store.acquire(probe);
@@ -165,8 +178,8 @@ async function runSessionStoreContract(make, hooks) {
165
178
  noteTaskRun: (s) => typeof s.noteTaskRun === "function",
166
179
  listPlaced: (s) => declaresPlacements(s) && typeof s.listPlaced === "function",
167
180
  placements: declaresPlacements,
181
+ headOf: hasHead,
168
182
  ownership: hasOwnership,
169
- incarnationOf: (s) => typeof s.incarnationOf === "function",
170
183
  replica: () => hooks.replica !== undefined,
171
184
  };
172
185
  const faceRun = (name, needs, fn) => run(name, async (store, open) => {
@@ -214,8 +227,8 @@ async function runSessionStoreContract(make, hooks) {
214
227
  }
215
228
  if (typeof store.list === "function")
216
229
  assert.equal(rowOf(await store.list(), id), undefined, "the refused acquire must not surface on the session list");
217
- if (typeof store.incarnationOf === "function")
218
- assert.equal(await store.incarnationOf(id), undefined, "nor have a row");
230
+ if (hasHead(store))
231
+ assert.equal(await headOf(store, id), undefined, "nor have a row");
219
232
  report("requireExisting", honored ? "verified" : "absent");
220
233
  });
221
234
  run("[acquire] two concurrent acquires of one id are one session: one log, and the second write on the same parent loses the CAS", async (store) => {
@@ -438,10 +451,10 @@ async function runSessionStoreContract(make, hooks) {
438
451
  assert.equal(await store.fork(missing), null);
439
452
  if (await honorsRequireExisting(store))
440
453
  await rejectsWithCode(() => store.acquire(missing, { requireExisting: true }), "not_found", "a fork of a missing source must not create it");
441
- if (typeof store.incarnationOf === "function")
442
- assert.equal(await store.incarnationOf(missing), undefined, "a fork of a missing source must not create it");
454
+ if (hasHead(store))
455
+ assert.equal(await headOf(store, missing), undefined, "a fork of a missing source must not create it");
443
456
  });
444
- faceRun("[fork] a fork never inherits an in-flight assistant turn: a leaf turn whose tool call has no result yet (with or without sibling results already in) is left out", ["fork"], async (store) => {
457
+ faceRun("[fork] a fork never inherits an in-flight assistant turn: a turn with a tool call no result answers yet (with or without sibling results already in) is left out; a turn every call of which is answered is kept", ["fork"], async (store) => {
445
458
  const parallel = freshId();
446
459
  const p = (await store.acquire(parallel)).session;
447
460
  await append(p, userEntry("u1", null));
@@ -451,10 +464,18 @@ async function runSessionStoreContract(make, hooks) {
451
464
  const sequential = freshId();
452
465
  const q = (await store.acquire(sequential)).session;
453
466
  await append(q, userEntry("u1", null));
454
- await append(q, assistantEntry("a1", "u1", "call-1"));
467
+ await append(q, assistantEntry("a1", "u1", "call-1", "call-2"));
455
468
  await append(q, toolResultEntry("r1", "a1", "call-1"));
456
469
  const qf = await store.fork(sequential);
457
- assert.deepEqual(await pathIds((await readBack(store, qf))), ["u1"], "a sibling call's committed result does not settle the turn");
470
+ assert.deepEqual(await pathIds((await readBack(store, qf))), ["u1"], "a sibling call's committed result does not settle the turn: call-2 has none");
471
+ const answered = freshId();
472
+ const t = (await store.acquire(answered)).session;
473
+ await append(t, userEntry("u1", null));
474
+ await append(t, assistantEntry("a1", "u1", "call-1", "call-2"));
475
+ await append(t, toolResultEntry("r1", "a1", "call-2"));
476
+ await append(t, toolResultEntry("r2", "r1", "call-1"));
477
+ const tf = await store.fork(answered);
478
+ assert.deepEqual(await pathIds((await readBack(store, tf))), ["u1", "a1", "r1", "r2"], "a tool turn every call of which has its result is settled: a fork keeps it");
458
479
  const settled = freshId();
459
480
  const s = (await store.acquire(settled)).session;
460
481
  await append(s, userEntry("u1", null));
@@ -462,7 +483,7 @@ async function runSessionStoreContract(make, hooks) {
462
483
  const sf = await store.fork(settled);
463
484
  assert.deepEqual(await pathIds((await readBack(store, sf))), ["u1", "a1"], "a settled text turn forks whole");
464
485
  });
465
- faceRun("[list] list projects each live session once, newest-first by lastActiveAt, and a touch counts as activity", ["list"], async (store) => {
486
+ faceRun("[list] list projects each session row once, newest-first by lastActiveAt, and a touch counts as activity", ["list"], async (store) => {
466
487
  const [a, b, c] = [freshId(), freshId(), freshId()];
467
488
  await store.acquire(a);
468
489
  await tick();
@@ -473,7 +494,7 @@ async function runSessionStoreContract(make, hooks) {
473
494
  assertNewestFirst(rows);
474
495
  for (const id of [a, b, c])
475
496
  assert.equal(rows.filter((r) => r.sessionId === id).length, 1, `session ${id} is listed exactly once`);
476
- assertActivityOrder(rows, [c, b, a], "acquire is activity");
497
+ assertActivityOrder(rows, [c, b, a], "a session created later is more recent");
477
498
  for (const r of rows)
478
499
  assert.ok(r.createdAt === undefined || (typeof r.createdAt === "string" && r.createdAt !== ""), "createdAt is a real timestamp or absent, never an empty placeholder");
479
500
  const before = rowOf(rows, a).lastActiveAt;
@@ -492,25 +513,148 @@ async function runSessionStoreContract(make, hooks) {
492
513
  assert.equal(rowOf(rows, forkedId)?.forkedFrom, src, "the fork's row carries its lineage");
493
514
  assert.equal(rowOf(rows, src)?.forkedFrom, undefined, "the source is no fork");
494
515
  });
495
- faceRun("[list] noteTaskRun lands on the summary and counts as activity; a call without a run id clears lastTaskRunId; an unknown id never throws", ["noteTaskRun"], async (store) => {
516
+ faceRun("[list] noteTaskRun lands on the summary; a call without a run id clears lastTaskRunId; an unknown id never throws", ["noteTaskRun"], async (store) => {
496
517
  await store.noteTaskRun(freshId(), "kit-task", "kit-run");
497
518
  if (typeof store.list !== "function")
498
519
  return;
499
- const [a, b] = [freshId(), freshId()];
520
+ const a = freshId();
500
521
  await store.acquire(a);
501
- await tick();
502
- await store.acquire(b);
503
- await tick();
504
522
  await store.noteTaskRun(a, "task-1", "run-1");
505
523
  let rows = await store.list();
506
524
  assert.equal(rowOf(rows, a)?.lastTaskId, "task-1");
507
525
  assert.equal(rowOf(rows, a)?.lastTaskRunId, "run-1");
508
- assertActivityOrder(rows, [a, b], "a run is activity: the session that just ran is the most recently active");
509
526
  await store.noteTaskRun(a, "task-2");
510
527
  rows = await store.list();
511
528
  assert.equal(rowOf(rows, a)?.lastTaskId, "task-2");
512
529
  assert.equal(rowOf(rows, a)?.lastTaskRunId, undefined, "the two seats describe one run: a newer task with no run id must not keep the older run's id");
513
530
  });
531
+ faceRun("[list] the list reads the rows, not a cache: sessions are listed after their cached view is dropped and on a store reopened over the same rows (a restart), newest first", ["list"], async (store, open) => {
532
+ const [a, b] = [freshId(), freshId()];
533
+ await append((await store.acquire(a)).session, userEntry("e1", null));
534
+ await tick();
535
+ await append((await store.acquire(b)).session, userEntry("e1", null));
536
+ if (typeof store.forget === "function") {
537
+ await store.forget(a);
538
+ assert.ok(rowOf(await store.list({ limit: SESSION_LIST_LIMITS.max }), a) !== undefined, "a session whose cached view was dropped is still a row");
539
+ }
540
+ const second = await open(hooks.reopen(store));
541
+ const rows = await second.list({ limit: SESSION_LIST_LIMITS.max });
542
+ assertNewestFirst(rows);
543
+ assertActivityOrder(rows, [b, a], "a restart keeps the activity order the rows recorded");
544
+ });
545
+ faceRun("[list] a touch is recorded on the row: the touched session is still the most recent on a store reopened over the same rows (a restart)", ["list"], async (store, open) => {
546
+ const [a, b] = [freshId(), freshId()];
547
+ await append((await store.acquire(a)).session, userEntry("e1", null));
548
+ await tick();
549
+ await append((await store.acquire(b)).session, userEntry("e1", null));
550
+ await tick();
551
+ await store.touch(a);
552
+ const second = await open(hooks.reopen(store));
553
+ assertActivityOrder(await second.list({ limit: SESSION_LIST_LIMITS.max }), [a, b], "the touch outlives the process that made it");
554
+ });
555
+ faceRun("[list] a cached view of an occupant the listed row does not name lends it nothing: after another instance deleted the row and registered the id to another owner, the row lists without the old occupant's run", ["list", "noteTaskRun", "ownership", "replica"], async (store, open) => {
556
+ const id = freshId();
557
+ const other = await open(hooks.replica(store));
558
+ await store.register(id, "kit-alice");
559
+ await store.acquire(id);
560
+ await store.noteTaskRun(id, "alice-task", "alice-run");
561
+ await other.acquire(id);
562
+ await hooks.deleteSession(other, id);
563
+ await other.register(id, "kit-bob");
564
+ const row = rowOf(await store.list({ owner: "kit-bob", limit: SESSION_LIST_LIMITS.max }), id);
565
+ assert.ok(row !== undefined, "the new owner's row is listed");
566
+ assert.equal(row.lastTaskId, undefined, "the previous occupant's run is not lent to the new owner's row");
567
+ assert.equal(row.lastTaskRunId, undefined);
568
+ });
569
+ faceRun("[list] paging survives a dropped cache: a session whose cached view is dropped between two pages is not read again", ["list", "ownership", "forget"], async (store) => {
570
+ const owner = `kit-forget-${uuidv7().slice(-12)}`;
571
+ const ids = [freshId(), freshId(), freshId()];
572
+ for (const id of ids) {
573
+ await store.register(id, owner);
574
+ await tick();
575
+ }
576
+ for (const id of ids)
577
+ await store.acquire(id);
578
+ const first = await store.list({ owner, limit: 1 });
579
+ assert.equal(first.length, 1);
580
+ const top = first[0];
581
+ await store.forget(top.sessionId);
582
+ const rest = await store.list({ owner, after: { lastActiveAt: top.lastActiveAt, sessionId: top.sessionId } });
583
+ assert.ok(!rest.some((r) => r.sessionId === top.sessionId), "the first page's row is not on the next page");
584
+ assert.equal(rest.length, ids.length - 1, "and every other row is");
585
+ });
586
+ faceRun("[list] owner filters by the row's owner cell: an owner's rows only; with includeUnowned the unowned rows too; no owner = every owner's rows", ["list", "ownership"], async (store) => {
587
+ const [alice, bob] = [`kit-alice-${uuidv7().slice(-12)}`, `kit-bob-${uuidv7().slice(-12)}`];
588
+ const [a1, a2, b1, u1] = [freshId(), freshId(), freshId(), freshId()];
589
+ await store.register(a1, alice);
590
+ await store.register(a2, alice);
591
+ await store.register(b1, bob);
592
+ await store.acquire(u1);
593
+ const ids = (rows) => new Set(rows.map((r) => r.sessionId));
594
+ const own = ids(await store.list({ owner: alice, limit: SESSION_LIST_LIMITS.max }));
595
+ assert.ok(own.has(a1) && own.has(a2), "the owner's rows are listed");
596
+ assert.ok(!own.has(b1) && !own.has(u1), "another owner's row and an unowned row are not");
597
+ const withUnowned = ids(await store.list({ owner: alice, includeUnowned: true, limit: SESSION_LIST_LIMITS.max }));
598
+ assert.ok(withUnowned.has(a1) && withUnowned.has(a2) && withUnowned.has(u1), "includeUnowned adds the unowned rows");
599
+ assert.ok(!withUnowned.has(b1), "and never another owner's");
600
+ const all = ids(await store.list({ limit: SESSION_LIST_LIMITS.max }));
601
+ for (const id of [a1, a2, b1, u1])
602
+ assert.ok(all.has(id), `no owner filter lists every owner's rows (${id})`);
603
+ });
604
+ faceRun("[list] keyset paging reads each row once: pages cut with limit and the after cursor, concatenated, are the one-page list — ties ordered by session id, descending", ["list", "ownership"], async (store) => {
605
+ const owner = `kit-pager-${uuidv7().slice(-12)}`;
606
+ const ids = [freshId(), freshId(), freshId(), freshId(), freshId()];
607
+ for (const id of ids)
608
+ await store.register(id, owner);
609
+ const whole = await store.list({ owner, limit: SESSION_LIST_LIMITS.max });
610
+ assert.deepEqual(new Set(whole.map((r) => r.sessionId)), new Set(ids), "the owner's five rows");
611
+ for (let i = 1; i < whole.length; i++) {
612
+ const [prev, row] = [whole[i - 1], whole[i]];
613
+ assert.ok(prev.lastActiveAt > row.lastActiveAt || (prev.lastActiveAt === row.lastActiveAt && prev.sessionId > row.sessionId), `row ${i} follows row ${i - 1} in (lastActiveAt, sessionId) descending order`);
614
+ }
615
+ const paged = [];
616
+ let after;
617
+ for (let pages = 0; pages < 10; pages++) {
618
+ const page = await store.list({ owner, limit: 2, ...(after !== undefined ? { after } : {}) });
619
+ assert.ok(page.length <= 2, "a page holds at most limit rows");
620
+ if (page.length === 0)
621
+ break;
622
+ paged.push(...page.map((r) => r.sessionId));
623
+ const last = page[page.length - 1];
624
+ after = { lastActiveAt: last.lastActiveAt, sessionId: last.sessionId };
625
+ }
626
+ assert.deepEqual(paged, whole.map((r) => r.sessionId), "the pages, in order, are the one-page list: no row twice, none skipped");
627
+ });
628
+ faceRun("[list] a query without limit reads the default page; an explicit limit reads that many", ["list", "ownership"], async (store) => {
629
+ const owner = `kit-page-${uuidv7().slice(-12)}`;
630
+ const total = SESSION_LIST_LIMITS.default + 1;
631
+ for (let i = 0; i < total; i++)
632
+ await store.register(freshId(), owner);
633
+ assert.equal((await store.list({ owner })).length, SESSION_LIST_LIMITS.default, `no limit = the first ${SESSION_LIST_LIMITS.default} rows`);
634
+ assert.equal((await store.list({ owner, limit: total })).length, total);
635
+ assert.equal((await store.list({ owner, limit: 1 })).length, 1);
636
+ });
637
+ faceRun("[list] a bad query is refused config.invalid_argument, never read as a default: a limit out of 1..max or not an integer, a member of the wrong type, includeUnowned without owner, a malformed cursor, a member the store does not read", ["list"], async (store) => {
638
+ const bad = [
639
+ ["limit 0", { limit: 0 }],
640
+ ["a negative limit", { limit: -1 }],
641
+ ["a fractional limit", { limit: 1.5 }],
642
+ ["a limit past the bound", { limit: SESSION_LIST_LIMITS.max + 1 }],
643
+ ["a limit spelled as a string", { limit: "10" }],
644
+ ["a NaN limit", { limit: Number.NaN }],
645
+ ["an owner that is not a string", { owner: 7 }],
646
+ ["includeUnowned that is not a boolean", { owner: "kit-x", includeUnowned: "yes" }],
647
+ ["includeUnowned without owner", { includeUnowned: true }],
648
+ ["a cursor without a session id", { after: { lastActiveAt: 1 } }],
649
+ ["a cursor whose time is not a finite number", { after: { lastActiveAt: Number.POSITIVE_INFINITY, sessionId: "x" } }],
650
+ ["a cursor carrying another member", { after: { lastActiveAt: 1, sessionId: "x", page: 2 } }],
651
+ ["a member the store does not read", { kitUnreadFilter: "x" }],
652
+ ["a query that is not an object", 7],
653
+ ];
654
+ for (const [what, query] of bad) {
655
+ await rejectsWithCode(() => Reflect.apply(store.list, store, [query]), "config.invalid_argument", `list(${what})`);
656
+ }
657
+ });
514
658
  faceRun("[placements] a placed session stays off the host list — before and after a restart, also when read back with require-existing", ["placements"], async (store, open) => {
515
659
  const placed = freshId();
516
660
  const plain = freshId();
@@ -620,64 +764,230 @@ async function runSessionStoreContract(make, hooks) {
620
764
  assert.ok(scoped.includes(complete) && scoped.includes(incomplete) && !scoped.includes(elsewhere), "scope narrows to one tenant");
621
765
  assert.ok(!(await store.listPlaced("subagent", { olderThanMs: 60 * 60 * 1000 })).some((r) => r.sessionId === complete), "a session placed just now is not an hour old");
622
766
  });
623
- faceRun("[ownership] ownerOf answers three states that never collapse: an id never registered ⇒ undefined, anonymous ⇒ null, owned ⇒ the owner", ["ownership"], async (store) => {
624
- const [unknown, owned, anonymous] = [freshId(), freshId(), freshId()];
625
- assert.equal(await store.ownerOf(unknown), undefined, "no ownership row ⇒ undefined, never null");
767
+ faceRun("[head] headOf answers undefined for an id with no row; for a row, its owner, its occupant and its active leaf — the leaf follows appends and a branch switch", ["headOf"], async (store) => {
768
+ const id = freshId();
769
+ assert.equal(await headOf(store, id), undefined, "no row ⇒ undefined");
770
+ const { session } = await store.acquire(id);
771
+ assert.deepEqual(await headOf(store, id), { owner: null, incarnation: (await session.getMetadata()).incarnation, leafId: null }, "a row acquire created: no owner, its occupant, no leaf");
772
+ await append(session, userEntry("e1", null));
773
+ await append(session, assistantEntry("e2", "e1"));
774
+ assert.equal((await headOf(store, id))?.leafId, "e2", "an append moves the head's leaf");
775
+ await session.getStorage().setLeafId("e1");
776
+ assert.equal((await headOf(store, id))?.leafId, "e1", "a branch switch moves it back");
777
+ await session.getStorage().setLeafId(null);
778
+ assert.equal((await headOf(store, id))?.leafId, null);
779
+ });
780
+ faceRun("[head] the head lives in the row: a store reopened over the same rows (a restart) answers the same owner, occupant and leaf", ["headOf"], async (store, open) => {
781
+ const id = freshId();
782
+ const { session } = await store.acquire(id);
783
+ await append(session, userEntry("e1", null));
784
+ await append(session, assistantEntry("e2", "e1"));
785
+ await session.getStorage().setLeafId("e1");
786
+ const before = await headOf(store, id);
787
+ const second = await open(hooks.reopen(store));
788
+ assert.deepEqual(await headOf(second, id), before, "a head an instance kept for itself would answer differently after every restart");
789
+ });
790
+ faceRun("[head] one read, one generation: while an id is deleted and created again under other owners, no head pairs one generation's owner with another's occupant", ["ownership"], async (store) => {
791
+ const id = freshId();
792
+ const generations = [];
793
+ const reads = [];
794
+ const probe = () => {
795
+ for (let k = 0; k < 3; k++)
796
+ reads.push(headOf(store, id));
797
+ };
798
+ for (let i = 0; i < 12; i++) {
799
+ if (i > 0) {
800
+ probe();
801
+ await hooks.deleteSession(store, id);
802
+ }
803
+ probe();
804
+ await store.register(id, `kit-gen-${i}`);
805
+ probe();
806
+ await store.acquire(id);
807
+ generations.push((await headOf(store, id)));
808
+ }
809
+ const whole = new Set(generations.map((g) => `${g.owner}|${g.incarnation}`));
810
+ assert.equal(whole.size, generations.length, "each generation is its own owner and occupant");
811
+ for (const head of await Promise.all(reads)) {
812
+ if (head === undefined)
813
+ continue;
814
+ assert.ok(whole.has(`${head.owner}|${head.incarnation}`), `a head read ${head.owner} with occupant ${head.incarnation}, a pair no generation of the row ever held`);
815
+ }
816
+ });
817
+ faceRun("[ownership] register on an id with no row creates its row recording the owner — a string, or null for anonymous; a row acquire created records null", ["ownership"], async (store) => {
818
+ const [owned, anonymous, acquired] = [freshId(), freshId(), freshId()];
626
819
  await store.register(owned, "kit-alice");
627
820
  await store.register(anonymous, null);
628
- assert.equal(await store.ownerOf(owned), "kit-alice");
629
- assert.equal(await store.ownerOf(anonymous), null, "an anonymous row ⇒ null, never undefined");
630
- });
631
- faceRun("[ownership] register is first-write-wins: later calls neither overwrite nor throw, whatever they carry", ["ownership"], async (store) => {
632
- const [owned, anonymous] = [freshId(), freshId()];
821
+ await store.acquire(acquired);
822
+ assert.equal(await ownerOf(store, owned, "a registered id"), "kit-alice");
823
+ assert.equal(await ownerOf(store, anonymous, "an anonymous registration"), null);
824
+ assert.equal(await ownerOf(store, acquired, "a row acquire created"), null, "acquire names no owner");
825
+ const token = await tokenOf(store, owned, "a registered row");
826
+ assert.equal((await (await readBack(store, owned)).getMetadata()).incarnation, token, "the registered row is the session acquire then hands out");
827
+ });
828
+ faceRun("[ownership] register on a row that exists does nothing, whatever it carries — an owned row, an anonymous row, a row acquire created, a placed row", ["ownership"], async (store) => {
829
+ const [owned, anonymous, acquired] = [freshId(), freshId(), freshId()];
633
830
  await store.register(owned, "kit-alice");
634
831
  await store.register(owned, "kit-bob");
635
832
  await store.register(owned, null);
636
833
  await registerUntyped(store, owned, undefined);
637
- assert.equal(await store.ownerOf(owned), "kit-alice");
834
+ assert.equal(await ownerOf(store, owned, "an owned row"), "kit-alice");
638
835
  await store.register(anonymous, null);
639
836
  await store.register(anonymous, "kit-bob");
640
- assert.equal(await store.ownerOf(anonymous), null, "an anonymous row is not claimable by a later register");
837
+ assert.equal(await ownerOf(store, anonymous, "an anonymous row"), null, "an anonymous row is not claimable by a later register");
838
+ await store.acquire(acquired);
839
+ await store.register(acquired, "kit-carol");
840
+ assert.equal(await ownerOf(store, acquired, "a row acquire created"), null, "there is no claiming of a row that exists");
841
+ if (declaresPlacements(store)) {
842
+ for (const owner of [`kit-claimant-${freshId()}`, null]) {
843
+ const placed = freshId();
844
+ await store.acquire(placed, { placement: placementFor() });
845
+ const recorded = await ownerOf(store, placed, "a placed row");
846
+ await store.register(placed, owner);
847
+ assert.equal(await ownerOf(store, placed, "a placed row after a register"), recorded, `a register carrying ${owner === null ? "null" : "an owner"} left the placed row's owner as its placing write recorded it`);
848
+ }
849
+ const sequenced = freshId();
850
+ await store.acquire(sequenced, { placement: placementFor() });
851
+ const recordedSeq = await ownerOf(store, sequenced, "a placed row (sequence)");
852
+ await store.register(sequenced, null);
853
+ await store.register(sequenced, `kit-claimant-${freshId()}`);
854
+ assert.equal(await ownerOf(store, sequenced, "a placed row after two registers"), recordedSeq, "two registers in sequence (null, then a name) left the placed row's owner as its placing write recorded it");
855
+ }
641
856
  });
642
- faceRun("[ownership] a FIRST register whose owner is neither a string nor null is refused loudly and leaves no row behind", ["ownership"], async (store) => {
857
+ faceRun("[ownership] a FIRST register whose owner is neither a principal name nor null is refused loudly and leaves no row behind — a blank string, or one holding a control or invisible character, names nobody", ["ownership"], async (store) => {
643
858
  const id = freshId();
644
859
  await thrownBy(() => registerUntyped(store, id, undefined), "a first register with owner undefined");
645
860
  await thrownBy(() => registerUntyped(store, id, 42), "a first register with a numeric owner");
646
- assert.equal(await store.ownerOf(id), undefined, "the refused writes stored nothing");
861
+ await thrownBy(() => registerUntyped(store, id, ""), "a first register with the empty string as owner");
862
+ await thrownBy(() => registerUntyped(store, id, " \t "), "a first register with a blank string as owner");
863
+ await thrownBy(() => registerUntyped(store, id, "ali\u0000ce"), "a first register with a control character inside the owner");
864
+ await thrownBy(() => registerUntyped(store, id, "\u200b"), "a first register with an invisible character as owner");
865
+ assert.equal(await headOf(store, id), undefined, "the refused writes stored nothing");
647
866
  await store.register(id, "kit-carol");
648
- assert.equal(await store.ownerOf(id), "kit-carol", "the id is still ownable — no row that reads as 'no row' was minted");
867
+ assert.equal(await ownerOf(store, id, "the first well-formed register"), "kit-carol", "the id is still ownable");
649
868
  });
650
869
  faceRun("[ownership] two concurrent first registers: each caller's own re-read sees the same winner, one of the two", ["ownership"], async (store) => {
651
870
  const id = freshId();
652
871
  const caller = async (owner) => {
653
872
  await store.register(id, owner);
654
- return store.ownerOf(id);
873
+ return ownerOf(store, id, "after a register");
655
874
  };
656
875
  const [x, y] = await Promise.all([caller("kit-x"), caller("kit-y")]);
657
876
  assert.equal(x, y, "the loser must see the winner on its own re-read, or both callers attach to one session");
658
877
  assert.ok(x === "kit-x" || x === "kit-y");
659
- assert.equal(await store.ownerOf(id), x, "and the row keeps that winner");
878
+ assert.equal(await ownerOf(store, id, "the settled row"), x, "and the row keeps that winner");
660
879
  });
661
- faceRun("[ownership] the ownership row outlives the cached view: a registered session that is forgotten still reads its owner", ["ownership", "forget"], async (store) => {
662
- const id = freshId();
663
- await store.register(id, "kit-alice");
664
- await append((await store.acquire(id)).session, userEntry("e1", null));
665
- await store.forget(id);
666
- assert.equal(await store.ownerOf(id), "kit-alice", "a live session reading undefined would be re-claimable by anyone");
880
+ faceRun("[ownership] the owner is a cell of the row: it reads back after the cached view is dropped and on a store reopened over the same rows (a restart)", ["ownership"], async (store, open) => {
881
+ const [owned, acquired] = [freshId(), freshId()];
882
+ await store.register(owned, "kit-alice");
883
+ await append((await store.acquire(owned)).session, userEntry("e1", null));
884
+ await store.acquire(acquired);
885
+ if (typeof store.forget === "function") {
886
+ await store.forget(owned);
887
+ assert.equal(await ownerOf(store, owned, "a forgotten session"), "kit-alice", "a live session must never lose its owner with the cached view");
888
+ }
889
+ const second = await open(hooks.reopen(store));
890
+ assert.equal(await ownerOf(second, owned, "after a restart"), "kit-alice", "an owner an instance kept for itself is lost on every restart, and the session is then anyone's");
891
+ assert.equal(await ownerOf(second, acquired, "after a restart"), null);
892
+ await second.register(owned, "kit-bob");
893
+ assert.equal(await ownerOf(second, owned, "a register after a restart"), "kit-alice", "a restart opens no claiming window");
667
894
  });
668
- faceRun("[ownership] a fork's ownership agrees with its source: no owner or null ⇒ the source's (an owned copy is never downgraded), an explicit owner ⇒ that owner, an anonymous source ⇒ anonymous", ["ownership", "fork"], async (store) => {
895
+ faceRun("[ownership] a fork records its owner in its own row: no owner or null ⇒ the source's (an owned copy is never downgraded), an explicit owner ⇒ that owner, an anonymous source ⇒ anonymous", ["ownership", "fork"], async (store) => {
669
896
  const owned = freshId();
670
897
  await store.register(owned, "kit-alice");
671
898
  await append((await store.acquire(owned)).session, userEntry("e1", null));
672
- assert.equal(await store.ownerOf((await store.fork(owned))), "kit-alice", "no owner argument ⇒ the source's owner");
673
- assert.equal(await store.ownerOf((await store.fork(owned, null))), "kit-alice", "an explicit null never downgrades a copy of an owned history to anonymous");
674
- assert.equal(await store.ownerOf((await store.fork(owned, "kit-bob"))), "kit-bob", "an explicit owner scopes the fork");
899
+ assert.equal(await ownerOf(store, (await store.fork(owned)), "a fork"), "kit-alice", "no owner argument ⇒ the source's owner");
900
+ assert.equal(await ownerOf(store, (await store.fork(owned, null)), "a fork"), "kit-alice", "an explicit null never downgrades a copy of an owned history to anonymous");
901
+ assert.equal(await ownerOf(store, (await store.fork(owned, "kit-bob")), "a fork"), "kit-bob", "an explicit owner scopes the fork");
675
902
  const anonymous = freshId();
676
- await store.register(anonymous, null);
677
903
  await append((await store.acquire(anonymous)).session, userEntry("e1", null));
678
- assert.equal(await store.ownerOf((await store.fork(anonymous))), null);
679
- assert.equal(await store.ownerOf((await store.fork(anonymous, null))), null);
904
+ assert.equal(await ownerOf(store, (await store.fork(anonymous)), "a fork"), null);
905
+ assert.equal(await ownerOf(store, (await store.fork(anonymous, null)), "a fork"), null);
906
+ });
907
+ faceRun("[ownership] an id registered first is an ordinary row: a placement-carrying acquire of it is refused placement_refused", ["ownership", "placements"], async (store) => {
908
+ const id = freshId();
909
+ await store.register(id, "kit-alice");
910
+ await rejectsWithCode(() => store.acquire(id, { placement: placementFor() }), "placement_refused", "a placed creation onto an id a register already created");
911
+ assert.equal(await ownerOf(store, id, "the registered row"), "kit-alice", "the refused acquire rewrote nothing");
912
+ });
913
+ faceRun("[ownership] acquire serves only the occupant the row holds: after another instance deleted the row and registered the id to another owner, this instance's next acquire serves the new occupant, never its cached view of the old one", ["ownership", "replica"], async (store, open) => {
914
+ const id = freshId();
915
+ const other = await open(hooks.replica(store));
916
+ await store.register(id, "kit-alice");
917
+ await append((await store.acquire(id)).session, userEntry("alice-only", null));
918
+ await other.acquire(id);
919
+ await hooks.deleteSession(other, id);
920
+ await other.register(id, "kit-bob");
921
+ const head = await headOf(store, id);
922
+ assert.equal(head?.owner, "kit-bob", "this instance's head reads the row");
923
+ const served = (await store.acquire(id)).session;
924
+ assert.equal((await served.getMetadata()).incarnation, head?.incarnation, "the session handed out is the row the owner was read from");
925
+ assert.deepEqual(await entryIds(served), [], "the previous occupant's transcript is not served under the new owner");
926
+ });
927
+ faceRun("[ownership] the metadata a session hands out is not the row: writing to it changes no owner or occupant the head answers", ["ownership"], async (store) => {
928
+ const id = freshId();
929
+ await store.register(id, "kit-alice");
930
+ const before = await headOf(store, id);
931
+ try {
932
+ Object.assign(await (await store.acquire(id)).session.getMetadata(), { owner: null, incarnation: "kit-forged" });
933
+ }
934
+ catch {
935
+ }
936
+ assert.deepEqual(await headOf(store, id), before, "admission changes only through a write of the row");
937
+ });
938
+ faceRun("[incarnation] a session created through acquire carries a token, and every later read of the row answers the same one", ["headOf", "requireExisting"], async (store) => {
939
+ const id = freshId();
940
+ await store.acquire(id);
941
+ const first = await tokenOf(store, id, "after acquire");
942
+ await store.acquire(id);
943
+ assert.equal(await tokenOf(store, id, "a re-acquire"), first, "a re-acquire reopens the same occupant");
944
+ await store.forget?.(id);
945
+ assert.equal(await tokenOf(store, id, "after forget"), first, "the token is the row's, not the cache's: forgetting the cached view must not change it");
946
+ await store.acquire(id, { requireExisting: true });
947
+ assert.equal(await tokenOf(store, id, "a require-existing reopen"), first, "a require-existing reopen after the cache forgot it is still the same occupant");
948
+ });
949
+ faceRun("[incarnation] the session acquire hands back carries the token headOf answers — a run's memory seat reads the former", ["headOf", "requireExisting"], async (store) => {
950
+ const id = freshId();
951
+ const acquired = await store.acquire(id);
952
+ const byId = await tokenOf(store, id, "by-id read");
953
+ assert.equal((await acquired.session.getMetadata()).incarnation, byId, "two faces of one row cell must agree, or the seat and the by-id read name two occupants");
954
+ });
955
+ faceRun("[incarnation] a second store over the same rows (a restart, a replica) answers the same token — the token lives in the row, not in an instance", ["headOf", "requireExisting"], async (store, open) => {
956
+ const id = freshId();
957
+ await store.acquire(id);
958
+ const first = await tokenOf(store, id, "first instance");
959
+ const second = await open(hooks.reopen(store));
960
+ assert.equal(await tokenOf(second, id, "second instance"), first, "an instance-local token would name a different occupant after every restart");
961
+ await second.acquire(id, { requireExisting: true });
962
+ assert.equal(await tokenOf(second, id, "reopened on the second instance"), first, "reopening the session on the second instance is still the same occupant");
963
+ });
964
+ faceRun("[incarnation] two sessions never share a token, and an id with no session has no head (unknown — not an error, not a token)", ["headOf", "requireExisting"], async (store) => {
965
+ const id = freshId();
966
+ await store.acquire(id);
967
+ const generated = await store.acquire();
968
+ const a = await tokenOf(store, id, "explicit id");
969
+ const g = await tokenOf(store, generated.sessionId, "generated id");
970
+ assert.notEqual(a, g, "two sessions are two occupants");
971
+ assert.equal(await headOf(store, freshId()), undefined, "no row ⇒ unknown");
972
+ });
973
+ faceRun("[incarnation] a deleted id handed out again is a NEW occupant: a fresh token, never the previous occupant's", ["headOf", "requireExisting"], async (store) => {
974
+ const id = freshId();
975
+ await store.acquire(id);
976
+ const before = await tokenOf(store, id, "first occupant");
977
+ await hooks.deleteSession(store, id);
978
+ assert.equal(await headOf(store, id), undefined, "a deleted row has no occupant");
979
+ await store.acquire(id);
980
+ const after = await tokenOf(store, id, "second occupant");
981
+ assert.notEqual(after, before, "the id was recycled — the previous occupant's records must not bind this one");
982
+ });
983
+ faceRun("[incarnation] a fork is a new occupant: a fresh token, and the source keeps its own", ["headOf", "requireExisting", "fork"], async (store) => {
984
+ const src = freshId();
985
+ await store.acquire(src);
986
+ const source = await tokenOf(store, src, "fork source");
987
+ const forkedId = await store.fork(src);
988
+ assert.ok(forkedId !== null, "forking an existing session returns the fork's id");
989
+ assert.notEqual(await tokenOf(store, forkedId, "fork"), source, "a fork copies the history, never the occupant");
990
+ assert.equal(await tokenOf(store, src, "fork source after"), source, "forking does not touch the source's occupant");
680
991
  });
681
- registerSessionIncarnationCases((name, fn) => faceRun(`[incarnation] ${name}`, ["incarnationOf", "requireExisting"], (store) => fn(store)), hooks);
682
992
  await settle();
683
993
  }
@@ -1,5 +1,5 @@
1
1
  import { strict as assert } from "node:assert";
2
- import { JOURNAL_OVERSIZE_ERROR_CODE, MAX_JOURNAL_RESULT_BYTES, } from "../workflow-journal-store.js";
2
+ import { JOURNAL_INCOMPATIBLE_ERROR_CODE, JOURNAL_OVERSIZE_ERROR_CODE, MAX_JOURNAL_RESULT_BYTES, } from "../workflow-journal-store.js";
3
3
  import { beginContract } from "./contract-harness.js";
4
4
  const key = (ordinal) => `${ordinal}:h`;
5
5
  const completed = (ordinal, over = {}) => ({
@@ -22,6 +22,7 @@ const parked = (ordinal, over = {}) => ({
22
22
  });
23
23
  const OVER_CAP = "x".repeat(MAX_JOURNAL_RESULT_BYTES + 1024);
24
24
  const isOversizeRefusal = (e) => e instanceof Error && e.code === JOURNAL_OVERSIZE_ERROR_CODE;
25
+ const isIncompatibleRefusal = (e) => e instanceof Error && e.code === JOURNAL_INCOMPATIBLE_ERROR_CODE;
25
26
  export async function workflowJournalStoreContract(make, runAssertion) {
26
27
  const { run, settle } = beginContract(runAssertion);
27
28
  run("append → load returns entries ASCENDING by ordinal, both arms verbatim; a re-append per ordinal OVERWRITES", async () => {
@@ -64,6 +65,30 @@ export async function workflowJournalStoreContract(make, runAssertion) {
64
65
  await store.append("run-1", "tenant-a", { callKey: key(1), parked: parked(1) });
65
66
  assert.equal((await store.load("run-1", "tenant-a"))[1].parked?.terminal.kind, "paused");
66
67
  });
68
+ run("a PARKED row whose payload is not a paused result is refused — at append, or by the whole load — never read back as an entry, never dropped", async () => {
69
+ const bad = [
70
+ ["terminal completed", { ...parked(1), terminal: { kind: "completed" } }],
71
+ ["paused terminal without a checkpoint token", { ...parked(1), terminal: { kind: "paused", gate: { kind: "human", reason: "approve", toolName: "Write" } } }],
72
+ ["parked arm a string", "paused"],
73
+ ];
74
+ for (const [what, payload] of bad) {
75
+ const store = make();
76
+ await store.append("run-1", "tenant-a", { callKey: key(0), result: completed(0) });
77
+ let appended = true;
78
+ try {
79
+ await store.append("run-1", "tenant-a", { callKey: key(1), parked: payload });
80
+ }
81
+ catch {
82
+ appended = false;
83
+ }
84
+ if (appended) {
85
+ await assert.rejects(store.load("run-1", "tenant-a"), isIncompatibleRefusal, `${what}: the row was journaled, so the load must refuse — with code "${JOURNAL_INCOMPATIBLE_ERROR_CODE}" — rather than answer around it`);
86
+ }
87
+ else {
88
+ assert.deepEqual((await store.load("run-1", "tenant-a")).map((e) => e.callKey), [key(0)], `${what}: an append that refused must have journaled nothing`);
89
+ }
90
+ }
91
+ });
67
92
  run("the cap is inclusive: a payload of EXACTLY the cap's byte count is accepted on both arms", async () => {
68
93
  const store = make();
69
94
  const fit = (payload) => {
@@ -14,6 +14,8 @@ export declare class StubExecutionEnv implements ExecutionEnv {
14
14
  readonly externalContentTarget?: boolean;
15
15
  /** `ExecutionEnv.cdPath`: the stub runs no shell, so no shell receives a `CDPATH`. */
16
16
  readonly cdPath: readonly string[];
17
+ /** `ExecutionEnv.withheldEnv`: no shell child, so nothing is withheld from one. */
18
+ readonly withheldEnv: readonly [];
17
19
  constructor(cwd?: string);
18
20
  private fsErr;
19
21
  absolutePath(path: string): Promise<Result<string, FileError>>;
@@ -3,6 +3,7 @@ export class StubExecutionEnv {
3
3
  cwd;
4
4
  externalContentTarget;
5
5
  cdPath = [];
6
+ withheldEnv = [];
6
7
  constructor(cwd = "/") {
7
8
  this.cwd = cwd;
8
9
  }