@code-yeongyu/senpi 2026.10.25-4 → 2026.10.26-2

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 (260) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/bundle/chunks/{anthropic-messages-WBASBTIW.js → anthropic-messages-ODSFJCXR.js} +3 -3
  3. package/dist/bundle/chunks/{args-B444OQBK.js → args-SWKJAYQD.js} +1 -1
  4. package/dist/bundle/chunks/azure-openai-responses-REGXS4XO.js +2 -0
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +3 -3
  6. package/dist/bundle/chunks/chunk-2XF6W6F6.js +410 -0
  7. package/dist/bundle/chunks/{chunk-WDPQ6CZJ.js → chunk-4QVORFQ2.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-6AOVUTUR.js → chunk-5W3DTM2J.js} +2 -2
  9. package/dist/bundle/chunks/{chunk-E6WYH3ZL.js → chunk-6GAMRAVM.js} +9 -7
  10. package/dist/bundle/chunks/{chunk-N7IN46Q5.js → chunk-7IZH2KQX.js} +9 -7
  11. package/dist/bundle/chunks/{chunk-7GZUFOM3.js → chunk-ARSENVDY.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-STMXIBXF.js → chunk-CNDPCYNT.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-XF3LJC3O.js → chunk-CYOHFREV.js} +1 -1
  14. package/dist/bundle/chunks/{chunk-E6D4RIT7.js → chunk-DNDZPKIQ.js} +2 -2
  15. package/dist/bundle/chunks/{chunk-E4LBKOZG.js → chunk-EQNUL2WB.js} +4 -4
  16. package/dist/bundle/chunks/{chunk-2RB5EL4X.js → chunk-ESNSRUW4.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-4AOMNOGY.js → chunk-FQDARFHT.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-BWA4FVFW.js → chunk-GPPOPK76.js} +3 -3
  19. package/dist/bundle/chunks/{chunk-SBGIXMX3.js → chunk-GWGNVYPB.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-KKD4CYCI.js → chunk-H4WJ4LY3.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-V2UYXTRR.js → chunk-HWTD4QF3.js} +1 -1
  22. package/dist/bundle/chunks/chunk-I2WFLKZ2.js +5 -0
  23. package/dist/bundle/chunks/{chunk-WD6WBYC5.js → chunk-IHKHCWWG.js} +1 -1
  24. package/dist/bundle/chunks/{chunk-T36LJ6MW.js → chunk-IY4G44MN.js} +1 -1
  25. package/dist/bundle/chunks/{chunk-BZ7726QO.js → chunk-J2YAYTB2.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-UFJ2K7Q7.js → chunk-JH7MVRNJ.js} +1 -1
  27. package/dist/bundle/chunks/chunk-KBSFTXAT.js +2 -0
  28. package/dist/bundle/chunks/{chunk-OLCDIITS.js → chunk-KRO63T4Q.js} +1 -1
  29. package/dist/bundle/chunks/{chunk-TTU2RNVX.js → chunk-KTDIIF2F.js} +1 -1
  30. package/dist/bundle/chunks/{chunk-KNVNKDER.js → chunk-L2O6K3SJ.js} +1 -1
  31. package/dist/bundle/chunks/{chunk-3BTN337S.js → chunk-NDNIMC5W.js} +1 -1
  32. package/dist/bundle/chunks/{chunk-ITGYN22E.js → chunk-ODRGPC5S.js} +1 -1
  33. package/dist/bundle/chunks/{chunk-4MOZXOJX.js → chunk-PDG4XWO2.js} +1 -1
  34. package/dist/bundle/chunks/{chunk-3QCYBOKN.js → chunk-PPECSSWX.js} +1 -1
  35. package/dist/bundle/chunks/{chunk-4P7WARQZ.js → chunk-PY4ZQHTN.js} +1 -1
  36. package/dist/bundle/chunks/{chunk-C2MQ4QXN.js → chunk-RPW3XQW3.js} +48 -42
  37. package/dist/bundle/chunks/{chunk-JMOUU75H.js → chunk-RQ5JTXUC.js} +1 -1
  38. package/dist/bundle/chunks/chunk-TQ67PB7U.js +13 -0
  39. package/dist/bundle/chunks/{chunk-5JJDR6XI.js → chunk-UJPG4ROT.js} +1 -1
  40. package/dist/bundle/chunks/{chunk-FHXR4ZAC.js → chunk-XM36IFP3.js} +1 -1
  41. package/dist/bundle/chunks/{chunk-PT6JJUN2.js → chunk-XMNPK472.js} +1 -1
  42. package/dist/bundle/chunks/{client-WTYEICLH.js → chunk-Y3TMJV3J.js} +3 -3
  43. package/dist/bundle/chunks/{chunk-GSSD3R5X.js → chunk-Z6BREXV3.js} +1 -1
  44. package/dist/bundle/chunks/{chunk-KXB5WIS5.js → chunk-Z6SAQ5O3.js} +1 -1
  45. package/dist/bundle/chunks/{chunk-LOIF6PHI.js → chunk-ZAUVY6ZC.js} +3 -3
  46. package/dist/bundle/chunks/{chunk-7CNOCJCN.js → chunk-ZP6JJDYV.js} +1 -1
  47. package/dist/bundle/chunks/cli-main-Q2GA53XM.js +3 -0
  48. package/dist/bundle/chunks/client-LOS6GSEW.js +2 -0
  49. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-K5H6BJP3.js → cloudflare-workers-ai-system-one-WDHT7BBV.js} +1 -1
  50. package/dist/bundle/chunks/cursor-agent.js +3 -3
  51. package/dist/bundle/chunks/devin-agent.js +3 -3
  52. package/dist/bundle/chunks/dispatch-5R3TYAYB.js +9 -0
  53. package/dist/bundle/chunks/google-generative-ai-FHYFXKLA.js +2 -0
  54. package/dist/bundle/chunks/google-vertex-5DWE5U5K.js +2 -0
  55. package/dist/bundle/chunks/{help-fast-path-3Z26DQSV.js → help-fast-path-FETLIF3W.js} +1 -1
  56. package/dist/bundle/chunks/host-command-XEB7SHT3.js +2 -0
  57. package/dist/bundle/chunks/{host-lifecycle-3VHHIGDL.js → host-lifecycle-HX3EGRRW.js} +1 -1
  58. package/dist/bundle/chunks/host-lifecycle.js +2 -2
  59. package/dist/bundle/chunks/{host-session-command-56XF6ACS.js → host-session-command-FFN52LXJ.js} +1 -1
  60. package/dist/bundle/chunks/{host-socket-request-QL2WEZTN.js → host-socket-request-K4LCUOUD.js} +1 -1
  61. package/dist/bundle/chunks/interactive-mode-5DEKY3YW.js +2 -0
  62. package/dist/bundle/chunks/{mcp-presenter-bootstrap-XUSCQVRC.js → mcp-presenter-bootstrap-FOMKF4TU.js} +1 -1
  63. package/dist/bundle/chunks/{mistral-conversations-DH44NOYV.js → mistral-conversations-I2PI7DDF.js} +1 -1
  64. package/dist/bundle/chunks/{multi-session-host-Z2CW4SDM.js → multi-session-host-OPR5VBFO.js} +6 -7
  65. package/dist/bundle/chunks/{openai-codex-responses-SERAFKAS.js → openai-codex-responses-WSEX6SH7.js} +1 -1
  66. package/dist/bundle/chunks/{openai-completions-BZB5O45A.js → openai-completions-YCBHTBSS.js} +1 -1
  67. package/dist/bundle/chunks/openai-responses-GXNSQF4A.js +3 -0
  68. package/dist/bundle/chunks/{package-manager-cli-N55U6PEO.js → package-manager-cli-W55E3LHP.js} +1 -1
  69. package/dist/bundle/chunks/{pi-messages-VXATX45B.js → pi-messages-O6GISIRW.js} +2 -2
  70. package/dist/bundle/chunks/{role-JVSBNKGE.js → role-SHIZQKK4.js} +1 -1
  71. package/dist/bundle/chunks/{rotation-stream-CBGCBYHV.js → rotation-stream-2PCZWNKF.js} +1 -1
  72. package/dist/bundle/chunks/rpc-mode-IDUJTSYS.js +2 -0
  73. package/dist/bundle/chunks/{rpc-monitor-source-AE3C6EQV.js → rpc-monitor-source-DRZXJHTL.js} +1 -1
  74. package/dist/bundle/chunks/{schedule-command-VS7CZC2J.js → schedule-command-B7RRSANU.js} +1 -1
  75. package/dist/bundle/chunks/session-control-endpoint-LMZH5JLH.js +3 -0
  76. package/dist/bundle/chunks/{session-picker-E4KUJDLO.js → session-picker-DTGBOXDR.js} +1 -1
  77. package/dist/bundle/chunks/session-worker.js +214 -205
  78. package/dist/bundle/chunks/{typesafe-system-one-P75WJ767.js → typesafe-system-one-XGYJJVSE.js} +1 -1
  79. package/dist/bundle/cli.js +2 -2
  80. package/dist/bundle/host-supervisor.js +2 -2
  81. package/dist/bundle/index.js +1 -1
  82. package/dist/bundle/rpc-entry.js +1 -1
  83. package/dist/bundle/runtime-manifest.json +1 -1
  84. package/dist/cli/host-command-args.d.ts +1 -0
  85. package/dist/cli/host-command-args.js +5 -0
  86. package/dist/cli/host-command.js +7 -1
  87. package/dist/cli.js +21 -3
  88. package/dist/core/agent-session.d.ts +11 -1
  89. package/dist/core/agent-session.js +19 -2
  90. package/dist/core/extensions/builtin/compaction/index.js +7 -1
  91. package/dist/core/extensions/builtin/herdr/index.js +9 -5
  92. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.d.ts +6 -0
  93. package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +25 -0
  94. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +3 -3
  95. package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +50 -19
  96. package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +4 -3
  97. package/dist/core/extensions/builtin/mcp/auth/oauth.js +19 -12
  98. package/dist/core/extensions/builtin/mcp/catalog-cache.d.ts +2 -1
  99. package/dist/core/extensions/builtin/mcp/catalog-cache.js +19 -6
  100. package/dist/core/extensions/builtin/mcp/catalog.d.ts +3 -2
  101. package/dist/core/extensions/builtin/mcp/catalog.js +20 -3
  102. package/dist/core/extensions/builtin/mcp/expose/call.d.ts +2 -1
  103. package/dist/core/extensions/builtin/mcp/expose/call.js +2 -1
  104. package/dist/core/extensions/builtin/mcp/expose/register.js +1 -1
  105. package/dist/core/extensions/builtin/mcp/expose/schema-compat.d.ts +2 -2
  106. package/dist/core/extensions/builtin/mcp/expose/schema-compat.js +11 -1
  107. package/dist/core/extensions/builtin/mcp/invocation.js +16 -2
  108. package/dist/core/extensions/builtin/mcp/modern/dispatch.js +1 -0
  109. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.d.ts +4 -1
  110. package/dist/core/extensions/builtin/mcp/modern/parameter-headers.js +33 -35
  111. package/dist/core/extensions/builtin/mcp/modern/request.d.ts +1 -1
  112. package/dist/core/extensions/builtin/mcp/modern/request.js +4 -1
  113. package/dist/core/extensions/builtin/mcp/modern/sdk-bridge.js +18 -3
  114. package/dist/core/extensions/builtin/mcp/modern/stdio-probe.js +23 -0
  115. package/dist/core/extensions/builtin/mcp/modern/subscriptions.d.ts +14 -0
  116. package/dist/core/extensions/builtin/mcp/modern/subscriptions.js +226 -0
  117. package/dist/core/extensions/builtin/mcp/modern/tool-schema.d.ts +13 -0
  118. package/dist/core/extensions/builtin/mcp/modern/tool-schema.js +75 -0
  119. package/dist/core/extensions/builtin/mcp/modern/tools.d.ts +107 -0
  120. package/dist/core/extensions/builtin/mcp/modern/tools.js +53 -0
  121. package/dist/core/extensions/builtin/mcp/modern/transport.d.ts +2 -0
  122. package/dist/core/extensions/builtin/mcp/modern/transport.js +4 -2
  123. package/dist/core/extensions/builtin/mcp/resources.js +4 -0
  124. package/dist/core/extensions/builtin/mcp/tool-client.d.ts +13 -0
  125. package/dist/core/extensions/builtin/mcp/tool-client.js +17 -0
  126. package/dist/core/extensions/builtin/mcp/tool-contracts.d.ts +19 -0
  127. package/dist/core/extensions/builtin/mcp/tool-contracts.js +2 -0
  128. package/dist/core/extensions/builtin/mcp/wrap.js +2 -2
  129. package/dist/core/extensions/builtin/permission-system/bash-command-segments.d.ts +37 -0
  130. package/dist/core/extensions/builtin/permission-system/bash-command-segments.js +899 -0
  131. package/dist/core/extensions/builtin/permission-system/parsers.d.ts +2 -0
  132. package/dist/core/extensions/builtin/permission-system/parsers.js +44 -10
  133. package/dist/core/extensions/builtin/terminal/extension-state.js +2 -1
  134. package/dist/core/extensions/builtin/vertex-model-garden/auth.d.ts +9 -0
  135. package/dist/core/extensions/builtin/vertex-model-garden/auth.js +17 -1
  136. package/dist/core/extensions/builtin/websearch/websearch/providers/google.d.ts +6 -0
  137. package/dist/core/extensions/builtin/websearch/websearch/providers/google.js +6 -1
  138. package/dist/core/extensions/types.d.ts +2 -0
  139. package/dist/core/lifecycle-children.d.ts +3 -1
  140. package/dist/core/lifecycle-children.js +79 -15
  141. package/dist/core/lifecycle-record.js +1 -24
  142. package/dist/core/lifecycle-session-binding.js +2 -1
  143. package/dist/core/lifecycle-wait-items.d.ts +17 -0
  144. package/dist/core/lifecycle-wait-items.js +72 -0
  145. package/dist/core/model-runtime.js +6 -1
  146. package/dist/core/provider-api-key-auth.d.ts +6 -1
  147. package/dist/core/provider-api-key-auth.js +11 -4
  148. package/dist/core/provider-composer.d.ts +8 -1
  149. package/dist/core/provider-composer.js +7 -3
  150. package/dist/core/sdk.js +4 -1
  151. package/dist/core/session-activity.js +2 -0
  152. package/dist/core/session-lifecycle-types.d.ts +20 -4
  153. package/dist/core/session-lifecycle.js +35 -4
  154. package/dist/core/structured-session-lane.d.ts +33 -0
  155. package/dist/core/structured-session-lane.js +62 -0
  156. package/dist/core/work-custody-record.d.ts +2 -1
  157. package/dist/core/work-custody-record.js +2 -0
  158. package/dist/core/work-obligations.d.ts +1 -1
  159. package/dist/core/work-obligations.js +8 -2
  160. package/dist/modes/interactive/session-control-commands.js +21 -4
  161. package/dist/modes/interactive/session-control-rpc-commands.d.ts +1 -1
  162. package/dist/modes/interactive/session-control-rpc-commands.js +19 -0
  163. package/dist/modes/json-event.d.ts +79 -1
  164. package/dist/modes/json-event.js +79 -1
  165. package/dist/modes/rpc/compact-content-index.d.ts +85 -0
  166. package/dist/modes/rpc/compact-content-index.js +254 -0
  167. package/dist/modes/rpc/compact-content-store.d.ts +59 -0
  168. package/dist/modes/rpc/compact-content-store.js +134 -0
  169. package/dist/modes/rpc/compact-index-pages.d.ts +12 -0
  170. package/dist/modes/rpc/compact-index-pages.js +31 -0
  171. package/dist/modes/rpc/compact-session-state.d.ts +83 -0
  172. package/dist/modes/rpc/compact-session-state.js +197 -0
  173. package/dist/modes/rpc/compact-spool-dir.d.ts +8 -0
  174. package/dist/modes/rpc/compact-spool-dir.js +94 -0
  175. package/dist/modes/rpc/compact-spool-storage.d.ts +53 -0
  176. package/dist/modes/rpc/compact-spool-storage.js +162 -0
  177. package/dist/modes/rpc/compact-storage-io.d.ts +28 -0
  178. package/dist/modes/rpc/compact-storage-io.js +25 -0
  179. package/dist/modes/rpc/compact-transition.d.ts +41 -0
  180. package/dist/modes/rpc/compact-transition.js +12 -0
  181. package/dist/modes/rpc/connection-handler.d.ts +4 -0
  182. package/dist/modes/rpc/connection-handler.js +51 -13
  183. package/dist/modes/rpc/connection-question-bridge.d.ts +2 -1
  184. package/dist/modes/rpc/connection-question-bridge.js +6 -2
  185. package/dist/modes/rpc/extension-ui-response.d.ts +7 -2
  186. package/dist/modes/rpc/extension-ui-response.js +27 -7
  187. package/dist/modes/rpc/get-snapshot.d.ts +18 -0
  188. package/dist/modes/rpc/get-snapshot.js +43 -0
  189. package/dist/modes/rpc/host-capabilities.js +3 -0
  190. package/dist/modes/rpc/host-ensure-start.js +6 -3
  191. package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
  192. package/dist/modes/rpc/host-ensure.js +1 -0
  193. package/dist/modes/rpc/host-handoff.d.ts +2 -0
  194. package/dist/modes/rpc/host-handover-request.d.ts +1 -1
  195. package/dist/modes/rpc/host-handover-request.js +4 -3
  196. package/dist/modes/rpc/host-handover-wire.js +6 -0
  197. package/dist/modes/rpc/host-idle-handover.d.ts +2 -0
  198. package/dist/modes/rpc/host-idle-handover.js +3 -1
  199. package/dist/modes/rpc/host-observe-request.js +1 -0
  200. package/dist/modes/rpc/host-runner.d.ts +1 -0
  201. package/dist/modes/rpc/host-runner.js +9 -7
  202. package/dist/modes/rpc/host-successor.js +4 -0
  203. package/dist/modes/rpc/json-normal-value.d.ts +6 -0
  204. package/dist/modes/rpc/json-normal-value.js +100 -0
  205. package/dist/modes/rpc/mcp-presenter-bootstrap.js +5 -0
  206. package/dist/modes/rpc/media-placeholders.d.ts +9 -3
  207. package/dist/modes/rpc/media-placeholders.js +12 -4
  208. package/dist/modes/rpc/rpc-host-command-types.d.ts +2 -0
  209. package/dist/modes/rpc/rpc-question-types.d.ts +1 -0
  210. package/dist/modes/rpc/rpc-read-response-types.d.ts +7 -0
  211. package/dist/modes/rpc/rpc-session-command-types.d.ts +6 -0
  212. package/dist/modes/rpc/rpc-session-state-types.d.ts +3 -0
  213. package/dist/modes/rpc/rpc-session-state.js +2 -0
  214. package/dist/modes/rpc/rpc-types.d.ts +1 -0
  215. package/dist/modes/rpc/security-presentations.js +2 -1
  216. package/dist/modes/rpc/session-attachment-gate.js +1 -0
  217. package/dist/modes/rpc/session-binding.js +30 -17
  218. package/dist/modes/rpc/session-command-activity.js +1 -0
  219. package/dist/modes/rpc/session-event-fanout.d.ts +1 -1
  220. package/dist/modes/rpc/session-event-fanout.js +17 -9
  221. package/dist/modes/rpc/session-event-writer.d.ts +2 -0
  222. package/dist/modes/rpc/session-event-writer.js +24 -5
  223. package/dist/modes/rpc/session-extension-ui-requests.d.ts +2 -1
  224. package/dist/modes/rpc/session-extension-ui-requests.js +15 -10
  225. package/dist/modes/rpc/session-held.js +1 -0
  226. package/dist/modes/rpc/session-record-stream.d.ts +3 -3
  227. package/dist/modes/rpc/session-record-stream.js +3 -3
  228. package/dist/modes/rpc/session-status-stream.d.ts +2 -1
  229. package/dist/modes/rpc/session-status-stream.js +8 -2
  230. package/dist/modes/rpc/session-status-subscriptions.d.ts +2 -1
  231. package/dist/modes/rpc/session-status-subscriptions.js +4 -0
  232. package/dist/modes/rpc/session-structured-lanes.d.ts +12 -0
  233. package/dist/modes/rpc/session-structured-lanes.js +24 -0
  234. package/dist/modes/rpc/session-worker-client.js +6 -5
  235. package/dist/modes/rpc/session-worker.js +5 -2
  236. package/dist/modes/rpc/snapshot-types.d.ts +29 -0
  237. package/dist/modes/rpc/snapshot-types.js +2 -0
  238. package/dist/modes/rpc/structured-session-records.d.ts +12 -0
  239. package/dist/modes/rpc/structured-session-records.js +88 -0
  240. package/dist/modes/rpc/ui-answer-ledger.d.ts +13 -0
  241. package/dist/modes/rpc/ui-answer-ledger.js +34 -0
  242. package/dist/senpi +21 -3
  243. package/docs/extensions.md +7 -1
  244. package/docs/mcp.md +20 -0
  245. package/docs/rpc.md +61 -2
  246. package/package.json +7 -6
  247. package/dist/bundle/chunks/azure-openai-responses-7I45DI72.js +0 -2
  248. package/dist/bundle/chunks/chunk-DMI55M2N.js +0 -2
  249. package/dist/bundle/chunks/chunk-DODH3N6U.js +0 -410
  250. package/dist/bundle/chunks/chunk-J37GRED2.js +0 -5
  251. package/dist/bundle/chunks/chunk-TBU6VYHN.js +0 -13
  252. package/dist/bundle/chunks/cli-main-YYH3IBC6.js +0 -3
  253. package/dist/bundle/chunks/dispatch-MXG5YWLV.js +0 -9
  254. package/dist/bundle/chunks/google-generative-ai-UM5Y7T6B.js +0 -2
  255. package/dist/bundle/chunks/google-vertex-OKU2VQHL.js +0 -2
  256. package/dist/bundle/chunks/host-command-TDLLHXVI.js +0 -2
  257. package/dist/bundle/chunks/interactive-mode-OEPULLZK.js +0 -2
  258. package/dist/bundle/chunks/openai-responses-TNHQ6SJS.js +0 -3
  259. package/dist/bundle/chunks/rpc-mode-ETXQWEB2.js +0 -2
  260. package/dist/bundle/chunks/session-control-endpoint-4LEIDIVI.js +0 -3
