@code-yeongyu/senpi-agent-core 2026.9.12-3 → 2026.9.12

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 (344) hide show
  1. package/README.md +0 -4
  2. package/dist/harness/agent-harness.d.ts +384 -639
  3. package/dist/harness/agent-harness.d.ts.map +1 -1
  4. package/dist/harness/agent-harness.js +250 -5
  5. package/dist/harness/agent-harness.js.map +1 -1
  6. package/dist/harness/compaction/branch-summarization.d.ts +6 -12
  7. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  8. package/dist/harness/compaction/branch-summarization.js +27 -33
  9. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  10. package/dist/harness/compaction/compaction.d.ts +8 -30
  11. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  12. package/dist/harness/compaction/compaction.js +64 -53
  13. package/dist/harness/compaction/compaction.js.map +1 -1
  14. package/dist/harness/env/nodejs.d.ts +28 -25
  15. package/dist/harness/env/nodejs.d.ts.map +1 -1
  16. package/dist/harness/env/nodejs.js +125 -368
  17. package/dist/harness/env/nodejs.js.map +1 -1
  18. package/dist/harness/events.d.ts +38 -20
  19. package/dist/harness/events.d.ts.map +1 -1
  20. package/dist/harness/events.js +55 -233
  21. package/dist/harness/events.js.map +1 -1
  22. package/dist/harness/messages.d.ts +2 -2
  23. package/dist/harness/messages.d.ts.map +1 -1
  24. package/dist/harness/messages.js.map +1 -1
  25. package/dist/harness/prompt-templates.d.ts +2 -3
  26. package/dist/harness/prompt-templates.d.ts.map +1 -1
  27. package/dist/harness/prompt-templates.js +17 -17
  28. package/dist/harness/prompt-templates.js.map +1 -1
  29. package/dist/harness/reducer.d.ts +100 -0
  30. package/dist/harness/reducer.d.ts.map +1 -0
  31. package/dist/harness/reducer.js +420 -0
  32. package/dist/harness/reducer.js.map +1 -0
  33. package/dist/harness/result.d.ts +0 -93
  34. package/dist/harness/result.d.ts.map +1 -1
  35. package/dist/harness/result.js +0 -39
  36. package/dist/harness/result.js.map +1 -1
  37. package/dist/harness/session/context.d.ts +19 -6
  38. package/dist/harness/session/context.d.ts.map +1 -1
  39. package/dist/harness/session/context.js +53 -31
  40. package/dist/harness/session/context.js.map +1 -1
  41. package/dist/harness/session/index.d.ts +5 -9
  42. package/dist/harness/session/index.d.ts.map +1 -1
  43. package/dist/harness/session/index.js +4 -7
  44. package/dist/harness/session/index.js.map +1 -1
  45. package/dist/harness/session/jsonl/codec.d.ts +8 -19
  46. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  47. package/dist/harness/session/jsonl/codec.js +219 -34
  48. package/dist/harness/session/jsonl/codec.js.map +1 -1
  49. package/dist/harness/session/jsonl/errors.d.ts +9 -0
  50. package/dist/harness/session/jsonl/errors.d.ts.map +1 -0
  51. package/dist/harness/session/jsonl/errors.js +18 -0
  52. package/dist/harness/session/jsonl/errors.js.map +1 -0
  53. package/dist/harness/session/jsonl/repo.d.ts +24 -27
  54. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  55. package/dist/harness/session/jsonl/repo.js +151 -276
  56. package/dist/harness/session/jsonl/repo.js.map +1 -1
  57. package/dist/harness/session/jsonl/storage.d.ts +41 -39
  58. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  59. package/dist/harness/session/jsonl/storage.js +196 -175
  60. package/dist/harness/session/jsonl/storage.js.map +1 -1
  61. package/dist/harness/session/jsonl/types.d.ts +22 -23
  62. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  63. package/dist/harness/session/jsonl/types.js +1 -2
  64. package/dist/harness/session/jsonl/types.js.map +1 -1
  65. package/dist/harness/session/jsonl.d.ts +3 -0
  66. package/dist/harness/session/jsonl.d.ts.map +1 -0
  67. package/dist/harness/session/jsonl.js +2 -0
  68. package/dist/harness/session/jsonl.js.map +1 -0
  69. package/dist/harness/session/memory.d.ts +40 -44
  70. package/dist/harness/session/memory.d.ts.map +1 -1
  71. package/dist/harness/session/memory.js +115 -333
  72. package/dist/harness/session/memory.js.map +1 -1
  73. package/dist/harness/session/session.d.ts +51 -78
  74. package/dist/harness/session/session.d.ts.map +1 -1
  75. package/dist/harness/session/session.js +207 -327
  76. package/dist/harness/session/session.js.map +1 -1
  77. package/dist/harness/session/state.d.ts +65 -0
  78. package/dist/harness/session/state.d.ts.map +1 -0
  79. package/dist/harness/session/state.js +320 -0
  80. package/dist/harness/session/state.js.map +1 -0
  81. package/dist/harness/session/testing/conformance.d.ts +4 -0
  82. package/dist/harness/session/testing/conformance.d.ts.map +1 -0
  83. package/dist/harness/session/testing/conformance.js +762 -0
  84. package/dist/harness/session/testing/conformance.js.map +1 -0
  85. package/dist/harness/session/testing/index.d.ts +2 -9
  86. package/dist/harness/session/testing/index.d.ts.map +1 -1
  87. package/dist/harness/session/testing/index.js +1 -8
  88. package/dist/harness/session/testing/index.js.map +1 -1
  89. package/dist/harness/session/testing/types.d.ts +7 -5
  90. package/dist/harness/session/testing/types.d.ts.map +1 -1
  91. package/dist/harness/session/testing/types.js.map +1 -1
  92. package/dist/harness/session/types.d.ts +265 -425
  93. package/dist/harness/session/types.d.ts.map +1 -1
  94. package/dist/harness/session/types.js +7 -7
  95. package/dist/harness/session/types.js.map +1 -1
  96. package/dist/harness/skills.d.ts +2 -3
  97. package/dist/harness/skills.d.ts.map +1 -1
  98. package/dist/harness/skills.js +26 -29
  99. package/dist/harness/skills.js.map +1 -1
  100. package/dist/harness/telemetry.d.ts +43 -64
  101. package/dist/harness/telemetry.d.ts.map +1 -1
  102. package/dist/harness/telemetry.js +26 -45
  103. package/dist/harness/telemetry.js.map +1 -1
  104. package/dist/harness/tools/bash.d.ts +4 -4
  105. package/dist/harness/tools/bash.d.ts.map +1 -1
  106. package/dist/harness/tools/bash.js +93 -66
  107. package/dist/harness/tools/bash.js.map +1 -1
  108. package/dist/harness/tools/edit.d.ts.map +1 -1
  109. package/dist/harness/tools/edit.js +13 -17
  110. package/dist/harness/tools/edit.js.map +1 -1
  111. package/dist/harness/tools/file-mutation-queue.d.ts +1 -2
  112. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
  113. package/dist/harness/tools/file-mutation-queue.js +5 -5
  114. package/dist/harness/tools/file-mutation-queue.js.map +1 -1
  115. package/dist/harness/tools/path-utils.d.ts +2 -3
  116. package/dist/harness/tools/path-utils.d.ts.map +1 -1
  117. package/dist/harness/tools/path-utils.js +5 -5
  118. package/dist/harness/tools/path-utils.js.map +1 -1
  119. package/dist/harness/tools/read.d.ts +1 -2
  120. package/dist/harness/tools/read.d.ts.map +1 -1
  121. package/dist/harness/tools/read.js +6 -4
  122. package/dist/harness/tools/read.js.map +1 -1
  123. package/dist/harness/tools/write.d.ts.map +1 -1
  124. package/dist/harness/tools/write.js +8 -12
  125. package/dist/harness/tools/write.js.map +1 -1
  126. package/dist/harness/types.d.ts +45 -128
  127. package/dist/harness/types.d.ts.map +1 -1
  128. package/dist/harness/types.js.map +1 -1
  129. package/dist/harness/utils/shell-output.d.ts +5 -19
  130. package/dist/harness/utils/shell-output.d.ts.map +1 -1
  131. package/dist/harness/utils/shell-output.js +192 -65
  132. package/dist/harness/utils/shell-output.js.map +1 -1
  133. package/dist/harness/utils/truncate.d.ts +0 -1
  134. package/dist/harness/utils/truncate.d.ts.map +1 -1
  135. package/dist/harness/utils/truncate.js +1 -1
  136. package/dist/harness/utils/truncate.js.map +1 -1
  137. package/dist/index.d.ts +1 -4
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/index.js +6 -3
  140. package/dist/index.js.map +1 -1
  141. package/dist/search/index.d.ts +16 -24
  142. package/dist/search/index.d.ts.map +1 -1
  143. package/dist/search/index.js +1 -1
  144. package/dist/search/index.js.map +1 -1
  145. package/dist/search/scanning.d.ts +29 -0
  146. package/dist/search/scanning.d.ts.map +1 -0
  147. package/dist/search/scanning.js +103 -0
  148. package/dist/search/scanning.js.map +1 -0
  149. package/dist/types.d.ts +0 -2
  150. package/dist/types.d.ts.map +1 -1
  151. package/dist/types.js.map +1 -1
  152. package/package.json +6 -27
  153. package/dist/harness/config.d.ts +0 -9
  154. package/dist/harness/config.d.ts.map +0 -1
  155. package/dist/harness/config.js +0 -35
  156. package/dist/harness/config.js.map +0 -1
  157. package/dist/harness/context.d.ts +0 -9
  158. package/dist/harness/context.d.ts.map +0 -1
  159. package/dist/harness/context.js +0 -13
  160. package/dist/harness/context.js.map +0 -1
  161. package/dist/harness/execution/assistant.d.ts +0 -42
  162. package/dist/harness/execution/assistant.d.ts.map +0 -1
  163. package/dist/harness/execution/assistant.js +0 -82
  164. package/dist/harness/execution/assistant.js.map +0 -1
  165. package/dist/harness/execution/effect-gate.d.ts +0 -22
  166. package/dist/harness/execution/effect-gate.d.ts.map +0 -1
  167. package/dist/harness/execution/effect-gate.js +0 -48
  168. package/dist/harness/execution/effect-gate.js.map +0 -1
  169. package/dist/harness/execution/tools.d.ts +0 -67
  170. package/dist/harness/execution/tools.d.ts.map +0 -1
  171. package/dist/harness/execution/tools.js +0 -121
  172. package/dist/harness/execution/tools.js.map +0 -1
  173. package/dist/harness/hooks.d.ts +0 -38
  174. package/dist/harness/hooks.d.ts.map +0 -1
  175. package/dist/harness/hooks.js +0 -404
  176. package/dist/harness/hooks.js.map +0 -1
  177. package/dist/harness/runtime/drive/boundary.d.ts +0 -28
  178. package/dist/harness/runtime/drive/boundary.d.ts.map +0 -1
  179. package/dist/harness/runtime/drive/boundary.js +0 -173
  180. package/dist/harness/runtime/drive/boundary.js.map +0 -1
  181. package/dist/harness/runtime/drive/checkpoint.d.ts +0 -8
  182. package/dist/harness/runtime/drive/checkpoint.d.ts.map +0 -1
  183. package/dist/harness/runtime/drive/checkpoint.js +0 -133
  184. package/dist/harness/runtime/drive/checkpoint.js.map +0 -1
  185. package/dist/harness/runtime/drive/deferred.d.ts +0 -15
  186. package/dist/harness/runtime/drive/deferred.d.ts.map +0 -1
  187. package/dist/harness/runtime/drive/deferred.js +0 -179
  188. package/dist/harness/runtime/drive/deferred.js.map +0 -1
  189. package/dist/harness/runtime/drive/generation.d.ts +0 -8
  190. package/dist/harness/runtime/drive/generation.d.ts.map +0 -1
  191. package/dist/harness/runtime/drive/generation.js +0 -205
  192. package/dist/harness/runtime/drive/generation.js.map +0 -1
  193. package/dist/harness/runtime/drive/reconcile.d.ts +0 -5
  194. package/dist/harness/runtime/drive/reconcile.d.ts.map +0 -1
  195. package/dist/harness/runtime/drive/reconcile.js +0 -140
  196. package/dist/harness/runtime/drive/reconcile.js.map +0 -1
  197. package/dist/harness/runtime/drive/recovery.d.ts +0 -8
  198. package/dist/harness/runtime/drive/recovery.d.ts.map +0 -1
  199. package/dist/harness/runtime/drive/recovery.js +0 -85
  200. package/dist/harness/runtime/drive/recovery.js.map +0 -1
  201. package/dist/harness/runtime/drive/response.d.ts +0 -23
  202. package/dist/harness/runtime/drive/response.d.ts.map +0 -1
  203. package/dist/harness/runtime/drive/response.js +0 -394
  204. package/dist/harness/runtime/drive/response.js.map +0 -1
  205. package/dist/harness/runtime/drive/retry.d.ts +0 -4
  206. package/dist/harness/runtime/drive/retry.d.ts.map +0 -1
  207. package/dist/harness/runtime/drive/retry.js +0 -34
  208. package/dist/harness/runtime/drive/retry.js.map +0 -1
  209. package/dist/harness/runtime/drive/structural.d.ts +0 -32
  210. package/dist/harness/runtime/drive/structural.d.ts.map +0 -1
  211. package/dist/harness/runtime/drive/structural.js +0 -929
  212. package/dist/harness/runtime/drive/structural.js.map +0 -1
  213. package/dist/harness/runtime/drive/terminal.d.ts +0 -7
  214. package/dist/harness/runtime/drive/terminal.d.ts.map +0 -1
  215. package/dist/harness/runtime/drive/terminal.js +0 -49
  216. package/dist/harness/runtime/drive/terminal.js.map +0 -1
  217. package/dist/harness/runtime/drive/tool-placement.d.ts +0 -14
  218. package/dist/harness/runtime/drive/tool-placement.d.ts.map +0 -1
  219. package/dist/harness/runtime/drive/tool-placement.js +0 -244
  220. package/dist/harness/runtime/drive/tool-placement.js.map +0 -1
  221. package/dist/harness/runtime/drive/tools.d.ts +0 -6
  222. package/dist/harness/runtime/drive/tools.d.ts.map +0 -1
  223. package/dist/harness/runtime/drive/tools.js +0 -449
  224. package/dist/harness/runtime/drive/tools.js.map +0 -1
  225. package/dist/harness/runtime/drive.d.ts +0 -6
  226. package/dist/harness/runtime/drive.d.ts.map +0 -1
  227. package/dist/harness/runtime/drive.js +0 -93
  228. package/dist/harness/runtime/drive.js.map +0 -1
  229. package/dist/harness/runtime/harness.d.ts +0 -59
  230. package/dist/harness/runtime/harness.d.ts.map +0 -1
  231. package/dist/harness/runtime/harness.js +0 -302
  232. package/dist/harness/runtime/harness.js.map +0 -1
  233. package/dist/harness/runtime/index.d.ts +0 -2
  234. package/dist/harness/runtime/index.d.ts.map +0 -1
  235. package/dist/harness/runtime/index.js +0 -2
  236. package/dist/harness/runtime/index.js.map +0 -1
  237. package/dist/harness/runtime/lane.d.ts +0 -118
  238. package/dist/harness/runtime/lane.d.ts.map +0 -1
  239. package/dist/harness/runtime/lane.js +0 -1559
  240. package/dist/harness/runtime/lane.js.map +0 -1
  241. package/dist/harness/runtime/progress.d.ts +0 -15
  242. package/dist/harness/runtime/progress.d.ts.map +0 -1
  243. package/dist/harness/runtime/progress.js +0 -66
  244. package/dist/harness/runtime/progress.js.map +0 -1
  245. package/dist/harness/runtime/reducer.d.ts +0 -5
  246. package/dist/harness/runtime/reducer.d.ts.map +0 -1
  247. package/dist/harness/runtime/reducer.js +0 -240
  248. package/dist/harness/runtime/reducer.js.map +0 -1
  249. package/dist/harness/runtime/restore.d.ts +0 -24
  250. package/dist/harness/runtime/restore.d.ts.map +0 -1
  251. package/dist/harness/runtime/restore.js +0 -117
  252. package/dist/harness/runtime/restore.js.map +0 -1
  253. package/dist/harness/runtime/transcript.d.ts +0 -21
  254. package/dist/harness/runtime/transcript.d.ts.map +0 -1
  255. package/dist/harness/runtime/transcript.js +0 -72
  256. package/dist/harness/runtime/transcript.js.map +0 -1
  257. package/dist/harness/runtime/types.d.ts +0 -105
  258. package/dist/harness/runtime/types.d.ts.map +0 -1
  259. package/dist/harness/runtime/types.js +0 -50
  260. package/dist/harness/runtime/types.js.map +0 -1
  261. package/dist/harness/session/commit.d.ts +0 -53
  262. package/dist/harness/session/commit.d.ts.map +0 -1
  263. package/dist/harness/session/commit.js +0 -57
  264. package/dist/harness/session/commit.js.map +0 -1
  265. package/dist/harness/session/fork-policy.d.ts +0 -21
  266. package/dist/harness/session/fork-policy.d.ts.map +0 -1
  267. package/dist/harness/session/fork-policy.js +0 -55
  268. package/dist/harness/session/fork-policy.js.map +0 -1
  269. package/dist/harness/session/fork.d.ts +0 -16
  270. package/dist/harness/session/fork.d.ts.map +0 -1
  271. package/dist/harness/session/fork.js +0 -79
  272. package/dist/harness/session/fork.js.map +0 -1
  273. package/dist/harness/session/in-memory-storage-state.d.ts +0 -39
  274. package/dist/harness/session/in-memory-storage-state.d.ts.map +0 -1
  275. package/dist/harness/session/in-memory-storage-state.js +0 -269
  276. package/dist/harness/session/in-memory-storage-state.js.map +0 -1
  277. package/dist/harness/session/jsonl/fork.d.ts +0 -38
  278. package/dist/harness/session/jsonl/fork.d.ts.map +0 -1
  279. package/dist/harness/session/jsonl/fork.js +0 -266
  280. package/dist/harness/session/jsonl/fork.js.map +0 -1
  281. package/dist/harness/session/jsonl/index.d.ts +0 -4
  282. package/dist/harness/session/jsonl/index.d.ts.map +0 -1
  283. package/dist/harness/session/jsonl/index.js +0 -4
  284. package/dist/harness/session/jsonl/index.js.map +0 -1
  285. package/dist/harness/session/jsonl/io.d.ts +0 -14
  286. package/dist/harness/session/jsonl/io.d.ts.map +0 -1
  287. package/dist/harness/session/jsonl/io.js +0 -86
  288. package/dist/harness/session/jsonl/io.js.map +0 -1
  289. package/dist/harness/session/jsonl/legacy-v3.d.ts +0 -44
  290. package/dist/harness/session/jsonl/legacy-v3.d.ts.map +0 -1
  291. package/dist/harness/session/jsonl/legacy-v3.js +0 -454
  292. package/dist/harness/session/jsonl/legacy-v3.js.map +0 -1
  293. package/dist/harness/session/mutation-line.d.ts +0 -8
  294. package/dist/harness/session/mutation-line.d.ts.map +0 -1
  295. package/dist/harness/session/mutation-line.js +0 -22
  296. package/dist/harness/session/mutation-line.js.map +0 -1
  297. package/dist/harness/session/testing/benchmark/datasets.d.ts +0 -12
  298. package/dist/harness/session/testing/benchmark/datasets.d.ts.map +0 -1
  299. package/dist/harness/session/testing/benchmark/datasets.js +0 -24
  300. package/dist/harness/session/testing/benchmark/datasets.js.map +0 -1
  301. package/dist/harness/session/testing/benchmark/session-repo.d.ts +0 -42
  302. package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +0 -1
  303. package/dist/harness/session/testing/benchmark/session-repo.js +0 -127
  304. package/dist/harness/session/testing/benchmark/session-repo.js.map +0 -1
  305. package/dist/harness/session/testing/benchmark/storage.d.ts +0 -25
  306. package/dist/harness/session/testing/benchmark/storage.d.ts.map +0 -1
  307. package/dist/harness/session/testing/benchmark/storage.js +0 -114
  308. package/dist/harness/session/testing/benchmark/storage.js.map +0 -1
  309. package/dist/harness/session/testing/conformance/session-repo.d.ts +0 -23
  310. package/dist/harness/session/testing/conformance/session-repo.d.ts.map +0 -1
  311. package/dist/harness/session/testing/conformance/session-repo.js +0 -809
  312. package/dist/harness/session/testing/conformance/session-repo.js.map +0 -1
  313. package/dist/harness/session/testing/conformance/storage.d.ts +0 -4
  314. package/dist/harness/session/testing/conformance/storage.d.ts.map +0 -1
  315. package/dist/harness/session/testing/conformance/storage.js +0 -600
  316. package/dist/harness/session/testing/conformance/storage.js.map +0 -1
  317. package/dist/harness/session/testing/gating-storage.d.ts +0 -26
  318. package/dist/harness/session/testing/gating-storage.d.ts.map +0 -1
  319. package/dist/harness/session/testing/gating-storage.js +0 -103
  320. package/dist/harness/session/testing/gating-storage.js.map +0 -1
  321. package/dist/harness/session/testing/instrumented-storage.d.ts +0 -11
  322. package/dist/harness/session/testing/instrumented-storage.d.ts.map +0 -1
  323. package/dist/harness/session/testing/instrumented-storage.js +0 -19
  324. package/dist/harness/session/testing/instrumented-storage.js.map +0 -1
  325. package/dist/harness/session/testing/storage-decorator.d.ts +0 -20
  326. package/dist/harness/session/testing/storage-decorator.d.ts.map +0 -1
  327. package/dist/harness/session/testing/storage-decorator.js +0 -40
  328. package/dist/harness/session/testing/storage-decorator.js.map +0 -1
  329. package/dist/harness/session/values.d.ts +0 -95
  330. package/dist/harness/session/values.d.ts.map +0 -1
  331. package/dist/harness/session/values.js +0 -81
  332. package/dist/harness/session/values.js.map +0 -1
  333. package/dist/harness/utils/adaptive-publisher.d.ts +0 -24
  334. package/dist/harness/utils/adaptive-publisher.d.ts.map +0 -1
  335. package/dist/harness/utils/adaptive-publisher.js +0 -79
  336. package/dist/harness/utils/adaptive-publisher.js.map +0 -1
  337. package/dist/harness/utils/output-capture.d.ts +0 -31
  338. package/dist/harness/utils/output-capture.d.ts.map +0 -1
  339. package/dist/harness/utils/output-capture.js +0 -217
  340. package/dist/harness/utils/output-capture.js.map +0 -1
  341. package/dist/harness/utils/usage.d.ts +0 -4
  342. package/dist/harness/utils/usage.d.ts.map +0 -1
  343. package/dist/harness/utils/usage.js +0 -33
  344. package/dist/harness/utils/usage.js.map +0 -1
@@ -1,41 +1,43 @@
1
- import type { Context } from "../../context.ts";
2
- import type { CommitResult, Entry, EntryScan, EntryStructure, SessionStats, Storage, StorageBranchScan, UsageRow, UsageScan, Write } from "../types.ts";
3
- import type { ListElement, ListReadOptions, StoredValue, Value, ValueList } from "../values.ts";
4
- import { type JsonlStorageHeader, type JsonlStorageOptions } from "./types.ts";
5
- /** JSONL storage backed by an injected filesystem capability. */
6
- export declare class JsonlStorage implements Storage {
7
- private readonly fileSystem;
8
- private readonly path;
9
- private readonly now;
10
- readonly header: JsonlStorageHeader;
11
- private backing;
12
- private readonly storageState;
13
- private commitQueue;
14
- private state;
15
- private closePromise;
16
- private constructor();
17
- static create(options: JsonlStorageOptions, header: JsonlStorageHeader, initialWrites: Write[], context: Context): Promise<JsonlStorage>;
18
- static open(options: JsonlStorageOptions, context: Context): Promise<JsonlStorage>;
19
- private static openV4;
20
- private static openLegacyV3;
21
- private replayCommitted;
22
- commit(writes: Write[], context: Context): Promise<CommitResult>;
23
- private applyCommit;
24
- /** Atomically upgrade legacy v3 backing and preserve the first caller write as a v4 transaction. */
25
- private upgradeLegacyV3ToV4;
26
- getEntries(ids: string[], _context: Context): Promise<Map<string, Entry>>;
27
- getValue<T>(address: Value<T>, _context: Context): Promise<StoredValue<T> | undefined>;
28
- scanValues<T>(prefix: Value<T>, _context: Context): Promise<StoredValue<T>[]>;
29
- readList<T>(address: ValueList<T>, options: ListReadOptions | undefined, _context: Context): Promise<ListElement<T>[]>;
30
- scanBranch(query: StorageBranchScan, _context: Context): Promise<Entry[]>;
31
- scanBranchStructure(query: StorageBranchScan, _context: Context): Promise<EntryStructure[]>;
32
- scanEntries(query: EntryScan, _context: Context): Promise<Entry[]>;
33
- scanUsage(query: UsageScan, _context: Context): Promise<UsageRow[]>;
34
- getStats(_context: Context): Promise<SessionStats>;
35
- private withImportedUsage;
36
- isLegacyV3(): boolean;
37
- /** Capture the first sequence a later source commit would use. */
38
- captureForkNextSeq(_context: Context): Promise<number>;
39
- close(_context: Context): Promise<void>;
1
+ import { type BranchBounds, type Entry, type EntryQuery, type ForkOptions, type LanePointer, type LaneRecord, type LogItem, type LogOptions, type NewRecord, type OperationStartedRecord, type ProvisionedEntry, type RecordQuery, type SessionStats, type SessionStorage } from "../types.ts";
2
+ import type { JsonlSessionMetadata, JsonlSessionRepoFileSystem, JsonlV4Header } from "./types.ts";
3
+ export declare class JsonlSessionStorage implements SessionStorage<JsonlSessionMetadata> {
4
+ private readonly fs;
5
+ private readonly metadata;
6
+ private readonly state;
7
+ private tail;
8
+ constructor(fs: JsonlSessionRepoFileSystem, metadata: JsonlSessionMetadata);
9
+ static create(fs: JsonlSessionRepoFileSystem, path: string, header: JsonlV4Header): Promise<JsonlSessionStorage>;
10
+ static load(fs: JsonlSessionRepoFileSystem, path: string): Promise<JsonlSessionStorage>;
11
+ fork(path: string, header: JsonlV4Header, options: ForkOptions): Promise<JsonlSessionStorage>;
12
+ drain(): Promise<void>;
13
+ getMetadata(): Promise<JsonlSessionMetadata>;
14
+ getLanes(): Promise<LanePointer[]>;
15
+ createLane(lane: string, at: string | null): Promise<void>;
16
+ moveLane(lane: string, to: string | null): Promise<void>;
17
+ appendEntry<TEntry extends Entry>(newEntry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry>;
18
+ appendRecord<TRecord extends LaneRecord>(newRecord: NewRecord<TRecord>): Promise<TRecord>;
19
+ getEntry(id: string): Promise<Entry | undefined>;
20
+ findEntries(query?: EntryQuery): Promise<Entry[]>;
21
+ findEntriesOnBranch(query: EntryQuery & BranchBounds & {
22
+ start: string;
23
+ }): Promise<Entry[]>;
24
+ findRecords<K extends LaneRecord["type"]>(query: RecordQuery & {
25
+ type: K;
26
+ }): Promise<Extract<LaneRecord, {
27
+ type: K;
28
+ }>[]>;
29
+ findRecords(query?: RecordQuery): Promise<LaneRecord[]>;
30
+ findOpenOperations(lane: string, options?: {
31
+ limit?: number;
32
+ }): Promise<OperationStartedRecord[]>;
33
+ getLog(options?: LogOptions): Promise<LogItem[]>;
34
+ getName(): Promise<string | undefined>;
35
+ setName(name: string | undefined): Promise<void>;
36
+ getLabel(id: string): Promise<string | undefined>;
37
+ setLabel(id: string, label: string | undefined): Promise<void>;
38
+ getStats(): Promise<SessionStats>;
39
+ private enqueue;
40
+ private appendMutation;
41
+ private applyMutation;
40
42
  }
41
43
  //# sourceMappingURL=storage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/storage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/storage.ts"],"names":[],"mappings":"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"}
@@ -1,194 +1,215 @@
1
- import { uuidv7 } from "@earendil-works/pi-ai/utils/uuid";
2
- import { insertUsage } from "../commit.js";
3
- import { InMemoryStorageState } from "../in-memory-storage-state.js";
4
- import { fileValue, parseJsonlTransaction, publishFileAtomically, publishJsonl, readJsonlHeader, serializeJsonlTransaction, } from "./io.js";
5
- import { LegacyV3Source } from "./legacy-v3.js";
6
- import { JSONL_STORAGE_VERSION } from "./types.js";
7
- function splitCompleteLines(content) {
8
- if (content.endsWith("\n"))
9
- return { lines: content.slice(0, -1).split("\n"), torn: false };
10
- const lastNewline = content.lastIndexOf("\n");
11
- if (lastNewline === -1)
12
- return { lines: [], torn: true };
13
- return { lines: content.slice(0, lastNewline).split("\n"), torn: true };
14
- }
15
- /** JSONL storage backed by an injected filesystem capability. */
16
- export class JsonlStorage {
17
- constructor(options, header, backing) {
18
- this.storageState = new InMemoryStorageState();
19
- this.commitQueue = Promise.resolve();
20
- this.state = "open";
21
- this.fileSystem = options.fileSystem;
22
- this.path = options.path;
23
- this.now = options.now ?? Date.now;
24
- this.header = header;
25
- this.backing = backing;
26
- }
27
- static async create(options, header, initialWrites, context) {
28
- const storage = new JsonlStorage(options, header, { kind: "v4" });
29
- const prepared = storage.storageState.prepareCommit(initialWrites, storage.now());
30
- await publishJsonl(options.fileSystem, options.path, header, context, async (append) => {
31
- if (prepared.writes.length !== 0)
32
- await append(prepared.writes);
33
- });
34
- storage.storageState.applyValidated(prepared.writes);
35
- return storage;
1
+ import { SessionState } from "../state.js";
2
+ import { SessionError, } from "../types.js";
3
+ import { encodeHeader, encodeMutation, metadataFromHeader, parseHeader, parseMutation } from "./codec.js";
4
+ import { fileResult, invalidFile, JsonlDecodeError } from "./errors.js";
5
+ /**
6
+ * Build a complete sibling temporary file, then atomically rename it over the destination.
7
+ * The populate callback must create or overwrite `tempPath` with the complete file. The
8
+ * destination is untouched until the rename commits, so a process crash while populating
9
+ * can leave only the ignored `.tmp` file behind.
10
+ *
11
+ * Rejects when population or rename fails. On rejection, temporary-file removal is
12
+ * best-effort and the original error is preserved. Callers must serialize publications to
13
+ * the same destination because they share its deterministic `.tmp` path.
14
+ */
15
+ async function publishFileAtomically(fs, destinationPath, populate) {
16
+ const tempPath = `${destinationPath}.tmp`;
17
+ try {
18
+ await populate(tempPath);
19
+ fileResult(await fs.renameFile(tempPath, destinationPath), `Failed to publish staged file ${destinationPath}`);
20
+ }
21
+ catch (error) {
22
+ await fs.remove(tempPath, { force: true });
23
+ throw error;
36
24
  }
37
- static async open(options, context) {
38
- const reader = fileValue(await options.fileSystem.openTextLineReader(options.path, context), `Failed to read JSONL storage ${options.path}`);
39
- const parsed = await readJsonlHeader(reader, options.path, context).finally(() => reader.close(context));
40
- return parsed.format === "v3-legacy"
41
- ? JsonlStorage.openLegacyV3(options, context)
42
- : JsonlStorage.openV4(options, parsed.header, context);
43
- }
44
- static async openV4(options, header, context) {
45
- const content = fileValue(await options.fileSystem.readTextFile(options.path, context), `Failed to read JSONL storage ${options.path}`);
46
- const { lines, torn } = splitCompleteLines(content);
47
- if (header.storageVersion !== JSONL_STORAGE_VERSION) {
48
- throw new Error(`Session ${header.id} uses unsupported storage version ${header.storageVersion}`);
25
+ }
26
+ export class JsonlSessionStorage {
27
+ constructor(fs, metadata) {
28
+ this.state = new SessionState();
29
+ this.tail = Promise.resolve();
30
+ this.fs = fs;
31
+ this.metadata = structuredClone(metadata);
32
+ }
33
+ static async create(fs, path, header) {
34
+ fileResult(await fs.writeFile(path, encodeHeader(header)), `Failed to initialize session ${path}`);
35
+ const fileInfo = fileResult(await fs.fileInfo(path), `Failed to read session metadata ${path}`);
36
+ return new JsonlSessionStorage(fs, metadataFromHeader(header, path, fileInfo.mtimeMs));
37
+ }
38
+ static async load(fs, path) {
39
+ const content = fileResult(await fs.readTextFile(path), `Failed to read session ${path}`);
40
+ const physicalLines = content.split("\n");
41
+ if (physicalLines.at(-1) === "")
42
+ physicalLines.pop();
43
+ if (physicalLines.length === 0 || !physicalLines[0]) {
44
+ throw invalidFile(path, 1, new JsonlDecodeError("schema", "is missing a header"));
49
45
  }
50
- const storage = new JsonlStorage(options, header, { kind: "v4" });
51
- for (let index = 1; index < lines.length; index++) {
52
- const line = lines[index];
46
+ const headerResult = parseHeader(physicalLines[0]);
47
+ if (!headerResult.ok)
48
+ throw invalidFile(path, 1, headerResult.error);
49
+ const fileInfo = fileResult(await fs.fileInfo(path), `Failed to read session metadata ${path}`);
50
+ const storage = new JsonlSessionStorage(fs, metadataFromHeader(headerResult.value, path, fileInfo.mtimeMs));
51
+ for (let index = 1; index < physicalLines.length; index++) {
52
+ const line = physicalLines[index];
53
+ const mutationResult = parseMutation(line);
54
+ if (!mutationResult.ok) {
55
+ const isTornTail = index === physicalLines.length - 1 && mutationResult.error.kind === "syntax";
56
+ if (isTornTail) {
57
+ // Drop the unacknowledged partial append by atomically publishing the valid prefix.
58
+ const validPrefix = `${physicalLines.slice(0, index).join("\n")}\n`;
59
+ await publishFileAtomically(fs, path, async (tempPath) => {
60
+ fileResult(await fs.writeFile(tempPath, validPrefix), `Failed to stage torn-tail repair ${path}`);
61
+ });
62
+ return storage;
63
+ }
64
+ throw invalidFile(path, index + 1, mutationResult.error);
65
+ }
53
66
  try {
54
- storage.replayCommitted(parseJsonlTransaction(line));
67
+ storage.applyMutation(mutationResult.value);
55
68
  }
56
69
  catch (error) {
57
- throw new Error(`Invalid JSONL storage ${options.path}: line ${index + 1}`, { cause: error });
70
+ if (error instanceof SessionError && error.code === "invalid_entry") {
71
+ throw invalidFile(path, index + 1, error);
72
+ }
73
+ throw error;
58
74
  }
59
75
  }
60
- if (header.nextSeq !== undefined)
61
- storage.storageState.advanceNextSeq(header.nextSeq);
62
- if (torn) {
63
- await publishFileAtomically(options.fileSystem, options.path, context, (append) => append(`${lines.join("\n")}\n`));
76
+ if (!content.endsWith("\n")) {
77
+ fileResult(await fs.appendFile(path, "\n"), `Failed to repair unterminated session tail ${path}`);
64
78
  }
65
79
  return storage;
66
80
  }
67
- static async openLegacyV3(options, context) {
68
- const source = await LegacyV3Source.read(options.fileSystem, options.path, context);
69
- const storage = new JsonlStorage(options, { ...source.header, nextSeq: source.nextSeq }, { kind: "v3", source });
70
- for await (const write of source.writes(context))
71
- storage.replayCommitted([write]);
72
- return storage;
81
+ async fork(path, header, options) {
82
+ const mutations = this.state.createForkMutations(options);
83
+ await publishFileAtomically(this.fs, path, async (tempPath) => {
84
+ const targetStorage = await JsonlSessionStorage.create(this.fs, tempPath, header);
85
+ for (const mutation of mutations) {
86
+ await targetStorage.appendMutation(mutation);
87
+ targetStorage.applyMutation(mutation);
88
+ }
89
+ });
90
+ return JsonlSessionStorage.load(this.fs, path);
73
91
  }
74
- replayCommitted(writes) {
75
- this.storageState.validateCommitted(writes);
76
- this.storageState.applyValidated(writes);
92
+ async drain() {
93
+ await this.tail;
77
94
  }
78
- async commit(writes, context) {
79
- if (this.state !== "open")
80
- throw new Error("JsonlStorage is closed");
81
- const result = this.commitQueue.then(() => this.applyCommit(writes, context));
82
- this.commitQueue = result.then(() => undefined, () => undefined);
83
- return result;
95
+ async getMetadata() {
96
+ return structuredClone(this.metadata);
84
97
  }
85
- async applyCommit(writes, context) {
86
- if (this.backing.kind === "v3" && writes.length !== 0) {
87
- return this.upgradeLegacyV3ToV4(this.backing.source, writes, context);
88
- }
89
- const prepared = this.storageState.prepareCommit(writes, this.now());
90
- if (prepared.writes.length !== 0) {
91
- fileValue(await this.fileSystem.appendFile(this.path, `${serializeJsonlTransaction(prepared.writes)}\n`, context), `Failed to append JSONL storage ${this.path}`);
92
- }
93
- const stats = this.storageState.applyValidated(prepared.writes);
94
- return { ...prepared.result, stats: this.withImportedUsage(stats) };
95
- }
96
- /** Atomically upgrade legacy v3 backing and preserve the first caller write as a v4 transaction. */
97
- async upgradeLegacyV3ToV4(source, callerWrites, context) {
98
- const timestamp = this.now();
99
- const prepared = this.storageState.prepareCommit([
100
- insertUsage({
101
- id: uuidv7(timestamp),
102
- usage: source.importedUsage,
103
- adjustment: true,
104
- details: { source: "v3-import" },
105
- }),
106
- ...callerWrites,
107
- ], timestamp);
108
- const nextSeq = prepared.result.firstSeq + prepared.writes.length;
109
- const upgradedHeader = { ...this.header, nextSeq };
110
- await publishJsonl(this.fileSystem, this.path, upgradedHeader, context, async (append) => {
111
- for await (const write of source.writes(context))
112
- await append([write]);
113
- await append(prepared.writes);
98
+ async getLanes() {
99
+ return this.state.getLanes();
100
+ }
101
+ createLane(lane, at) {
102
+ return this.enqueue(async () => {
103
+ this.state.validateNewLane(lane);
104
+ this.state.validateTarget(at);
105
+ const mutation = { kind: "lane", seq: this.state.nextSequence, lane, leafId: at };
106
+ await this.appendMutation(mutation);
107
+ this.applyMutation(mutation);
108
+ });
109
+ }
110
+ moveLane(lane, to) {
111
+ return this.enqueue(async () => {
112
+ this.state.requireLane(lane);
113
+ this.state.validateTarget(to);
114
+ const mutation = { kind: "lane", seq: this.state.nextSequence, lane, leafId: to };
115
+ await this.appendMutation(mutation);
116
+ this.applyMutation(mutation);
117
+ });
118
+ }
119
+ appendEntry(newEntry, lane) {
120
+ return this.enqueue(async () => {
121
+ const parentId = this.state.requireLane(lane);
122
+ this.state.validateUnusedId(newEntry.id);
123
+ const entry = {
124
+ ...structuredClone(newEntry),
125
+ parentId,
126
+ seq: this.state.nextSequence,
127
+ timestamp: Date.now(),
128
+ };
129
+ const mutation = { kind: "entry", lane, entry };
130
+ await this.appendMutation(mutation);
131
+ this.applyMutation(mutation);
132
+ return structuredClone(entry);
114
133
  });
115
- const stats = this.storageState.applyValidated(prepared.writes);
116
- this.backing = { kind: "v4" };
117
- // The first sequence belongs to the internal usage adjustment; return only caller-write sequences.
118
- return {
119
- ...prepared.result,
120
- firstSeq: prepared.result.firstSeq + 1,
121
- seqs: prepared.result.seqs.slice(1),
122
- stats,
123
- };
124
- }
125
- getEntries(ids, _context) {
126
- if (this.state !== "open")
127
- return Promise.reject(new Error("JsonlStorage is closed"));
128
- return Promise.resolve(this.storageState.getEntries(ids));
129
- }
130
- getValue(address, _context) {
131
- if (this.state !== "open")
132
- return Promise.reject(new Error("JsonlStorage is closed"));
133
- return Promise.resolve(this.storageState.getValue(address));
134
- }
135
- scanValues(prefix, _context) {
136
- if (this.state !== "open")
137
- return Promise.reject(new Error("JsonlStorage is closed"));
138
- return Promise.resolve(this.storageState.scanValues(prefix));
139
- }
140
- async readList(address, options, _context) {
141
- if (this.state !== "open")
142
- throw new Error("JsonlStorage is closed");
143
- return this.storageState.readList(address, options);
144
- }
145
- async scanBranch(query, _context) {
146
- if (this.state !== "open")
147
- throw new Error("JsonlStorage is closed");
148
- return this.storageState.scanBranch(query);
149
- }
150
- async scanBranchStructure(query, _context) {
151
- if (this.state !== "open")
152
- throw new Error("JsonlStorage is closed");
153
- return this.storageState.scanBranchStructure(query);
154
- }
155
- scanEntries(query, _context) {
156
- if (this.state !== "open")
157
- return Promise.reject(new Error("JsonlStorage is closed"));
158
- return Promise.resolve(this.storageState.scanEntries(query));
159
- }
160
- scanUsage(query, _context) {
161
- if (this.state !== "open")
162
- return Promise.reject(new Error("JsonlStorage is closed"));
163
- return Promise.resolve(this.storageState.scanUsage(query));
164
- }
165
- getStats(_context) {
166
- if (this.state !== "open")
167
- return Promise.reject(new Error("JsonlStorage is closed"));
168
- return Promise.resolve(this.withImportedUsage(this.storageState.getStats()));
169
- }
170
- withImportedUsage(stats) {
171
- return this.backing.kind === "v4" ? stats : { ...stats, usage: this.backing.source.importedUsage };
172
- }
173
- isLegacyV3() {
174
- return this.backing.kind === "v3";
175
- }
176
- /** Capture the first sequence a later source commit would use. */
177
- captureForkNextSeq(_context) {
178
- if (this.state !== "open")
179
- return Promise.reject(new Error("JsonlStorage is closed"));
180
- const result = this.commitQueue.then(() => this.storageState.getNextSeq());
181
- this.commitQueue = result.then(() => undefined, () => undefined);
182
- return result;
183
134
  }
184
- close(_context) {
185
- if (this.closePromise !== undefined)
186
- return this.closePromise;
187
- this.state = "closing";
188
- this.closePromise = this.commitQueue.then(() => {
189
- this.state = "closed";
135
+ appendRecord(newRecord) {
136
+ return this.enqueue(async () => {
137
+ this.state.requireLane(newRecord.lane);
138
+ this.state.validateUnusedId(newRecord.id);
139
+ const currentOpenOperationId = this.state.findOpenOperations(newRecord.lane, { limit: 1 })[0]?.id;
140
+ if (newRecord.type === "operation_started" && currentOpenOperationId !== undefined) {
141
+ throw new SessionError("storage", `Lane ${newRecord.lane} already has an open operation ${currentOpenOperationId}`);
142
+ }
143
+ const record = {
144
+ ...structuredClone(newRecord),
145
+ seq: this.state.nextSequence,
146
+ timestamp: Date.now(),
147
+ };
148
+ const mutation = { kind: "record", record };
149
+ await this.appendMutation(mutation);
150
+ this.applyMutation(mutation);
151
+ return structuredClone(record);
190
152
  });
191
- return this.closePromise;
153
+ }
154
+ async getEntry(id) {
155
+ const entry = this.state.getEntry(id);
156
+ return entry === undefined ? undefined : structuredClone(entry);
157
+ }
158
+ async findEntries(query = {}) {
159
+ return structuredClone(this.state.findEntries(query));
160
+ }
161
+ async findEntriesOnBranch(query) {
162
+ return structuredClone(this.state.findEntriesOnBranch(query));
163
+ }
164
+ async findRecords(query = {}) {
165
+ return structuredClone(this.state.findRecords(query));
166
+ }
167
+ async findOpenOperations(lane, options) {
168
+ return structuredClone(this.state.findOpenOperations(lane, options));
169
+ }
170
+ async getLog(options = {}) {
171
+ return structuredClone(this.state.getLog(options));
172
+ }
173
+ async getName() {
174
+ return this.state.getName();
175
+ }
176
+ setName(name) {
177
+ return this.enqueue(async () => {
178
+ const mutation = { kind: "fact", seq: this.state.nextSequence, fact: "name", name };
179
+ await this.appendMutation(mutation);
180
+ this.applyMutation(mutation);
181
+ });
182
+ }
183
+ async getLabel(id) {
184
+ return this.state.getLabel(id);
185
+ }
186
+ setLabel(id, label) {
187
+ return this.enqueue(async () => {
188
+ this.state.validateTarget(id);
189
+ const mutation = {
190
+ kind: "fact",
191
+ seq: this.state.nextSequence,
192
+ fact: "label",
193
+ targetId: id,
194
+ label,
195
+ };
196
+ await this.appendMutation(mutation);
197
+ this.applyMutation(mutation);
198
+ });
199
+ }
200
+ async getStats() {
201
+ return structuredClone(this.state.getStats());
202
+ }
203
+ enqueue(operation) {
204
+ const result = this.tail.then(operation);
205
+ this.tail = result.then(() => undefined, () => undefined);
206
+ return result;
207
+ }
208
+ async appendMutation(mutation) {
209
+ fileResult(await this.fs.appendFile(this.metadata.path, encodeMutation(mutation)), `Failed to append session ${this.metadata.path}`);
210
+ }
211
+ applyMutation(mutation) {
212
+ this.state.applyMutation(mutation);
192
213
  }
193
214
  }
194
215
  //# sourceMappingURL=storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import { uuidv7 } from \"@earendil-works/pi-ai/utils/uuid\";\nimport type { Context } from \"../../context.ts\";\nimport type { FileSystem } from \"../../types.ts\";\nimport { insertUsage } from \"../commit.ts\";\nimport { type CommittedWrite, InMemoryStorageState } from \"../in-memory-storage-state.ts\";\nimport type {\n\tCommitResult,\n\tEntry,\n\tEntryScan,\n\tEntryStructure,\n\tSessionStats,\n\tStorage,\n\tStorageBranchScan,\n\tUsageRow,\n\tUsageScan,\n\tWrite,\n} from \"../types.ts\";\nimport type { ListElement, ListReadOptions, StoredValue, Value, ValueList } from \"../values.ts\";\nimport {\n\tfileValue,\n\tparseJsonlTransaction,\n\tpublishFileAtomically,\n\tpublishJsonl,\n\treadJsonlHeader,\n\tserializeJsonlTransaction,\n} from \"./io.ts\";\nimport { LegacyV3Source } from \"./legacy-v3.ts\";\nimport { JSONL_STORAGE_VERSION, type JsonlStorageHeader, type JsonlStorageOptions } from \"./types.ts\";\n\nfunction splitCompleteLines(content: string): { lines: string[]; torn: boolean } {\n\tif (content.endsWith(\"\\n\")) return { lines: content.slice(0, -1).split(\"\\n\"), torn: false };\n\tconst lastNewline = content.lastIndexOf(\"\\n\");\n\tif (lastNewline === -1) return { lines: [], torn: true };\n\treturn { lines: content.slice(0, lastNewline).split(\"\\n\"), torn: true };\n}\n\ntype JsonlBacking = { kind: \"v4\" } | { kind: \"v3\"; source: LegacyV3Source };\n\n/** JSONL storage backed by an injected filesystem capability. */\nexport class JsonlStorage implements Storage {\n\tprivate readonly fileSystem: FileSystem;\n\tprivate readonly path: string;\n\tprivate readonly now: () => number;\n\treadonly header: JsonlStorageHeader;\n\tprivate backing: JsonlBacking;\n\tprivate readonly storageState = new InMemoryStorageState();\n\tprivate commitQueue: Promise<void> = Promise.resolve();\n\tprivate state: \"open\" | \"closing\" | \"closed\" = \"open\";\n\tprivate closePromise: Promise<void> | undefined;\n\n\tprivate constructor(options: JsonlStorageOptions, header: JsonlStorageHeader, backing: JsonlBacking) {\n\t\tthis.fileSystem = options.fileSystem;\n\t\tthis.path = options.path;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.header = header;\n\t\tthis.backing = backing;\n\t}\n\n\tstatic async create(\n\t\toptions: JsonlStorageOptions,\n\t\theader: JsonlStorageHeader,\n\t\tinitialWrites: Write[],\n\t\tcontext: Context,\n\t): Promise<JsonlStorage> {\n\t\tconst storage = new JsonlStorage(options, header, { kind: \"v4\" });\n\t\tconst prepared = storage.storageState.prepareCommit(initialWrites, storage.now());\n\t\tawait publishJsonl(options.fileSystem, options.path, header, context, async (append) => {\n\t\t\tif (prepared.writes.length !== 0) await append(prepared.writes);\n\t\t});\n\t\tstorage.storageState.applyValidated(prepared.writes);\n\t\treturn storage;\n\t}\n\n\tstatic async open(options: JsonlStorageOptions, context: Context): Promise<JsonlStorage> {\n\t\tconst reader = fileValue(\n\t\t\tawait options.fileSystem.openTextLineReader(options.path, context),\n\t\t\t`Failed to read JSONL storage ${options.path}`,\n\t\t);\n\t\tconst parsed = await readJsonlHeader(reader, options.path, context).finally(() => reader.close(context));\n\t\treturn parsed.format === \"v3-legacy\"\n\t\t\t? JsonlStorage.openLegacyV3(options, context)\n\t\t\t: JsonlStorage.openV4(options, parsed.header, context);\n\t}\n\n\tprivate static async openV4(\n\t\toptions: JsonlStorageOptions,\n\t\theader: JsonlStorageHeader,\n\t\tcontext: Context,\n\t): Promise<JsonlStorage> {\n\t\tconst content = fileValue(\n\t\t\tawait options.fileSystem.readTextFile(options.path, context),\n\t\t\t`Failed to read JSONL storage ${options.path}`,\n\t\t);\n\t\tconst { lines, torn } = splitCompleteLines(content);\n\t\tif (header.storageVersion !== JSONL_STORAGE_VERSION) {\n\t\t\tthrow new Error(`Session ${header.id} uses unsupported storage version ${header.storageVersion}`);\n\t\t}\n\t\tconst storage = new JsonlStorage(options, header, { kind: \"v4\" });\n\t\tfor (let index = 1; index < lines.length; index++) {\n\t\t\tconst line = lines[index]!;\n\t\t\ttry {\n\t\t\t\tstorage.replayCommitted(parseJsonlTransaction(line));\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Invalid JSONL storage ${options.path}: line ${index + 1}`, { cause: error });\n\t\t\t}\n\t\t}\n\t\tif (header.nextSeq !== undefined) storage.storageState.advanceNextSeq(header.nextSeq);\n\t\tif (torn) {\n\t\t\tawait publishFileAtomically(options.fileSystem, options.path, context, (append) =>\n\t\t\t\tappend(`${lines.join(\"\\n\")}\\n`),\n\t\t\t);\n\t\t}\n\t\treturn storage;\n\t}\n\n\tprivate static async openLegacyV3(options: JsonlStorageOptions, context: Context): Promise<JsonlStorage> {\n\t\tconst source = await LegacyV3Source.read(options.fileSystem, options.path, context);\n\t\tconst storage = new JsonlStorage(options, { ...source.header, nextSeq: source.nextSeq }, { kind: \"v3\", source });\n\t\tfor await (const write of source.writes(context)) storage.replayCommitted([write]);\n\t\treturn storage;\n\t}\n\n\tprivate replayCommitted(writes: readonly CommittedWrite[]): void {\n\t\tthis.storageState.validateCommitted(writes);\n\t\tthis.storageState.applyValidated(writes);\n\t}\n\n\tasync commit(writes: Write[], context: Context): Promise<CommitResult> {\n\t\tif (this.state !== \"open\") throw new Error(\"JsonlStorage is closed\");\n\t\tconst result = this.commitQueue.then(() => this.applyCommit(writes, context));\n\t\tthis.commitQueue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tprivate async applyCommit(writes: Write[], context: Context): Promise<CommitResult> {\n\t\tif (this.backing.kind === \"v3\" && writes.length !== 0) {\n\t\t\treturn this.upgradeLegacyV3ToV4(this.backing.source, writes, context);\n\t\t}\n\t\tconst prepared = this.storageState.prepareCommit(writes, this.now());\n\t\tif (prepared.writes.length !== 0) {\n\t\t\tfileValue(\n\t\t\t\tawait this.fileSystem.appendFile(this.path, `${serializeJsonlTransaction(prepared.writes)}\\n`, context),\n\t\t\t\t`Failed to append JSONL storage ${this.path}`,\n\t\t\t);\n\t\t}\n\t\tconst stats = this.storageState.applyValidated(prepared.writes);\n\t\treturn { ...prepared.result, stats: this.withImportedUsage(stats) };\n\t}\n\n\t/** Atomically upgrade legacy v3 backing and preserve the first caller write as a v4 transaction. */\n\tprivate async upgradeLegacyV3ToV4(\n\t\tsource: LegacyV3Source,\n\t\tcallerWrites: Write[],\n\t\tcontext: Context,\n\t): Promise<CommitResult> {\n\t\tconst timestamp = this.now();\n\t\tconst prepared = this.storageState.prepareCommit(\n\t\t\t[\n\t\t\t\tinsertUsage({\n\t\t\t\t\tid: uuidv7(timestamp),\n\t\t\t\t\tusage: source.importedUsage,\n\t\t\t\t\tadjustment: true,\n\t\t\t\t\tdetails: { source: \"v3-import\" },\n\t\t\t\t}),\n\t\t\t\t...callerWrites,\n\t\t\t],\n\t\t\ttimestamp,\n\t\t);\n\n\t\tconst nextSeq = prepared.result.firstSeq + prepared.writes.length;\n\t\tconst upgradedHeader = { ...this.header, nextSeq };\n\t\tawait publishJsonl(this.fileSystem, this.path, upgradedHeader, context, async (append) => {\n\t\t\tfor await (const write of source.writes(context)) await append([write]);\n\t\t\tawait append(prepared.writes);\n\t\t});\n\n\t\tconst stats = this.storageState.applyValidated(prepared.writes);\n\t\tthis.backing = { kind: \"v4\" };\n\t\t// The first sequence belongs to the internal usage adjustment; return only caller-write sequences.\n\t\treturn {\n\t\t\t...prepared.result,\n\t\t\tfirstSeq: prepared.result.firstSeq + 1,\n\t\t\tseqs: prepared.result.seqs.slice(1),\n\t\t\tstats,\n\t\t};\n\t}\n\n\tgetEntries(ids: string[], _context: Context): Promise<Map<string, Entry>> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.getEntries(ids));\n\t}\n\n\tgetValue<T>(address: Value<T>, _context: Context): Promise<StoredValue<T> | undefined> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.getValue(address));\n\t}\n\n\tscanValues<T>(prefix: Value<T>, _context: Context): Promise<StoredValue<T>[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanValues(prefix));\n\t}\n\n\tasync readList<T>(\n\t\taddress: ValueList<T>,\n\t\toptions: ListReadOptions | undefined,\n\t\t_context: Context,\n\t): Promise<ListElement<T>[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"JsonlStorage is closed\");\n\t\treturn this.storageState.readList(address, options);\n\t}\n\n\tasync scanBranch(query: StorageBranchScan, _context: Context): Promise<Entry[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"JsonlStorage is closed\");\n\t\treturn this.storageState.scanBranch(query);\n\t}\n\n\tasync scanBranchStructure(query: StorageBranchScan, _context: Context): Promise<EntryStructure[]> {\n\t\tif (this.state !== \"open\") throw new Error(\"JsonlStorage is closed\");\n\t\treturn this.storageState.scanBranchStructure(query);\n\t}\n\n\tscanEntries(query: EntryScan, _context: Context): Promise<Entry[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanEntries(query));\n\t}\n\n\tscanUsage(query: UsageScan, _context: Context): Promise<UsageRow[]> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.storageState.scanUsage(query));\n\t}\n\n\tgetStats(_context: Context): Promise<SessionStats> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\treturn Promise.resolve(this.withImportedUsage(this.storageState.getStats()));\n\t}\n\n\tprivate withImportedUsage(stats: SessionStats): SessionStats {\n\t\treturn this.backing.kind === \"v4\" ? stats : { ...stats, usage: this.backing.source.importedUsage };\n\t}\n\n\tisLegacyV3(): boolean {\n\t\treturn this.backing.kind === \"v3\";\n\t}\n\n\t/** Capture the first sequence a later source commit would use. */\n\tcaptureForkNextSeq(_context: Context): Promise<number> {\n\t\tif (this.state !== \"open\") return Promise.reject(new Error(\"JsonlStorage is closed\"));\n\t\tconst result = this.commitQueue.then(() => this.storageState.getNextSeq());\n\t\tthis.commitQueue = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tclose(_context: Context): Promise<void> {\n\t\tif (this.closePromise !== undefined) return this.closePromise;\n\t\tthis.state = \"closing\";\n\t\tthis.closePromise = this.commitQueue.then(() => {\n\t\t\tthis.state = \"closed\";\n\t\t});\n\t\treturn this.closePromise;\n\t}\n}\n"]}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAaN,YAAY,GAGZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC1G,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGxE;;;;;;;;;GASG;AACH,KAAK,UAAU,qBAAqB,CACnC,EAA8B,EAC9B,eAAuB,EACvB,QAA6C;IAE7C,MAAM,QAAQ,GAAG,GAAG,eAAe,MAAM,CAAC;IAC1C,IAAI,CAAC;QACJ,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzB,UAAU,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,iCAAiC,eAAe,EAAE,CAAC,CAAC;IAChH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,OAAO,mBAAmB;IAM/B,YAAY,EAA8B,EAAE,QAA8B;QAHzD,UAAK,GAAG,IAAI,YAAY,EAAE,CAAC;QACpC,SAAI,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;QAG/C,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,MAAM,CAClB,EAA8B,EAC9B,IAAY,EACZ,MAAqB;QAErB,UAAU,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,gCAAgC,IAAI,EAAE,CAAC,CAAC;QACnG,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,mCAAmC,IAAI,EAAE,CAAC,CAAC;QAChG,OAAO,IAAI,mBAAmB,CAAC,EAAE,EAAE,kBAAkB,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAA8B,EAAE,IAAY;QAC7D,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,0BAA0B,IAAI,EAAE,CAAC,CAAC;QAC1F,MAAM,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;YAAE,aAAa,CAAC,GAAG,EAAE,CAAC;QACrD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,gBAAgB,CAAC,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC;QACnF,CAAC;QACD,MAAM,YAAY,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,YAAY,CAAC,EAAE;YAAE,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QACrE,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,mCAAmC,IAAI,EAAE,CAAC,CAAC;QAChG,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC,EAAE,EAAE,kBAAkB,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5G,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YAC3D,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAE,CAAC;YACnC,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC;gBACxB,MAAM,UAAU,GAAG,KAAK,KAAK,aAAa,CAAC,MAAM,GAAG,CAAC,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;gBAChG,IAAI,UAAU,EAAE,CAAC;oBAChB,oFAAoF;oBACpF,MAAM,WAAW,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;oBACpE,MAAM,qBAAqB,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;wBACxD,UAAU,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,oCAAoC,IAAI,EAAE,CAAC,CAAC;oBACnG,CAAC,CAAC,CAAC;oBACH,OAAO,OAAO,CAAC;gBAChB,CAAC;gBACD,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;YAC1D,CAAC;YACD,IAAI,CAAC;gBACJ,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;oBACrE,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;gBAC3C,CAAC;gBACD,MAAM,KAAK,CAAC;YACb,CAAC;QACF,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,UAAU,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,8CAA8C,IAAI,EAAE,CAAC,CAAC;QACnG,CAAC;QACD,OAAO,OAAO,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,MAAqB,EAAE,OAAoB;QACnE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1D,MAAM,qBAAqB,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;YAC7D,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;YAClF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBAClC,MAAM,aAAa,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;gBAC7C,aAAa,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YACvC,CAAC;QACF,CAAC,CAAC,CAAC;QACH,OAAO,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,KAAK;QACV,MAAM,IAAI,CAAC,IAAI,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,WAAW;QAChB,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,QAAQ;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED,UAAU,CAAC,IAAY,EAAE,EAAiB;QACzC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC9B,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YAC9B,MAAM,QAAQ,GAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YACnG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,IAAY,EAAE,EAAiB;QACvC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC9B,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YAC9B,MAAM,QAAQ,GAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YACnG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,WAAW,CAAuB,QAAkC,EAAE,IAAY;QACjF,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACzC,MAAM,KAAK,GAAG;gBACb,GAAG,eAAe,CAAC,QAAQ,CAAC;gBAC5B,QAAQ;gBACR,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;gBAC5B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACA,CAAC;YACvB,MAAM,QAAQ,GAAoB,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YACjE,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC7B,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,YAAY,CAA6B,SAA6B;QACrE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC9B,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACvC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAC1C,MAAM,sBAAsB,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YAClG,IAAI,SAAS,CAAC,IAAI,KAAK,mBAAmB,IAAI,sBAAsB,KAAK,SAAS,EAAE,CAAC;gBACpF,MAAM,IAAI,YAAY,CACrB,SAAS,EACT,QAAQ,SAAS,CAAC,IAAI,kCAAkC,sBAAsB,EAAE,CAChF,CAAC;YACH,CAAC;YACD,MAAM,MAAM,GAAG;gBACd,GAAG,eAAe,CAAC,SAAS,CAAC;gBAC7B,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;gBAC5B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACC,CAAC;YACxB,MAAM,QAAQ,GAAoB,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;YAC7D,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC7B,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACxB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACtC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,KAAK,GAAe,EAAE;QACvC,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,KAAoD;QAC7E,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,CAAC;IAMD,KAAK,CAAC,WAAW,CAAC,KAAK,GAAgB,EAAE;QACxC,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,IAAY,EAAE,OAA4B;QAClE,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAO,GAAe,EAAE;QACpC,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,IAAwB;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC9B,MAAM,QAAQ,GAAoB,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YACrG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,QAAQ,CAAC,EAAU,EAAE,KAAyB;QAC7C,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE;YAC9B,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YAC9B,MAAM,QAAQ,GAAoB;gBACjC,IAAI,EAAE,MAAM;gBACZ,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY;gBAC5B,IAAI,EAAE,OAAO;gBACb,QAAQ,EAAE,EAAE;gBACZ,KAAK;aACL,CAAC;YACF,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ;QACb,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/C,CAAC;IAEO,OAAO,CAAI,SAA2B;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CACtB,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;QACF,OAAO,MAAM,CAAC;IACf,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,QAAyB;QACrD,UAAU,CACT,MAAM,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,EACtE,4BAA4B,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAChD,CAAC;IACH,CAAC;IAEO,aAAa,CAAC,QAAyB;QAC9C,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;CACD","sourcesContent":["import { type SessionMutation, SessionState } from \"../state.ts\";\nimport {\n\ttype BranchBounds,\n\ttype Entry,\n\ttype EntryQuery,\n\ttype ForkOptions,\n\ttype LanePointer,\n\ttype LaneRecord,\n\ttype LogItem,\n\ttype LogOptions,\n\ttype NewRecord,\n\ttype OperationStartedRecord,\n\ttype ProvisionedEntry,\n\ttype RecordQuery,\n\tSessionError,\n\ttype SessionStats,\n\ttype SessionStorage,\n} from \"../types.ts\";\nimport { encodeHeader, encodeMutation, metadataFromHeader, parseHeader, parseMutation } from \"./codec.ts\";\nimport { fileResult, invalidFile, JsonlDecodeError } from \"./errors.ts\";\nimport type { JsonlSessionMetadata, JsonlSessionRepoFileSystem, JsonlV4Header } from \"./types.ts\";\n\n/**\n * Build a complete sibling temporary file, then atomically rename it over the destination.\n * The populate callback must create or overwrite `tempPath` with the complete file. The\n * destination is untouched until the rename commits, so a process crash while populating\n * can leave only the ignored `.tmp` file behind.\n *\n * Rejects when population or rename fails. On rejection, temporary-file removal is\n * best-effort and the original error is preserved. Callers must serialize publications to\n * the same destination because they share its deterministic `.tmp` path.\n */\nasync function publishFileAtomically(\n\tfs: JsonlSessionRepoFileSystem,\n\tdestinationPath: string,\n\tpopulate: (tempPath: string) => Promise<void>,\n): Promise<void> {\n\tconst tempPath = `${destinationPath}.tmp`;\n\ttry {\n\t\tawait populate(tempPath);\n\t\tfileResult(await fs.renameFile(tempPath, destinationPath), `Failed to publish staged file ${destinationPath}`);\n\t} catch (error) {\n\t\tawait fs.remove(tempPath, { force: true });\n\t\tthrow error;\n\t}\n}\n\nexport class JsonlSessionStorage implements SessionStorage<JsonlSessionMetadata> {\n\tprivate readonly fs: JsonlSessionRepoFileSystem;\n\tprivate readonly metadata: JsonlSessionMetadata;\n\tprivate readonly state = new SessionState();\n\tprivate tail: Promise<void> = Promise.resolve();\n\n\tconstructor(fs: JsonlSessionRepoFileSystem, metadata: JsonlSessionMetadata) {\n\t\tthis.fs = fs;\n\t\tthis.metadata = structuredClone(metadata);\n\t}\n\n\tstatic async create(\n\t\tfs: JsonlSessionRepoFileSystem,\n\t\tpath: string,\n\t\theader: JsonlV4Header,\n\t): Promise<JsonlSessionStorage> {\n\t\tfileResult(await fs.writeFile(path, encodeHeader(header)), `Failed to initialize session ${path}`);\n\t\tconst fileInfo = fileResult(await fs.fileInfo(path), `Failed to read session metadata ${path}`);\n\t\treturn new JsonlSessionStorage(fs, metadataFromHeader(header, path, fileInfo.mtimeMs));\n\t}\n\n\tstatic async load(fs: JsonlSessionRepoFileSystem, path: string): Promise<JsonlSessionStorage> {\n\t\tconst content = fileResult(await fs.readTextFile(path), `Failed to read session ${path}`);\n\t\tconst physicalLines = content.split(\"\\n\");\n\t\tif (physicalLines.at(-1) === \"\") physicalLines.pop();\n\t\tif (physicalLines.length === 0 || !physicalLines[0]) {\n\t\t\tthrow invalidFile(path, 1, new JsonlDecodeError(\"schema\", \"is missing a header\"));\n\t\t}\n\t\tconst headerResult = parseHeader(physicalLines[0]);\n\t\tif (!headerResult.ok) throw invalidFile(path, 1, headerResult.error);\n\t\tconst fileInfo = fileResult(await fs.fileInfo(path), `Failed to read session metadata ${path}`);\n\t\tconst storage = new JsonlSessionStorage(fs, metadataFromHeader(headerResult.value, path, fileInfo.mtimeMs));\n\t\tfor (let index = 1; index < physicalLines.length; index++) {\n\t\t\tconst line = physicalLines[index]!;\n\t\t\tconst mutationResult = parseMutation(line);\n\t\t\tif (!mutationResult.ok) {\n\t\t\t\tconst isTornTail = index === physicalLines.length - 1 && mutationResult.error.kind === \"syntax\";\n\t\t\t\tif (isTornTail) {\n\t\t\t\t\t// Drop the unacknowledged partial append by atomically publishing the valid prefix.\n\t\t\t\t\tconst validPrefix = `${physicalLines.slice(0, index).join(\"\\n\")}\\n`;\n\t\t\t\t\tawait publishFileAtomically(fs, path, async (tempPath) => {\n\t\t\t\t\t\tfileResult(await fs.writeFile(tempPath, validPrefix), `Failed to stage torn-tail repair ${path}`);\n\t\t\t\t\t});\n\t\t\t\t\treturn storage;\n\t\t\t\t}\n\t\t\t\tthrow invalidFile(path, index + 1, mutationResult.error);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tstorage.applyMutation(mutationResult.value);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof SessionError && error.code === \"invalid_entry\") {\n\t\t\t\t\tthrow invalidFile(path, index + 1, error);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\tif (!content.endsWith(\"\\n\")) {\n\t\t\tfileResult(await fs.appendFile(path, \"\\n\"), `Failed to repair unterminated session tail ${path}`);\n\t\t}\n\t\treturn storage;\n\t}\n\n\tasync fork(path: string, header: JsonlV4Header, options: ForkOptions): Promise<JsonlSessionStorage> {\n\t\tconst mutations = this.state.createForkMutations(options);\n\t\tawait publishFileAtomically(this.fs, path, async (tempPath) => {\n\t\t\tconst targetStorage = await JsonlSessionStorage.create(this.fs, tempPath, header);\n\t\t\tfor (const mutation of mutations) {\n\t\t\t\tawait targetStorage.appendMutation(mutation);\n\t\t\t\ttargetStorage.applyMutation(mutation);\n\t\t\t}\n\t\t});\n\t\treturn JsonlSessionStorage.load(this.fs, path);\n\t}\n\n\tasync drain(): Promise<void> {\n\t\tawait this.tail;\n\t}\n\n\tasync getMetadata(): Promise<JsonlSessionMetadata> {\n\t\treturn structuredClone(this.metadata);\n\t}\n\n\tasync getLanes(): Promise<LanePointer[]> {\n\t\treturn this.state.getLanes();\n\t}\n\n\tcreateLane(lane: string, at: string | null): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.state.validateNewLane(lane);\n\t\t\tthis.state.validateTarget(at);\n\t\t\tconst mutation: SessionMutation = { kind: \"lane\", seq: this.state.nextSequence, lane, leafId: at };\n\t\t\tawait this.appendMutation(mutation);\n\t\t\tthis.applyMutation(mutation);\n\t\t});\n\t}\n\n\tmoveLane(lane: string, to: string | null): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.state.requireLane(lane);\n\t\t\tthis.state.validateTarget(to);\n\t\t\tconst mutation: SessionMutation = { kind: \"lane\", seq: this.state.nextSequence, lane, leafId: to };\n\t\t\tawait this.appendMutation(mutation);\n\t\t\tthis.applyMutation(mutation);\n\t\t});\n\t}\n\n\tappendEntry<TEntry extends Entry>(newEntry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst parentId = this.state.requireLane(lane);\n\t\t\tthis.state.validateUnusedId(newEntry.id);\n\t\t\tconst entry = {\n\t\t\t\t...structuredClone(newEntry),\n\t\t\t\tparentId,\n\t\t\t\tseq: this.state.nextSequence,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t} as unknown as TEntry;\n\t\t\tconst mutation: SessionMutation = { kind: \"entry\", lane, entry };\n\t\t\tawait this.appendMutation(mutation);\n\t\t\tthis.applyMutation(mutation);\n\t\t\treturn structuredClone(entry);\n\t\t});\n\t}\n\n\tappendRecord<TRecord extends LaneRecord>(newRecord: NewRecord<TRecord>): Promise<TRecord> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.state.requireLane(newRecord.lane);\n\t\t\tthis.state.validateUnusedId(newRecord.id);\n\t\t\tconst currentOpenOperationId = this.state.findOpenOperations(newRecord.lane, { limit: 1 })[0]?.id;\n\t\t\tif (newRecord.type === \"operation_started\" && currentOpenOperationId !== undefined) {\n\t\t\t\tthrow new SessionError(\n\t\t\t\t\t\"storage\",\n\t\t\t\t\t`Lane ${newRecord.lane} already has an open operation ${currentOpenOperationId}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst record = {\n\t\t\t\t...structuredClone(newRecord),\n\t\t\t\tseq: this.state.nextSequence,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t} as unknown as TRecord;\n\t\t\tconst mutation: SessionMutation = { kind: \"record\", record };\n\t\t\tawait this.appendMutation(mutation);\n\t\t\tthis.applyMutation(mutation);\n\t\t\treturn structuredClone(record);\n\t\t});\n\t}\n\n\tasync getEntry(id: string): Promise<Entry | undefined> {\n\t\tconst entry = this.state.getEntry(id);\n\t\treturn entry === undefined ? undefined : structuredClone(entry);\n\t}\n\n\tasync findEntries(query: EntryQuery = {}): Promise<Entry[]> {\n\t\treturn structuredClone(this.state.findEntries(query));\n\t}\n\n\tasync findEntriesOnBranch(query: EntryQuery & BranchBounds & { start: string }): Promise<Entry[]> {\n\t\treturn structuredClone(this.state.findEntriesOnBranch(query));\n\t}\n\n\tasync findRecords<K extends LaneRecord[\"type\"]>(\n\t\tquery: RecordQuery & { type: K },\n\t): Promise<Extract<LaneRecord, { type: K }>[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]>;\n\tasync findRecords(query: RecordQuery = {}): Promise<LaneRecord[]> {\n\t\treturn structuredClone(this.state.findRecords(query));\n\t}\n\n\tasync findOpenOperations(lane: string, options?: { limit?: number }): Promise<OperationStartedRecord[]> {\n\t\treturn structuredClone(this.state.findOpenOperations(lane, options));\n\t}\n\n\tasync getLog(options: LogOptions = {}): Promise<LogItem[]> {\n\t\treturn structuredClone(this.state.getLog(options));\n\t}\n\n\tasync getName(): Promise<string | undefined> {\n\t\treturn this.state.getName();\n\t}\n\n\tsetName(name: string | undefined): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tconst mutation: SessionMutation = { kind: \"fact\", seq: this.state.nextSequence, fact: \"name\", name };\n\t\t\tawait this.appendMutation(mutation);\n\t\t\tthis.applyMutation(mutation);\n\t\t});\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.state.getLabel(id);\n\t}\n\n\tsetLabel(id: string, label: string | undefined): Promise<void> {\n\t\treturn this.enqueue(async () => {\n\t\t\tthis.state.validateTarget(id);\n\t\t\tconst mutation: SessionMutation = {\n\t\t\t\tkind: \"fact\",\n\t\t\t\tseq: this.state.nextSequence,\n\t\t\t\tfact: \"label\",\n\t\t\t\ttargetId: id,\n\t\t\t\tlabel,\n\t\t\t};\n\t\t\tawait this.appendMutation(mutation);\n\t\t\tthis.applyMutation(mutation);\n\t\t});\n\t}\n\n\tasync getStats(): Promise<SessionStats> {\n\t\treturn structuredClone(this.state.getStats());\n\t}\n\n\tprivate enqueue<T>(operation: () => Promise<T>): Promise<T> {\n\t\tconst result = this.tail.then(operation);\n\t\tthis.tail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined,\n\t\t);\n\t\treturn result;\n\t}\n\n\tprivate async appendMutation(mutation: SessionMutation): Promise<void> {\n\t\tfileResult(\n\t\t\tawait this.fs.appendFile(this.metadata.path, encodeMutation(mutation)),\n\t\t\t`Failed to append session ${this.metadata.path}`,\n\t\t);\n\t}\n\n\tprivate applyMutation(mutation: SessionMutation): void {\n\t\tthis.state.applyMutation(mutation);\n\t}\n}\n"]}