@@ -0,0 +1,197 @@
1
+ import { CompactContentIndex } from "./compact-content-index.js";
2
+ import { COMPACT_LEAF_CODE_UNITS, CompactContentStore, } from "./compact-content-store.js";
3
+ import { CompactTransitionError, } from "./compact-transition.js";
4
+ const byContentIndex = ([a], [b]) => a - b;
5
+ export class CompactSessionState {
6
+ constructor(options) {
7
+ this.active = new Map();
8
+ this.sequence = 0;
9
+ this.frozenTailBytes = 0;
10
+ this.store = new CompactContentStore(options);
11
+ const leavesOf = (row) => row.blocks.flatMap((block) => block.leaves);
12
+ this.settled = new CompactContentIndex(this.store, leavesOf, options.compactAboveRows ?? 128);
13
+ }
14
+ get acceptedSequence() {
15
+ return this.sequence;
16
+ }
17
+ accept(transition) {
18
+ switch (transition.type) {
19
+ case "block_start":
20
+ this.start(transition);
21
+ break;
22
+ case "block_delta":
23
+ this.delta(this.block(transition), transition.offset, transition.text);
24
+ break;
25
+ case "block_replace":
26
+ this.replace(this.block(transition), transition.text);
27
+ break;
28
+ case "message_status":
29
+ this.setPersistence(transition.messageId, transition.persistence);
30
+ break;
31
+ case "message_remove":
32
+ this.remove(transition.messageId);
33
+ break;
34
+ }
35
+ return ++this.sequence;
36
+ }
37
+ freeze() {
38
+ const active = new Map();
39
+ for (const [messageId, blocks] of this.active)
40
+ active.set(messageId, this.seal(blocks, false));
41
+ for (const blocks of active.values())
42
+ this.pin(blocks, 1);
43
+ return { sequence: this.sequence, active, settled: this.settled.freeze() };
44
+ }
45
+ release(frozen) {
46
+ for (const blocks of frozen.active.values())
47
+ this.pin(blocks, -1);
48
+ this.settled.release(frozen.settled);
49
+ }
50
+ /** Rejects with the storage error when committed content is gone; never returns empty content for it. */
51
+ async read(frozen) {
52
+ const messages = [];
53
+ for (const [messageId, row] of await this.settled.entries(frozen.settled)) {
54
+ messages.push({ messageId, persistence: row.persistence, blocks: await this.texts(row.blocks) });
55
+ }
56
+ for (const [messageId, blocks] of frozen.active) {
57
+ messages.push({ messageId, persistence: undefined, blocks: await this.texts(blocks) });
58
+ }
59
+ return { sequence: frozen.sequence, messages };
60
+ }
61
+ storageStatus() {
62
+ let residentTailBytes = 0;
63
+ let hotWitnessBytes = 0;
64
+ for (const blocks of this.active.values()) {
65
+ for (const block of blocks.values()) {
66
+ residentTailBytes += block.tail.length * 2;
67
+ hotWitnessBytes += block.witness.length * 2;
68
+ }
69
+ }
70
+ return {
71
+ ...this.store.status(),
72
+ ...this.settled.status(),
73
+ residentTailBytes,
74
+ frozenTailBytes: this.frozenTailBytes,
75
+ hotWitnessBytes,
76
+ };
77
+ }
78
+ idle() {
79
+ return this.store.idle();
80
+ }
81
+ retire() {
82
+ return this.store.retire();
83
+ }
84
+ start(t) {
85
+ if (this.active.get(t.messageId)?.has(t.contentIndex) || this.settled.has(t.messageId)) {
86
+ throw new CompactTransitionError("duplicate_block", `${t.messageId}#${t.contentIndex}`);
87
+ }
88
+ const covered = t.coveredCodeUnits;
89
+ if (!Number.isInteger(covered) || (covered !== 0 && covered !== t.seed.length)) {
90
+ throw new CompactTransitionError("invalid_coverage", `${covered} of ${t.seed.length} seed code units`);
91
+ }
92
+ const emitted = covered > 0 ? 0 : t.seed.length;
93
+ const block = { kind: t.kind, leaves: [], tail: "", units: 0, emitted, covered, witness: "" };
94
+ block.witness = t.seed.slice(0, covered);
95
+ this.append(block, t.seed);
96
+ const blocks = this.active.get(t.messageId) ?? new Map();
97
+ blocks.set(t.contentIndex, block);
98
+ this.active.set(t.messageId, blocks);
99
+ }
100
+ /**
101
+ * Appends only the new suffix. Overlap exists only while a fully covered seed is being replayed
102
+ * (`emitted < covered == units`), so it always lies inside the producer-declared coverage.
103
+ */
104
+ delta(block, offset, text) {
105
+ if (offset !== block.emitted) {
106
+ throw new CompactTransitionError("non_contiguous", `offset ${offset}, expected ${block.emitted}`);
107
+ }
108
+ const overlapEnd = Math.min(offset + text.length, block.units);
109
+ const overlap = Math.max(0, overlapEnd - offset);
110
+ if (overlap > 0 && text.slice(0, overlap) !== block.witness.slice(offset, overlapEnd)) {
111
+ throw new CompactTransitionError("conflicting_overlap", `[${offset}, ${overlapEnd})`);
112
+ }
113
+ this.append(block, text.slice(overlap));
114
+ block.emitted = offset + text.length;
115
+ if (block.emitted >= block.covered)
116
+ block.witness = "";
117
+ }
118
+ replace(block, text) {
119
+ for (const id of block.leaves.splice(0))
120
+ this.store.adjust(id, -1, 0);
121
+ block.tail = "";
122
+ block.units = 0;
123
+ block.covered = 0;
124
+ block.witness = "";
125
+ this.append(block, text);
126
+ block.emitted = block.units;
127
+ }
128
+ setPersistence(messageId, persistence) {
129
+ const blocks = this.active.get(messageId);
130
+ if (blocks) {
131
+ this.settled.set(messageId, { persistence, blocks: this.seal(blocks, true) });
132
+ for (const block of blocks.values())
133
+ for (const id of block.leaves)
134
+ this.store.adjust(id, -1, 0);
135
+ this.active.delete(messageId);
136
+ }
137
+ else if (this.settled.has(messageId)) {
138
+ this.settled.patch(messageId, { persistence });
139
+ }
140
+ else {
141
+ throw new CompactTransitionError("unknown_message", messageId);
142
+ }
143
+ }
144
+ remove(messageId) {
145
+ const blocks = this.active.get(messageId);
146
+ const settled = this.settled.has(messageId);
147
+ if (!blocks && !settled)
148
+ throw new CompactTransitionError("unknown_message", messageId);
149
+ for (const block of blocks?.values() ?? [])
150
+ for (const id of block.leaves)
151
+ this.store.adjust(id, -1, 0);
152
+ this.active.delete(messageId);
153
+ if (settled)
154
+ this.settled.delete(messageId);
155
+ }
156
+ block(address) {
157
+ const block = this.active.get(address.messageId)?.get(address.contentIndex);
158
+ if (!block)
159
+ throw new CompactTransitionError("unknown_block", `${address.messageId}#${address.contentIndex}`);
160
+ return block;
161
+ }
162
+ append(block, text) {
163
+ block.units += text.length;
164
+ let rest = text;
165
+ while (block.tail.length + rest.length >= COMPACT_LEAF_CODE_UNITS) {
166
+ const take = COMPACT_LEAF_CODE_UNITS - block.tail.length;
167
+ block.leaves.push(this.store.createLeaf(block.tail + rest.slice(0, take)));
168
+ block.tail = "";
169
+ rest = rest.slice(take);
170
+ }
171
+ block.tail += rest;
172
+ }
173
+ /** Settling mints the final tail into a spillable leaf; a freeze of an active block shares the tail. */
174
+ seal(blocks, settle) {
175
+ return [...blocks].sort(byContentIndex).map(([contentIndex, block]) => {
176
+ if (settle && block.tail.length > 0) {
177
+ block.leaves.push(this.store.createLeaf(block.tail));
178
+ block.tail = "";
179
+ }
180
+ return { contentIndex, kind: block.kind, leaves: [...block.leaves], tail: block.tail };
181
+ });
182
+ }
183
+ pin(blocks, delta) {
184
+ for (const block of blocks) {
185
+ this.frozenTailBytes += delta * block.tail.length * 2;
186
+ for (const id of block.leaves)
187
+ this.store.adjust(id, 0, delta);
188
+ }
189
+ }
190
+ async texts(blocks) {
191
+ return Promise.all(blocks.map(async ({ contentIndex, kind, leaves, tail }) => {
192
+ const parts = await Promise.all(leaves.map((id) => this.store.readLeaf(id)));
193
+ return { contentIndex, kind, text: parts.join("") + tail };
194
+ }));
195
+ }
196
+ }
197
+ //# sourceMappingURL=compact-session-state.js.map
@@ -0,0 +1,8 @@
1
+ /** Creates a fresh private epoch directory and returns the token its ownership marker holds. */
2
+ export declare function createSpoolDir(dir: string): Promise<string>;
3
+ /**
4
+ * Removes `dir` only while it is still the private, marked directory `createSpoolDir` made. A
5
+ * directory that is already gone leaves nothing to remove, so retirement settles as a no-op.
6
+ */
7
+ export declare function removeSpoolDir(dir: string, token: string): Promise<void>;
8
+ //# sourceMappingURL=compact-spool-dir.d.ts.map
@@ -0,0 +1,94 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { lstat, mkdir, readFile, rm, rmdir, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { describeCause } from "./compact-storage-io.js";
5
+ /**
6
+ * Ownership of the compact spool's epoch directory (senpi#4272). The store creates the directory
7
+ * itself (0700, never adopting an existing one) and writes a private marker into it; removal
8
+ * re-checks the directory and that marker without following symlinks, so retirement never deletes
9
+ * a directory this store did not create and still own.
10
+ */
11
+ const MARKER = ".compact-spool-owner";
12
+ class CompactSpoolRefusedError extends Error {
13
+ constructor(reason, path, cause, cleanupFailure) {
14
+ const detail = cause === undefined ? "" : `: ${describeCause(cause)}`;
15
+ const kept = cleanupFailure === undefined ? "" : `; removing the directory failed: ${describeCause(cleanupFailure)}`;
16
+ super(`compact spool directory refused (${reason}): ${path}${detail}${kept}`, { cause });
17
+ this.name = "CompactSpoolRefusedError";
18
+ this.reason = reason;
19
+ this.path = path;
20
+ }
21
+ }
22
+ /** Creates a fresh private epoch directory and returns the token its ownership marker holds. */
23
+ export async function createSpoolDir(dir) {
24
+ await mkdir(dirname(dir), { recursive: true, mode: 0o700 });
25
+ try {
26
+ await mkdir(dir, { mode: 0o700 });
27
+ }
28
+ catch (cause) {
29
+ if (cause.code === "EEXIST")
30
+ throw new CompactSpoolRefusedError("pre_existing", dir);
31
+ throw cause;
32
+ }
33
+ const token = randomUUID();
34
+ try {
35
+ await writeFile(join(dir, MARKER), token, { flag: "wx", mode: 0o600 });
36
+ }
37
+ catch (cause) {
38
+ throw await abandonSpoolDir(dir, cause);
39
+ }
40
+ return token;
41
+ }
42
+ /**
43
+ * Removes a directory `createSpoolDir` just made but could not mark, so a later store creates it
44
+ * fresh instead of refusing it as pre-existing. Only a marker this call's exclusive create made (any
45
+ * failure but EEXIST) is unlinked, then the directory itself, which `rmdir` removes only while empty.
46
+ */
47
+ async function abandonSpoolDir(dir, cause) {
48
+ try {
49
+ if (cause.code !== "EEXIST")
50
+ await rm(join(dir, MARKER), { force: true });
51
+ await rmdir(dir);
52
+ return new CompactSpoolRefusedError("marker_write_failed", dir, cause);
53
+ }
54
+ catch (cleanupFailure) {
55
+ return new CompactSpoolRefusedError("marker_write_failed", dir, cause, cleanupFailure);
56
+ }
57
+ }
58
+ /**
59
+ * Removes `dir` only while it is still the private, marked directory `createSpoolDir` made. A
60
+ * directory that is already gone leaves nothing to remove, so retirement settles as a no-op.
61
+ */
62
+ export async function removeSpoolDir(dir, token) {
63
+ const owned = await lstat(dir).catch((cause) => {
64
+ if (cause.code === "ENOENT")
65
+ return undefined;
66
+ throw cause;
67
+ });
68
+ if (owned === undefined)
69
+ return;
70
+ const refusal = await refusalFor(dir, owned, token);
71
+ if (refusal)
72
+ throw new CompactSpoolRefusedError(refusal, dir);
73
+ await rm(dir, { recursive: true, force: true });
74
+ }
75
+ async function refusalFor(dir, owned, token) {
76
+ if (owned.isSymbolicLink())
77
+ return "symlink";
78
+ if (!owned.isDirectory())
79
+ return "not_directory";
80
+ const markerPath = join(dir, MARKER);
81
+ const marker = await lstat(markerPath).catch(() => undefined);
82
+ if (!marker?.isFile() || (await readFile(markerPath, "utf8")) !== token)
83
+ return "marker_missing";
84
+ // Windows has no POSIX owner or mode bits; the marker token and the no-follow checks still apply.
85
+ const uid = process.getuid?.();
86
+ if (uid === undefined)
87
+ return undefined;
88
+ if (owned.uid !== uid || marker.uid !== uid)
89
+ return "foreign_owner";
90
+ if (((owned.mode | marker.mode) & 0o077) !== 0)
91
+ return "wide_mode";
92
+ return undefined;
93
+ }
94
+ //# sourceMappingURL=compact-spool-dir.js.map
@@ -0,0 +1,53 @@
1
+ import { type CompactStorageFailure, type CompactStorageIo, type CompactStorageOperation } from "./compact-storage-io.ts";
2
+ /**
3
+ * The spool file path of compact current-state storage (senpi#4272, R3.C6.4): one serial
4
+ * asynchronous job chain, the private epoch directory, spool file writes, reads and reclamation,
5
+ * and the failure ledger. `CompactContentStore` adds leaf ownership and spilling on top.
6
+ */
7
+ export declare const RECLAIM_WORK_ALLOWANCE_BYTES: number;
8
+ export declare abstract class CompactSpoolStorage {
9
+ readonly dir: string;
10
+ private readonly io;
11
+ protected readonly failures: CompactStorageFailure[];
12
+ private nextFile;
13
+ private dirReady;
14
+ private spoolToken;
15
+ private chain;
16
+ private queued;
17
+ protected spillQueued: boolean;
18
+ private halted;
19
+ protected retired: boolean;
20
+ protected pendingIoCopyBytes: number;
21
+ protected spillBytesWritten: number;
22
+ protected reclaimDebtBytes: number;
23
+ constructor(dir: string, io: CompactStorageIo);
24
+ /** Resolves once every queued storage job, including the ones they queued, has completed. */
25
+ idle(): Promise<void>;
26
+ /**
27
+ * Invalidates spilling, joins owned I/O, then removes the epoch directory only when this store
28
+ * created it and it still passes the ownership checks; otherwise rejects with `CompactSpoolRefusedError`.
29
+ */
30
+ retire(): Promise<void>;
31
+ /** Reads a committed file and rejects a missing, unreadable or short one. */
32
+ readFile(path: string, bytes: number, operation: CompactStorageOperation): Promise<Buffer>;
33
+ /** Writes one new spool file; undefined means the write failed and was reported (a spill write also halts spilling). */
34
+ writeFile(data: Buffer, operation: CompactStorageOperation, retainedInMemoryBytes: number): Promise<string | undefined>;
35
+ /**
36
+ * An unreferenced (or partially written) spool file: reclamation debt until its unlink completes or
37
+ * finds it gone. Files dropped after retirement started belong to the directory retirement removes.
38
+ */
39
+ discard(path: string, bytes: number): void;
40
+ /** A failed unlink keeps its debt and is retried with bounded backoff outside the job chain. */
41
+ private reclaim;
42
+ get spillAllowed(): boolean;
43
+ /** True while a storage failure or unreclaimed debt above the work allowance holds spilling back. */
44
+ protected get spillStopped(): boolean;
45
+ enqueue(operation: CompactStorageOperation, job: () => Promise<void>): void;
46
+ protected abstract scheduleSpill(): void;
47
+ /** Creates the private epoch directory once; a refusal or failure halts spilling and is reported. */
48
+ private ensureDir;
49
+ private readFailure;
50
+ private fail;
51
+ private record;
52
+ }
53
+ //# sourceMappingURL=compact-spool-storage.d.ts.map
@@ -0,0 +1,162 @@
1
+ import { join } from "node:path";
2
+ import { createSpoolDir, removeSpoolDir } from "./compact-spool-dir.js";
3
+ import { CompactStorageReadError, describeCause, } from "./compact-storage-io.js";
4
+ /**
5
+ * The spool file path of compact current-state storage (senpi#4272, R3.C6.4): one serial
6
+ * asynchronous job chain, the private epoch directory, spool file writes, reads and reclamation,
7
+ * and the failure ledger. `CompactContentStore` adds leaf ownership and spilling on top.
8
+ */
9
+ export const RECLAIM_WORK_ALLOWANCE_BYTES = 1024 * 1024;
10
+ const RECLAIM_RETRY_DELAYS_MS = [100, 400, 1_600, 6_400, 25_600];
11
+ const FAILURE_HISTORY = 32;
12
+ export class CompactSpoolStorage {
13
+ constructor(dir, io) {
14
+ this.failures = [];
15
+ this.nextFile = 1;
16
+ this.chain = Promise.resolve();
17
+ this.queued = 0;
18
+ this.spillQueued = false;
19
+ this.halted = false;
20
+ this.retired = false;
21
+ this.pendingIoCopyBytes = 0;
22
+ this.spillBytesWritten = 0;
23
+ this.reclaimDebtBytes = 0;
24
+ this.dir = dir;
25
+ this.io = io;
26
+ }
27
+ /** Resolves once every queued storage job, including the ones they queued, has completed. */
28
+ async idle() {
29
+ while (this.queued > 0)
30
+ await this.chain;
31
+ }
32
+ /**
33
+ * Invalidates spilling, joins owned I/O, then removes the epoch directory only when this store
34
+ * created it and it still passes the ownership checks; otherwise rejects with `CompactSpoolRefusedError`.
35
+ */
36
+ async retire() {
37
+ this.retired = true;
38
+ await this.idle();
39
+ const token = this.spoolToken;
40
+ this.spoolToken = undefined;
41
+ if (token !== undefined)
42
+ await removeSpoolDir(this.dir, token);
43
+ }
44
+ /** Reads a committed file and rejects a missing, unreadable or short one. */
45
+ async readFile(path, bytes, operation) {
46
+ let data;
47
+ try {
48
+ data = await this.io.readFile(path);
49
+ }
50
+ catch (cause) {
51
+ throw this.readFailure(operation, path, cause);
52
+ }
53
+ if (data.byteLength !== bytes) {
54
+ throw this.readFailure(operation, path, new Error(`expected ${bytes} bytes, found ${data.byteLength}`));
55
+ }
56
+ return data;
57
+ }
58
+ /** Writes one new spool file; undefined means the write failed and was reported (a spill write also halts spilling). */
59
+ async writeFile(data, operation, retainedInMemoryBytes) {
60
+ const path = await this.ensureDir(retainedInMemoryBytes);
61
+ if (path === undefined)
62
+ return undefined;
63
+ const file = join(path, `${operation === "spill_write" ? "leaf" : "page"}-${this.nextFile++}.bin`);
64
+ this.pendingIoCopyBytes += data.byteLength;
65
+ try {
66
+ await this.io.writeFile(file, data);
67
+ this.spillBytesWritten += data.byteLength;
68
+ return file;
69
+ }
70
+ catch (cause) {
71
+ try {
72
+ this.fail(operation, file, cause, retainedInMemoryBytes);
73
+ }
74
+ finally {
75
+ this.discard(file, data.byteLength);
76
+ }
77
+ return undefined;
78
+ }
79
+ finally {
80
+ this.pendingIoCopyBytes -= data.byteLength;
81
+ }
82
+ }
83
+ /**
84
+ * An unreferenced (or partially written) spool file: reclamation debt until its unlink completes or
85
+ * finds it gone. Files dropped after retirement started belong to the directory retirement removes.
86
+ */
87
+ discard(path, bytes) {
88
+ if (this.retired)
89
+ return;
90
+ this.reclaimDebtBytes += bytes;
91
+ this.enqueue("reclaim", () => this.reclaim(path, bytes, 0));
92
+ }
93
+ /** A failed unlink keeps its debt and is retried with bounded backoff outside the job chain. */
94
+ async reclaim(path, bytes, attempt) {
95
+ try {
96
+ await this.io.unlink(path);
97
+ }
98
+ catch (cause) {
99
+ if (cause.code !== "ENOENT") {
100
+ this.fail("reclaim", path, cause, 0);
101
+ const delay = RECLAIM_RETRY_DELAYS_MS[attempt];
102
+ if (delay !== undefined) {
103
+ setTimeout(() => {
104
+ if (this.retired)
105
+ this.reclaimDebtBytes -= bytes;
106
+ else
107
+ this.enqueue("reclaim", () => this.reclaim(path, bytes, attempt + 1));
108
+ }, delay).unref?.();
109
+ }
110
+ return;
111
+ }
112
+ }
113
+ this.reclaimDebtBytes -= bytes;
114
+ this.scheduleSpill();
115
+ }
116
+ get spillAllowed() {
117
+ return !this.halted && !this.retired;
118
+ }
119
+ /** True while a storage failure or unreclaimed debt above the work allowance holds spilling back. */
120
+ get spillStopped() {
121
+ return this.halted || this.reclaimDebtBytes > RECLAIM_WORK_ALLOWANCE_BYTES;
122
+ }
123
+ enqueue(operation, job) {
124
+ this.queued++;
125
+ this.chain = this.chain
126
+ .then(job)
127
+ .catch((cause) => {
128
+ if (operation === "spill_write")
129
+ this.spillQueued = false;
130
+ this.fail(operation, this.dir, cause, 0);
131
+ })
132
+ .finally(() => {
133
+ this.queued--;
134
+ });
135
+ }
136
+ /** Creates the private epoch directory once; a refusal or failure halts spilling and is reported. */
137
+ ensureDir(retained) {
138
+ this.dirReady ??= createSpoolDir(this.dir).then((token) => {
139
+ this.spoolToken = token;
140
+ return this.dir;
141
+ }, (cause) => {
142
+ this.fail("spool_create", this.dir, cause, retained);
143
+ return undefined;
144
+ });
145
+ return this.dirReady;
146
+ }
147
+ readFailure(operation, path, cause) {
148
+ this.fail(operation, path, cause, 0);
149
+ return new CompactStorageReadError(operation, path, cause);
150
+ }
151
+ fail(operation, path, cause, retained) {
152
+ if (operation === "spill_write" || operation === "spool_create")
153
+ this.halted = true;
154
+ this.record({ operation, path, cause: describeCause(cause), retainedInMemoryBytes: retained });
155
+ }
156
+ record(failure) {
157
+ this.failures.push(failure);
158
+ if (this.failures.length > FAILURE_HISTORY)
159
+ this.failures.shift();
160
+ }
161
+ }
162
+ //# sourceMappingURL=compact-spool-storage.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The asynchronous file seam of compact current-state storage (senpi#4272) and the failures it
3
+ * reports. Every spool access goes through `CompactStorageIo`; nothing here blocks the host loop.
4
+ */
5
+ export type CompactStorageOperation = "spool_create" | "spill_write" | "page_write" | "leaf_read" | "page_read" | "reclaim";
6
+ export interface CompactStorageFailure {
7
+ readonly operation: CompactStorageOperation;
8
+ readonly path: string;
9
+ readonly cause: string;
10
+ /** Accepted bytes this failure leaves resident in memory instead of on disk. */
11
+ readonly retainedInMemoryBytes: number;
12
+ }
13
+ /** Asynchronous file access the host supplies; the default is node's promise filesystem. */
14
+ export interface CompactStorageIo {
15
+ writeFile(path: string, data: Buffer): Promise<void>;
16
+ readFile(path: string): Promise<Buffer>;
17
+ unlink(path: string): Promise<void>;
18
+ }
19
+ export declare const nodeCompactStorageIo: CompactStorageIo;
20
+ /** Never throws: a host-supplied cause that cannot be described must not abort the failure report. */
21
+ export declare function describeCause(cause: unknown): string;
22
+ /** Committed storage that can no longer be read: a real error, never empty content. */
23
+ export declare class CompactStorageReadError extends Error {
24
+ readonly operation: CompactStorageOperation;
25
+ readonly path: string;
26
+ constructor(operation: CompactStorageOperation, path: string, cause: unknown);
27
+ }
28
+ //# sourceMappingURL=compact-storage-io.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { readFile, unlink, writeFile } from "node:fs/promises";
2
+ export const nodeCompactStorageIo = {
3
+ writeFile: (path, data) => writeFile(path, data, { flag: "wx", mode: 0o600 }),
4
+ readFile: (path) => readFile(path),
5
+ unlink: (path) => unlink(path),
6
+ };
7
+ /** Never throws: a host-supplied cause that cannot be described must not abort the failure report. */
8
+ export function describeCause(cause) {
9
+ try {
10
+ return cause instanceof Error ? cause.message : String(cause);
11
+ }
12
+ catch {
13
+ return "unprintable failure cause";
14
+ }
15
+ }
16
+ /** Committed storage that can no longer be read: a real error, never empty content. */
17
+ export class CompactStorageReadError extends Error {
18
+ constructor(operation, path, cause) {
19
+ super(`compact storage ${operation} failed for ${path}: ${describeCause(cause)}`, { cause });
20
+ this.name = "CompactStorageReadError";
21
+ this.operation = operation;
22
+ this.path = path;
23
+ }
24
+ }
25
+ //# sourceMappingURL=compact-storage-io.js.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The typed transitions compact current state accepts (senpi#4272, R3.C6.4) and the typed
3
+ * rejection `CompactSessionState.accept` throws when one fails validation.
4
+ */
5
+ export type CompactBlockKind = "text" | "thinking" | "tool_arguments";
6
+ export type CompactPersistence = "pending" | "appended" | "failed";
7
+ export interface BlockAddress {
8
+ readonly messageId: string;
9
+ readonly contentIndex: number;
10
+ }
11
+ export type CompactTransition = (BlockAddress & {
12
+ readonly type: "block_start";
13
+ readonly kind: CompactBlockKind;
14
+ readonly seed: string;
15
+ /**
16
+ * The whole seed when later deltas replay it from offset 0, or 0 when they continue after it. Partial
17
+ * coverage is refused: the uncovered seed tail would make every later delta overlap or leave a gap.
18
+ */
19
+ readonly coveredCodeUnits: number;
20
+ }) | (BlockAddress & {
21
+ readonly type: "block_delta";
22
+ readonly offset: number;
23
+ readonly text: string;
24
+ }) | (BlockAddress & {
25
+ readonly type: "block_replace";
26
+ readonly text: string;
27
+ }) | {
28
+ readonly type: "message_status";
29
+ readonly messageId: string;
30
+ readonly persistence: CompactPersistence;
31
+ } | {
32
+ readonly type: "message_remove";
33
+ readonly messageId: string;
34
+ };
35
+ type CompactRejection = "unknown_block" | "duplicate_block" | "invalid_coverage" | "non_contiguous" | "conflicting_overlap" | "unknown_message";
36
+ export declare class CompactTransitionError extends Error {
37
+ readonly reason: CompactRejection;
38
+ constructor(reason: CompactRejection, detail: string);
39
+ }
40
+ export {};
41
+ //# sourceMappingURL=compact-transition.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The typed transitions compact current state accepts (senpi#4272, R3.C6.4) and the typed
3
+ * rejection `CompactSessionState.accept` throws when one fails validation.
4
+ */
5
+ export class CompactTransitionError extends Error {
6
+ constructor(reason, detail) {
7
+ super(`compact transition rejected (${reason}): ${detail}`);
8
+ this.name = "CompactTransitionError";
9
+ this.reason = reason;
10
+ }
11
+ }
12
+ //# sourceMappingURL=compact-transition.js.map
@@ -48,6 +48,10 @@ export interface RpcConnectionOptions {
48
48
  */
49
49
  export interface RpcConnectionSink {
50
50
  writeRaw(chunk: string): void;
51
+ /** Shared bindings accept correlated records synchronously, before JSONL buffering. */
52
+ writeRecord?(record: object): void;
53
+ /** The internal structured lane (senpi#4272): seeded records and block controls, never a legacy destination. */
54
+ writeStructured?(record: object): void;
51
55
  waitForBackpressure(): Promise<void>;
52
56
  /** Tears the transport down once its event queue can no longer deliver (overflow, write failure). */
53
57
  close?(): void;