@earendil-works/pi-agent-core 0.84.3 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/README.md +4 -0
  2. package/dist/agent-loop.d.ts.map +1 -1
  3. package/dist/agent-loop.js +32 -25
  4. package/dist/agent-loop.js.map +1 -1
  5. package/dist/harness/agent-harness.d.ts +639 -384
  6. package/dist/harness/agent-harness.d.ts.map +1 -1
  7. package/dist/harness/agent-harness.js +5 -251
  8. package/dist/harness/agent-harness.js.map +1 -1
  9. package/dist/harness/compaction/branch-summarization.d.ts +12 -6
  10. package/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
  11. package/dist/harness/compaction/branch-summarization.js +33 -26
  12. package/dist/harness/compaction/branch-summarization.js.map +1 -1
  13. package/dist/harness/compaction/compaction.d.ts +30 -8
  14. package/dist/harness/compaction/compaction.d.ts.map +1 -1
  15. package/dist/harness/compaction/compaction.js +53 -63
  16. package/dist/harness/compaction/compaction.js.map +1 -1
  17. package/dist/harness/config.d.ts +9 -0
  18. package/dist/harness/config.d.ts.map +1 -0
  19. package/dist/harness/config.js +27 -0
  20. package/dist/harness/config.js.map +1 -0
  21. package/dist/harness/context.d.ts +9 -0
  22. package/dist/harness/context.d.ts.map +1 -0
  23. package/dist/harness/context.js +13 -0
  24. package/dist/harness/context.js.map +1 -0
  25. package/dist/harness/env/nodejs.d.ts +24 -28
  26. package/dist/harness/env/nodejs.d.ts.map +1 -1
  27. package/dist/harness/env/nodejs.js +242 -87
  28. package/dist/harness/env/nodejs.js.map +1 -1
  29. package/dist/harness/events.d.ts +20 -38
  30. package/dist/harness/events.d.ts.map +1 -1
  31. package/dist/harness/events.js +240 -55
  32. package/dist/harness/events.js.map +1 -1
  33. package/dist/harness/execution/assistant.d.ts +42 -0
  34. package/dist/harness/execution/assistant.d.ts.map +1 -0
  35. package/dist/harness/execution/assistant.js +82 -0
  36. package/dist/harness/execution/assistant.js.map +1 -0
  37. package/dist/harness/execution/effect-gate.d.ts +22 -0
  38. package/dist/harness/execution/effect-gate.d.ts.map +1 -0
  39. package/dist/harness/execution/effect-gate.js +49 -0
  40. package/dist/harness/execution/effect-gate.js.map +1 -0
  41. package/dist/harness/execution/tools.d.ts +67 -0
  42. package/dist/harness/execution/tools.d.ts.map +1 -0
  43. package/dist/harness/execution/tools.js +121 -0
  44. package/dist/harness/execution/tools.js.map +1 -0
  45. package/dist/harness/hooks.d.ts +38 -0
  46. package/dist/harness/hooks.d.ts.map +1 -0
  47. package/dist/harness/hooks.js +406 -0
  48. package/dist/harness/hooks.js.map +1 -0
  49. package/dist/harness/messages.d.ts +2 -2
  50. package/dist/harness/messages.d.ts.map +1 -1
  51. package/dist/harness/messages.js.map +1 -1
  52. package/dist/harness/prompt-templates.d.ts +3 -2
  53. package/dist/harness/prompt-templates.d.ts.map +1 -1
  54. package/dist/harness/prompt-templates.js +17 -17
  55. package/dist/harness/prompt-templates.js.map +1 -1
  56. package/dist/harness/result.d.ts +93 -0
  57. package/dist/harness/result.d.ts.map +1 -1
  58. package/dist/harness/result.js +40 -0
  59. package/dist/harness/result.js.map +1 -1
  60. package/dist/harness/runtime/drive/boundary.d.ts +28 -0
  61. package/dist/harness/runtime/drive/boundary.d.ts.map +1 -0
  62. package/dist/harness/runtime/drive/boundary.js +170 -0
  63. package/dist/harness/runtime/drive/boundary.js.map +1 -0
  64. package/dist/harness/runtime/drive/checkpoint.d.ts +8 -0
  65. package/dist/harness/runtime/drive/checkpoint.d.ts.map +1 -0
  66. package/dist/harness/runtime/drive/checkpoint.js +133 -0
  67. package/dist/harness/runtime/drive/checkpoint.js.map +1 -0
  68. package/dist/harness/runtime/drive/deferred.d.ts +15 -0
  69. package/dist/harness/runtime/drive/deferred.d.ts.map +1 -0
  70. package/dist/harness/runtime/drive/deferred.js +179 -0
  71. package/dist/harness/runtime/drive/deferred.js.map +1 -0
  72. package/dist/harness/runtime/drive/generation.d.ts +8 -0
  73. package/dist/harness/runtime/drive/generation.d.ts.map +1 -0
  74. package/dist/harness/runtime/drive/generation.js +205 -0
  75. package/dist/harness/runtime/drive/generation.js.map +1 -0
  76. package/dist/harness/runtime/drive/reconcile.d.ts +5 -0
  77. package/dist/harness/runtime/drive/reconcile.d.ts.map +1 -0
  78. package/dist/harness/runtime/drive/reconcile.js +140 -0
  79. package/dist/harness/runtime/drive/reconcile.js.map +1 -0
  80. package/dist/harness/runtime/drive/recovery.d.ts +8 -0
  81. package/dist/harness/runtime/drive/recovery.d.ts.map +1 -0
  82. package/dist/harness/runtime/drive/recovery.js +85 -0
  83. package/dist/harness/runtime/drive/recovery.js.map +1 -0
  84. package/dist/harness/runtime/drive/response.d.ts +23 -0
  85. package/dist/harness/runtime/drive/response.d.ts.map +1 -0
  86. package/dist/harness/runtime/drive/response.js +394 -0
  87. package/dist/harness/runtime/drive/response.js.map +1 -0
  88. package/dist/harness/runtime/drive/retry.d.ts +4 -0
  89. package/dist/harness/runtime/drive/retry.d.ts.map +1 -0
  90. package/dist/harness/runtime/drive/retry.js +37 -0
  91. package/dist/harness/runtime/drive/retry.js.map +1 -0
  92. package/dist/harness/runtime/drive/structural.d.ts +32 -0
  93. package/dist/harness/runtime/drive/structural.d.ts.map +1 -0
  94. package/dist/harness/runtime/drive/structural.js +929 -0
  95. package/dist/harness/runtime/drive/structural.js.map +1 -0
  96. package/dist/harness/runtime/drive/terminal.d.ts +7 -0
  97. package/dist/harness/runtime/drive/terminal.d.ts.map +1 -0
  98. package/dist/harness/runtime/drive/terminal.js +49 -0
  99. package/dist/harness/runtime/drive/terminal.js.map +1 -0
  100. package/dist/harness/runtime/drive/tool-placement.d.ts +14 -0
  101. package/dist/harness/runtime/drive/tool-placement.d.ts.map +1 -0
  102. package/dist/harness/runtime/drive/tool-placement.js +244 -0
  103. package/dist/harness/runtime/drive/tool-placement.js.map +1 -0
  104. package/dist/harness/runtime/drive/tools.d.ts +6 -0
  105. package/dist/harness/runtime/drive/tools.d.ts.map +1 -0
  106. package/dist/harness/runtime/drive/tools.js +449 -0
  107. package/dist/harness/runtime/drive/tools.js.map +1 -0
  108. package/dist/harness/runtime/drive.d.ts +6 -0
  109. package/dist/harness/runtime/drive.d.ts.map +1 -0
  110. package/dist/harness/runtime/drive.js +93 -0
  111. package/dist/harness/runtime/drive.js.map +1 -0
  112. package/dist/harness/runtime/harness.d.ts +59 -0
  113. package/dist/harness/runtime/harness.d.ts.map +1 -0
  114. package/dist/harness/runtime/harness.js +311 -0
  115. package/dist/harness/runtime/harness.js.map +1 -0
  116. package/dist/harness/runtime/index.d.ts +2 -0
  117. package/dist/harness/runtime/index.d.ts.map +1 -0
  118. package/dist/harness/runtime/index.js +2 -0
  119. package/dist/harness/runtime/index.js.map +1 -0
  120. package/dist/harness/runtime/lane.d.ts +101 -0
  121. package/dist/harness/runtime/lane.d.ts.map +1 -0
  122. package/dist/harness/runtime/lane.js +1575 -0
  123. package/dist/harness/runtime/lane.js.map +1 -0
  124. package/dist/harness/runtime/progress.d.ts +15 -0
  125. package/dist/harness/runtime/progress.d.ts.map +1 -0
  126. package/dist/harness/runtime/progress.js +66 -0
  127. package/dist/harness/runtime/progress.js.map +1 -0
  128. package/dist/harness/runtime/reducer.d.ts +5 -0
  129. package/dist/harness/runtime/reducer.d.ts.map +1 -0
  130. package/dist/harness/runtime/reducer.js +240 -0
  131. package/dist/harness/runtime/reducer.js.map +1 -0
  132. package/dist/harness/runtime/restore.d.ts +24 -0
  133. package/dist/harness/runtime/restore.d.ts.map +1 -0
  134. package/dist/harness/runtime/restore.js +117 -0
  135. package/dist/harness/runtime/restore.js.map +1 -0
  136. package/dist/harness/runtime/transcript.d.ts +21 -0
  137. package/dist/harness/runtime/transcript.d.ts.map +1 -0
  138. package/dist/harness/runtime/transcript.js +72 -0
  139. package/dist/harness/runtime/transcript.js.map +1 -0
  140. package/dist/harness/runtime/types.d.ts +105 -0
  141. package/dist/harness/runtime/types.d.ts.map +1 -0
  142. package/dist/harness/runtime/types.js +61 -0
  143. package/dist/harness/runtime/types.js.map +1 -0
  144. package/dist/harness/session/commit.d.ts +53 -0
  145. package/dist/harness/session/commit.d.ts.map +1 -0
  146. package/dist/harness/session/commit.js +57 -0
  147. package/dist/harness/session/commit.js.map +1 -0
  148. package/dist/harness/session/context.d.ts +6 -18
  149. package/dist/harness/session/context.d.ts.map +1 -1
  150. package/dist/harness/session/context.js +31 -49
  151. package/dist/harness/session/context.js.map +1 -1
  152. package/dist/harness/session/fork-policy.d.ts +13 -0
  153. package/dist/harness/session/fork-policy.d.ts.map +1 -0
  154. package/dist/harness/session/fork-policy.js +22 -0
  155. package/dist/harness/session/fork-policy.js.map +1 -0
  156. package/dist/harness/session/fork.d.ts +18 -0
  157. package/dist/harness/session/fork.d.ts.map +1 -0
  158. package/dist/harness/session/fork.js +127 -0
  159. package/dist/harness/session/fork.js.map +1 -0
  160. package/dist/harness/session/in-memory-storage-state.d.ts +39 -0
  161. package/dist/harness/session/in-memory-storage-state.d.ts.map +1 -0
  162. package/dist/harness/session/in-memory-storage-state.js +216 -0
  163. package/dist/harness/session/in-memory-storage-state.js.map +1 -0
  164. package/dist/harness/session/index.d.ts +9 -5
  165. package/dist/harness/session/index.d.ts.map +1 -1
  166. package/dist/harness/session/index.js +7 -4
  167. package/dist/harness/session/index.js.map +1 -1
  168. package/dist/harness/session/jsonl/codec.d.ts +19 -8
  169. package/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  170. package/dist/harness/session/jsonl/codec.js +34 -218
  171. package/dist/harness/session/jsonl/codec.js.map +1 -1
  172. package/dist/harness/session/jsonl/index.d.ts +4 -0
  173. package/dist/harness/session/jsonl/index.d.ts.map +1 -0
  174. package/dist/harness/session/jsonl/index.js +4 -0
  175. package/dist/harness/session/jsonl/index.js.map +1 -0
  176. package/dist/harness/session/jsonl/legacy-v3.d.ts +18 -0
  177. package/dist/harness/session/jsonl/legacy-v3.d.ts.map +1 -0
  178. package/dist/harness/session/jsonl/legacy-v3.js +383 -0
  179. package/dist/harness/session/jsonl/legacy-v3.js.map +1 -0
  180. package/dist/harness/session/jsonl/repo.d.ts +26 -19
  181. package/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  182. package/dist/harness/session/jsonl/repo.js +266 -156
  183. package/dist/harness/session/jsonl/repo.js.map +1 -1
  184. package/dist/harness/session/jsonl/storage.d.ts +39 -41
  185. package/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  186. package/dist/harness/session/jsonl/storage.js +238 -181
  187. package/dist/harness/session/jsonl/storage.js.map +1 -1
  188. package/dist/harness/session/jsonl/types.d.ts +23 -22
  189. package/dist/harness/session/jsonl/types.d.ts.map +1 -1
  190. package/dist/harness/session/jsonl/types.js +2 -1
  191. package/dist/harness/session/jsonl/types.js.map +1 -1
  192. package/dist/harness/session/memory.d.ts +46 -40
  193. package/dist/harness/session/memory.d.ts.map +1 -1
  194. package/dist/harness/session/memory.js +346 -113
  195. package/dist/harness/session/memory.js.map +1 -1
  196. package/dist/harness/session/mutation-line.d.ts +8 -0
  197. package/dist/harness/session/mutation-line.d.ts.map +1 -0
  198. package/dist/harness/session/mutation-line.js +21 -0
  199. package/dist/harness/session/mutation-line.js.map +1 -0
  200. package/dist/harness/session/session.d.ts +78 -46
  201. package/dist/harness/session/session.d.ts.map +1 -1
  202. package/dist/harness/session/session.js +343 -209
  203. package/dist/harness/session/session.js.map +1 -1
  204. package/dist/harness/session/testing/benchmark/datasets.d.ts +12 -0
  205. package/dist/harness/session/testing/benchmark/datasets.d.ts.map +1 -0
  206. package/dist/harness/session/testing/benchmark/datasets.js +24 -0
  207. package/dist/harness/session/testing/benchmark/datasets.js.map +1 -0
  208. package/dist/harness/session/testing/benchmark/session-repo.d.ts +42 -0
  209. package/dist/harness/session/testing/benchmark/session-repo.d.ts.map +1 -0
  210. package/dist/harness/session/testing/benchmark/session-repo.js +127 -0
  211. package/dist/harness/session/testing/benchmark/session-repo.js.map +1 -0
  212. package/dist/harness/session/testing/benchmark/storage.d.ts +25 -0
  213. package/dist/harness/session/testing/benchmark/storage.d.ts.map +1 -0
  214. package/dist/harness/session/testing/benchmark/storage.js +114 -0
  215. package/dist/harness/session/testing/benchmark/storage.js.map +1 -0
  216. package/dist/harness/session/testing/conformance/session-repo.d.ts +21 -0
  217. package/dist/harness/session/testing/conformance/session-repo.d.ts.map +1 -0
  218. package/dist/harness/session/testing/conformance/session-repo.js +590 -0
  219. package/dist/harness/session/testing/conformance/session-repo.js.map +1 -0
  220. package/dist/harness/session/testing/conformance/storage.d.ts +4 -0
  221. package/dist/harness/session/testing/conformance/storage.d.ts.map +1 -0
  222. package/dist/harness/session/testing/conformance/storage.js +600 -0
  223. package/dist/harness/session/testing/conformance/storage.js.map +1 -0
  224. package/dist/harness/session/testing/gating-storage.d.ts +26 -0
  225. package/dist/harness/session/testing/gating-storage.d.ts.map +1 -0
  226. package/dist/harness/session/testing/gating-storage.js +100 -0
  227. package/dist/harness/session/testing/gating-storage.js.map +1 -0
  228. package/dist/harness/session/testing/index.d.ts +9 -2
  229. package/dist/harness/session/testing/index.d.ts.map +1 -1
  230. package/dist/harness/session/testing/index.js +8 -1
  231. package/dist/harness/session/testing/index.js.map +1 -1
  232. package/dist/harness/session/testing/instrumented-storage.d.ts +11 -0
  233. package/dist/harness/session/testing/instrumented-storage.d.ts.map +1 -0
  234. package/dist/harness/session/testing/instrumented-storage.js +16 -0
  235. package/dist/harness/session/testing/instrumented-storage.js.map +1 -0
  236. package/dist/harness/session/testing/storage-decorator.d.ts +20 -0
  237. package/dist/harness/session/testing/storage-decorator.d.ts.map +1 -0
  238. package/dist/harness/session/testing/storage-decorator.js +41 -0
  239. package/dist/harness/session/testing/storage-decorator.js.map +1 -0
  240. package/dist/harness/session/testing/types.d.ts +5 -7
  241. package/dist/harness/session/testing/types.d.ts.map +1 -1
  242. package/dist/harness/session/testing/types.js.map +1 -1
  243. package/dist/harness/session/types.d.ts +424 -259
  244. package/dist/harness/session/types.d.ts.map +1 -1
  245. package/dist/harness/session/types.js +7 -8
  246. package/dist/harness/session/types.js.map +1 -1
  247. package/dist/harness/session/values.d.ts +95 -0
  248. package/dist/harness/session/values.d.ts.map +1 -0
  249. package/dist/harness/session/values.js +81 -0
  250. package/dist/harness/session/values.js.map +1 -0
  251. package/dist/harness/skills.d.ts +3 -2
  252. package/dist/harness/skills.d.ts.map +1 -1
  253. package/dist/harness/skills.js +29 -26
  254. package/dist/harness/skills.js.map +1 -1
  255. package/dist/harness/telemetry.d.ts +64 -43
  256. package/dist/harness/telemetry.d.ts.map +1 -1
  257. package/dist/harness/telemetry.js +45 -26
  258. package/dist/harness/telemetry.js.map +1 -1
  259. package/dist/harness/tools/bash.d.ts +4 -4
  260. package/dist/harness/tools/bash.d.ts.map +1 -1
  261. package/dist/harness/tools/bash.js +66 -93
  262. package/dist/harness/tools/bash.js.map +1 -1
  263. package/dist/harness/tools/edit.d.ts.map +1 -1
  264. package/dist/harness/tools/edit.js +10 -10
  265. package/dist/harness/tools/edit.js.map +1 -1
  266. package/dist/harness/tools/file-mutation-queue.d.ts +2 -1
  267. package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -1
  268. package/dist/harness/tools/file-mutation-queue.js +5 -5
  269. package/dist/harness/tools/file-mutation-queue.js.map +1 -1
  270. package/dist/harness/tools/path-utils.d.ts +3 -2
  271. package/dist/harness/tools/path-utils.d.ts.map +1 -1
  272. package/dist/harness/tools/path-utils.js +5 -5
  273. package/dist/harness/tools/path-utils.js.map +1 -1
  274. package/dist/harness/tools/read.d.ts +2 -1
  275. package/dist/harness/tools/read.d.ts.map +1 -1
  276. package/dist/harness/tools/read.js +4 -6
  277. package/dist/harness/tools/read.js.map +1 -1
  278. package/dist/harness/tools/write.d.ts.map +1 -1
  279. package/dist/harness/tools/write.js +7 -7
  280. package/dist/harness/tools/write.js.map +1 -1
  281. package/dist/harness/types.d.ts +108 -45
  282. package/dist/harness/types.d.ts.map +1 -1
  283. package/dist/harness/types.js.map +1 -1
  284. package/dist/harness/utils/adaptive-publisher.d.ts +24 -0
  285. package/dist/harness/utils/adaptive-publisher.d.ts.map +1 -0
  286. package/dist/harness/utils/adaptive-publisher.js +79 -0
  287. package/dist/harness/utils/adaptive-publisher.js.map +1 -0
  288. package/dist/harness/utils/output-capture.d.ts +31 -0
  289. package/dist/harness/utils/output-capture.d.ts.map +1 -0
  290. package/dist/harness/utils/output-capture.js +217 -0
  291. package/dist/harness/utils/output-capture.js.map +1 -0
  292. package/dist/harness/utils/shell-output.d.ts +12 -5
  293. package/dist/harness/utils/shell-output.d.ts.map +1 -1
  294. package/dist/harness/utils/shell-output.js +64 -174
  295. package/dist/harness/utils/shell-output.js.map +1 -1
  296. package/dist/harness/utils/truncate.d.ts +1 -0
  297. package/dist/harness/utils/truncate.d.ts.map +1 -1
  298. package/dist/harness/utils/truncate.js +1 -1
  299. package/dist/harness/utils/truncate.js.map +1 -1
  300. package/dist/harness/utils/usage.d.ts +4 -0
  301. package/dist/harness/utils/usage.d.ts.map +1 -0
  302. package/dist/harness/utils/usage.js +33 -0
  303. package/dist/harness/utils/usage.js.map +1 -0
  304. package/dist/index.d.ts +4 -1
  305. package/dist/index.d.ts.map +1 -1
  306. package/dist/index.js +3 -6
  307. package/dist/index.js.map +1 -1
  308. package/dist/proxy.d.ts +2 -0
  309. package/dist/proxy.d.ts.map +1 -1
  310. package/dist/proxy.js +40 -10
  311. package/dist/proxy.js.map +1 -1
  312. package/dist/search/index.d.ts +24 -16
  313. package/dist/search/index.d.ts.map +1 -1
  314. package/dist/search/index.js +1 -1
  315. package/dist/search/index.js.map +1 -1
  316. package/dist/types.d.ts +5 -2
  317. package/dist/types.d.ts.map +1 -1
  318. package/dist/types.js.map +1 -1
  319. package/package.json +25 -4
  320. package/dist/harness/reducer.d.ts +0 -100
  321. package/dist/harness/reducer.d.ts.map +0 -1
  322. package/dist/harness/reducer.js +0 -415
  323. package/dist/harness/reducer.js.map +0 -1
  324. package/dist/harness/session/jsonl/errors.d.ts +0 -9
  325. package/dist/harness/session/jsonl/errors.d.ts.map +0 -1
  326. package/dist/harness/session/jsonl/errors.js +0 -19
  327. package/dist/harness/session/jsonl/errors.js.map +0 -1
  328. package/dist/harness/session/jsonl.d.ts +0 -3
  329. package/dist/harness/session/jsonl.d.ts.map +0 -1
  330. package/dist/harness/session/jsonl.js +0 -2
  331. package/dist/harness/session/jsonl.js.map +0 -1
  332. package/dist/harness/session/state.d.ts +0 -65
  333. package/dist/harness/session/state.d.ts.map +0 -1
  334. package/dist/harness/session/state.js +0 -319
  335. package/dist/harness/session/state.js.map +0 -1
  336. package/dist/harness/session/testing/conformance.d.ts +0 -4
  337. package/dist/harness/session/testing/conformance.d.ts.map +0 -1
  338. package/dist/harness/session/testing/conformance.js +0 -762
  339. package/dist/harness/session/testing/conformance.js.map +0 -1
  340. package/dist/search/scanning.d.ts +0 -29
  341. package/dist/search/scanning.d.ts.map +0 -1
  342. package/dist/search/scanning.js +0 -103
  343. package/dist/search/scanning.js.map +0 -1
@@ -0,0 +1,383 @@
1
+ import { uuidv7 } from "@earendil-works/pi-ai/utils/uuid";
2
+ import { createBranchSummaryMessage, createCompactionSummaryMessage } from "../../messages.js";
3
+ import { addUsage, emptyUsage } from "../../utils/usage.js";
4
+ import { branchTip, entryLabel, laneConfig, laneState, sessionName } from "../values.js";
5
+ import { parseJsonlSessionHeader } from "./codec.js";
6
+ import { JSONL_FORMAT_VERSION, JSONL_STORAGE_VERSION, } from "./types.js";
7
+ async function resolveLegacyV3ParentSessionId(fileSystem, parentSessionPath, context) {
8
+ const lines = await fileSystem.readTextLines(parentSessionPath, { maxLines: 1 }, context);
9
+ if (!lines.ok || lines.value[0] === undefined)
10
+ return undefined;
11
+ const parsed = parseJsonlSessionHeader(lines.value[0]);
12
+ return parsed.ok ? parsed.value.header.id : undefined;
13
+ }
14
+ export async function metadataFromLegacyV3Header(fileSystem, header, context) {
15
+ const metadata = {
16
+ id: header.id,
17
+ createdAt: Date.parse(header.timestamp),
18
+ storageVersion: JSONL_STORAGE_VERSION,
19
+ cwd: header.cwd,
20
+ };
21
+ if (header.parentSession !== undefined) {
22
+ const parentSessionId = await resolveLegacyV3ParentSessionId(fileSystem, header.parentSession, context);
23
+ if (parentSessionId !== undefined)
24
+ metadata.parentSessionId = parentSessionId;
25
+ else
26
+ metadata.legacyParentSessionPath = header.parentSession;
27
+ }
28
+ return metadata;
29
+ }
30
+ export async function normalizeLegacyV3Header(fileSystem, header, context) {
31
+ return {
32
+ v: JSONL_FORMAT_VERSION,
33
+ kind: "header",
34
+ ...(await metadataFromLegacyV3Header(fileSystem, header, context)),
35
+ };
36
+ }
37
+ function parseLegacyV3Entry(line, lineNumber) {
38
+ let entry;
39
+ try {
40
+ entry = JSON.parse(line);
41
+ }
42
+ catch (error) {
43
+ throw new Error(`Invalid legacy v3 JSONL record at line ${lineNumber}: not valid JSON`, { cause: error });
44
+ }
45
+ const recordType = entry.type;
46
+ if (recordType !== "message" &&
47
+ recordType !== "custom" &&
48
+ recordType !== "custom_message" &&
49
+ recordType !== "branch_summary" &&
50
+ recordType !== "compaction" &&
51
+ recordType !== "model_change" &&
52
+ recordType !== "thinking_level_change" &&
53
+ recordType !== "active_tools_change" &&
54
+ recordType !== "session_info" &&
55
+ recordType !== "label") {
56
+ throw new Error(`Unsupported legacy v3 record type at line ${lineNumber}: ${String(recordType)}`);
57
+ }
58
+ return entry;
59
+ }
60
+ function importedCustomMessage(entry) {
61
+ const message = {
62
+ role: "custom",
63
+ customType: entry.customType,
64
+ content: entry.content,
65
+ details: entry.details,
66
+ display: entry.display,
67
+ timestamp: Date.parse(entry.timestamp),
68
+ };
69
+ // The coding-agent CustomAgentMessages declaration merge is not visible in this package.
70
+ return message;
71
+ }
72
+ function isRetainedEntry(entry) {
73
+ return (entry.type !== "model_change" &&
74
+ entry.type !== "thinking_level_change" &&
75
+ entry.type !== "active_tools_change" &&
76
+ entry.type !== "session_info" &&
77
+ entry.type !== "label");
78
+ }
79
+ class RetainedIdResolver {
80
+ /** Caches the reminted ID of each discarded legacy node's nearest retained ancestor, or null. */
81
+ resolvedIds = new Map();
82
+ entriesById;
83
+ remintedIds;
84
+ /**
85
+ * @param entriesById Complete inventory of legacy physical nodes, including discarded nodes.
86
+ * @param remintedIds Legacy-to-current ID map containing retained nodes only.
87
+ */
88
+ constructor(entriesById, remintedIds) {
89
+ this.entriesById = entriesById;
90
+ this.remintedIds = remintedIds;
91
+ }
92
+ /**
93
+ * Resolve a legacy node to its reminted ID, or to its nearest retained ancestor when discarded.
94
+ * Returns null when the reference is null or no retained ancestor exists.
95
+ */
96
+ resolve(legacyId) {
97
+ const traversedIds = [];
98
+ const visitedIds = new Set();
99
+ let currentId = legacyId;
100
+ let resolvedId = null;
101
+ while (currentId !== null) {
102
+ const remintedId = this.remintedIds.get(currentId);
103
+ if (remintedId !== undefined) {
104
+ resolvedId = remintedId;
105
+ break;
106
+ }
107
+ if (this.resolvedIds.has(currentId)) {
108
+ resolvedId = this.resolvedIds.get(currentId) ?? null;
109
+ break;
110
+ }
111
+ if (visitedIds.has(currentId))
112
+ throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);
113
+ visitedIds.add(currentId);
114
+ const entry = this.entriesById.get(currentId);
115
+ if (entry === undefined)
116
+ throw new Error(`Missing legacy v3 entry reference: ${currentId}`);
117
+ traversedIds.push(currentId);
118
+ currentId = entry.parentId;
119
+ }
120
+ for (const traversedId of traversedIds)
121
+ this.resolvedIds.set(traversedId, resolvedId);
122
+ return resolvedId;
123
+ }
124
+ }
125
+ function requireRetainedId(resolver, legacyId) {
126
+ const importedId = resolver.resolve(legacyId);
127
+ if (importedId === null)
128
+ throw new Error(`Legacy v3 entry reference has no retained ancestor: ${legacyId}`);
129
+ return importedId;
130
+ }
131
+ function resolveBranchSummaryFromId(resolver, legacyFromId) {
132
+ // Legacy branchWithSummary() encoded a root source as the "root" sentinel instead of null.
133
+ return legacyFromId === "root" ? null : resolver.resolve(legacyFromId);
134
+ }
135
+ function projectContextMessages(entry, resolver) {
136
+ switch (entry.type) {
137
+ case "message":
138
+ return [entry.message];
139
+ case "custom_message":
140
+ return [importedCustomMessage(entry)];
141
+ case "branch_summary":
142
+ return entry.summary
143
+ ? [
144
+ createBranchSummaryMessage(entry.summary, resolveBranchSummaryFromId(resolver, entry.fromId), entry.timestamp),
145
+ ]
146
+ : [];
147
+ case "compaction":
148
+ return [createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp)];
149
+ case "custom":
150
+ case "model_change":
151
+ case "thinking_level_change":
152
+ case "active_tools_change":
153
+ case "session_info":
154
+ case "label":
155
+ return [];
156
+ }
157
+ }
158
+ function materializeRetainedTail(compaction, entriesById, resolver) {
159
+ const reversedTail = [];
160
+ const visited = new Set();
161
+ let currentId = compaction.parentId;
162
+ while (currentId !== null) {
163
+ if (visited.has(currentId))
164
+ throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);
165
+ visited.add(currentId);
166
+ const entry = entriesById.get(currentId);
167
+ if (entry === undefined)
168
+ throw new Error(`Missing legacy v3 parent entry: ${currentId}`);
169
+ reversedTail.push(entry);
170
+ if (currentId === compaction.firstKeptEntryId) {
171
+ return reversedTail.reverse().flatMap((tailEntry) => projectContextMessages(tailEntry, resolver));
172
+ }
173
+ currentId = entry.parentId;
174
+ }
175
+ throw new Error(`Legacy v3 compaction ${compaction.id} firstKeptEntryId is not on its parent branch: ${compaction.firstKeptEntryId}`);
176
+ }
177
+ function normalizeRetainedEntry(entry, seq, entriesById, resolver) {
178
+ const committedBase = {
179
+ kind: "entry",
180
+ id: requireRetainedId(resolver, entry.id),
181
+ parentId: resolver.resolve(entry.parentId),
182
+ seq,
183
+ timestamp: Date.parse(entry.timestamp),
184
+ };
185
+ if (entry.type === "message") {
186
+ return { ...committedBase, type: "message", message: entry.message };
187
+ }
188
+ if (entry.type === "custom_message") {
189
+ return {
190
+ ...committedBase,
191
+ type: "message",
192
+ message: importedCustomMessage(entry),
193
+ };
194
+ }
195
+ if (entry.type === "branch_summary") {
196
+ return {
197
+ ...committedBase,
198
+ type: "branch_summary",
199
+ fromId: resolveBranchSummaryFromId(resolver, entry.fromId),
200
+ summary: entry.summary,
201
+ details: entry.details,
202
+ usage: entry.usage,
203
+ fromHook: entry.fromHook ?? false,
204
+ };
205
+ }
206
+ if (entry.type === "compaction") {
207
+ return {
208
+ ...committedBase,
209
+ type: "compaction",
210
+ summary: entry.summary,
211
+ retainedTail: materializeRetainedTail(entry, entriesById, resolver),
212
+ tokensBefore: entry.tokensBefore,
213
+ details: entry.details,
214
+ usage: entry.usage,
215
+ fromHook: entry.fromHook ?? false,
216
+ };
217
+ }
218
+ return {
219
+ ...committedBase,
220
+ type: "custom",
221
+ customType: entry.customType,
222
+ data: entry.data,
223
+ };
224
+ }
225
+ const THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
226
+ function selectedConfiguration(entriesById, selectedId) {
227
+ let model;
228
+ let thinkingLevel;
229
+ let activeToolNames;
230
+ let sawModel = false;
231
+ let sawThinkingLevel = false;
232
+ let sawActiveToolNames = false;
233
+ const visited = new Set();
234
+ let currentId = selectedId;
235
+ while (currentId !== null && (!sawModel || !sawThinkingLevel || !sawActiveToolNames)) {
236
+ if (visited.has(currentId))
237
+ throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);
238
+ visited.add(currentId);
239
+ const entry = entriesById.get(currentId);
240
+ if (entry === undefined)
241
+ throw new Error(`Missing legacy v3 entry reference: ${currentId}`);
242
+ if (entry.type === "model_change" && !sawModel) {
243
+ sawModel = true;
244
+ if (typeof entry.provider === "string" &&
245
+ entry.provider.length !== 0 &&
246
+ typeof entry.modelId === "string" &&
247
+ entry.modelId.length !== 0) {
248
+ model = { provider: entry.provider, modelId: entry.modelId };
249
+ }
250
+ }
251
+ else if (entry.type === "thinking_level_change" && !sawThinkingLevel) {
252
+ sawThinkingLevel = true;
253
+ if (typeof entry.thinkingLevel === "string" && THINKING_LEVELS.has(entry.thinkingLevel)) {
254
+ thinkingLevel = entry.thinkingLevel;
255
+ }
256
+ }
257
+ else if (entry.type === "active_tools_change" && !sawActiveToolNames) {
258
+ sawActiveToolNames = true;
259
+ if (Array.isArray(entry.activeToolNames) && entry.activeToolNames.every((name) => typeof name === "string")) {
260
+ activeToolNames = [...entry.activeToolNames];
261
+ }
262
+ }
263
+ currentId = entry.parentId;
264
+ }
265
+ if (model === undefined || thinkingLevel === undefined)
266
+ return undefined;
267
+ return { model, thinkingLevel, activeToolNames: activeToolNames ?? [] };
268
+ }
269
+ function normalizeLegacyV3Values(entries, entriesById, resolver, firstSeq) {
270
+ const writes = [];
271
+ let latestSessionInfo;
272
+ for (const entry of entries) {
273
+ if (entry.type === "session_info")
274
+ latestSessionInfo = entry;
275
+ }
276
+ if (latestSessionInfo?.name) {
277
+ writes.push({
278
+ kind: "value",
279
+ op: "set",
280
+ seq: firstSeq + writes.length,
281
+ namespace: sessionName.namespace,
282
+ key: sessionName.key,
283
+ value: latestSessionInfo.name,
284
+ });
285
+ }
286
+ const labels = new Map();
287
+ for (const entry of entries) {
288
+ if (entry.type !== "label")
289
+ continue;
290
+ const targetId = resolver.resolve(entry.targetId);
291
+ // Labels have no current address when their target has no retained ancestor.
292
+ if (targetId === null)
293
+ continue;
294
+ // Legacy v3 treated both undefined and the empty string as clearing a label.
295
+ if (entry.label)
296
+ labels.set(targetId, entry.label);
297
+ else
298
+ labels.delete(targetId);
299
+ }
300
+ for (const [targetId, label] of labels) {
301
+ const address = entryLabel(targetId);
302
+ writes.push({
303
+ kind: "value",
304
+ op: "set",
305
+ seq: firstSeq + writes.length,
306
+ namespace: address.namespace,
307
+ key: address.key,
308
+ value: label,
309
+ });
310
+ }
311
+ const finalEntry = entries.at(-1);
312
+ const tipAddress = branchTip("main");
313
+ writes.push({
314
+ kind: "value",
315
+ op: "set",
316
+ seq: firstSeq + writes.length,
317
+ namespace: tipAddress.namespace,
318
+ key: tipAddress.key,
319
+ value: finalEntry === undefined ? null : resolver.resolve(finalEntry.id),
320
+ });
321
+ const configuration = selectedConfiguration(entriesById, finalEntry?.id ?? null);
322
+ if (configuration !== undefined) {
323
+ const configAddress = laneConfig("main");
324
+ writes.push({
325
+ kind: "value",
326
+ op: "set",
327
+ seq: firstSeq + writes.length,
328
+ namespace: configAddress.namespace,
329
+ key: configAddress.key,
330
+ value: configuration,
331
+ });
332
+ const stateAddress = laneState("main");
333
+ writes.push({
334
+ kind: "value",
335
+ op: "set",
336
+ seq: firstSeq + writes.length,
337
+ namespace: stateAddress.namespace,
338
+ key: stateAddress.key,
339
+ value: { currentOperationId: null, lastOperationId: null, inbox: [] },
340
+ });
341
+ }
342
+ return writes;
343
+ }
344
+ function aggregateImportedUsage(entries) {
345
+ let aggregate = emptyUsage();
346
+ for (const entry of entries) {
347
+ let usage;
348
+ if (entry.type === "message") {
349
+ if (entry.message.role === "assistant")
350
+ usage = entry.message.usage;
351
+ else if (entry.message.role === "toolResult")
352
+ usage = entry.message.usage;
353
+ }
354
+ else if (entry.type === "compaction" || entry.type === "branch_summary") {
355
+ usage = entry.usage;
356
+ }
357
+ if (usage !== undefined)
358
+ aggregate = addUsage(aggregate, usage);
359
+ }
360
+ return aggregate;
361
+ }
362
+ /** Normalize the currently supported v3 records without touching their source file. */
363
+ export function normalizeLegacyV3Records(recordLines) {
364
+ const entries = recordLines.map((line, index) => parseLegacyV3Entry(line, index + 2));
365
+ const entriesById = new Map();
366
+ for (const entry of entries) {
367
+ if (entriesById.has(entry.id))
368
+ throw new Error(`Duplicate legacy v3 entry id: ${entry.id}`);
369
+ entriesById.set(entry.id, entry);
370
+ }
371
+ const retainedEntries = entries.filter(isRetainedEntry);
372
+ const remintedIds = new Map(retainedEntries.map((entry) => [entry.id, uuidv7(Date.parse(entry.timestamp))]));
373
+ const resolver = new RetainedIdResolver(entriesById, remintedIds);
374
+ const entryWrites = retainedEntries.map((entry, index) => normalizeRetainedEntry(entry, index + 1, entriesById, resolver));
375
+ const valueWrites = normalizeLegacyV3Values(entries, entriesById, resolver, entryWrites.length + 1);
376
+ const writes = [...entryWrites, ...valueWrites];
377
+ return {
378
+ writes,
379
+ importedUsage: aggregateImportedUsage(entries),
380
+ nextSeq: writes.length + 1,
381
+ };
382
+ }
383
+ //# sourceMappingURL=legacy-v3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-v3.js","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/legacy-v3.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ImageContent, TextContent, Usage } from \"@earendil-works/pi-ai\";\nimport { uuidv7 } from \"@earendil-works/pi-ai/utils/uuid\";\nimport type { AgentMessage, ThinkingLevel } from \"../../../types.ts\";\nimport type { Context } from \"../../context.ts\";\nimport { createBranchSummaryMessage, createCompactionSummaryMessage } from \"../../messages.ts\";\nimport type { FileSystem } from \"../../types.ts\";\nimport { addUsage, emptyUsage } from \"../../utils/usage.ts\";\nimport type { CommittedEntryWrite, CommittedValueSetWrite, CommittedWrite } from \"../commit.ts\";\nimport type { JsonValue, LaneConfiguration } from \"../types.ts\";\nimport { branchTip, entryLabel, laneConfig, laneState, sessionName } from \"../values.ts\";\nimport { type LegacyV3SessionHeader, parseJsonlSessionHeader } from \"./codec.ts\";\nimport {\n\tJSONL_FORMAT_VERSION,\n\tJSONL_STORAGE_VERSION,\n\ttype JsonlSessionMetadata,\n\ttype JsonlStorageHeader,\n} from \"./types.ts\";\n\ninterface LegacyV3EntryBase {\n\ttype: string;\n\tid: string;\n\tparentId: string | null;\n\ttimestamp: string;\n}\n\ninterface LegacyV3MessageEntry extends LegacyV3EntryBase {\n\ttype: \"message\";\n\tmessage: AgentMessage;\n}\n\ninterface LegacyV3CustomEntry extends LegacyV3EntryBase {\n\ttype: \"custom\";\n\tcustomType: string;\n\tdata?: JsonValue;\n}\n\ninterface LegacyV3CustomMessageEntry extends LegacyV3EntryBase {\n\ttype: \"custom_message\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tdisplay: boolean;\n}\n\ninterface LegacyV3BranchSummaryEntry extends LegacyV3EntryBase {\n\ttype: \"branch_summary\";\n\tfromId: string;\n\tsummary: string;\n\tdetails?: JsonValue;\n\tusage?: Usage;\n\tfromHook?: boolean;\n}\n\ninterface LegacyV3CompactionEntry extends LegacyV3EntryBase {\n\ttype: \"compaction\";\n\tsummary: string;\n\tfirstKeptEntryId: string;\n\ttokensBefore: number;\n\tdetails?: JsonValue;\n\tusage?: Usage;\n\tfromHook?: boolean;\n}\n\ninterface LegacyV3ModelChangeEntry extends LegacyV3EntryBase {\n\ttype: \"model_change\";\n\tprovider: string;\n\tmodelId: string;\n}\n\ninterface LegacyV3ThinkingLevelChangeEntry extends LegacyV3EntryBase {\n\ttype: \"thinking_level_change\";\n\tthinkingLevel: string;\n}\n\ninterface LegacyV3ActiveToolsChangeEntry extends LegacyV3EntryBase {\n\ttype: \"active_tools_change\";\n\tactiveToolNames: string[];\n}\n\ninterface LegacyV3SessionInfoEntry extends LegacyV3EntryBase {\n\ttype: \"session_info\";\n\tname?: string;\n}\n\ninterface LegacyV3LabelEntry extends LegacyV3EntryBase {\n\ttype: \"label\";\n\ttargetId: string;\n\tlabel?: string;\n}\n\ninterface ImportedCustomMessage {\n\trole: \"custom\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdetails?: unknown;\n\tdisplay: boolean;\n\ttimestamp: number;\n}\n\ntype RetainedLegacyV3Entry =\n\t| LegacyV3MessageEntry\n\t| LegacyV3CustomEntry\n\t| LegacyV3CustomMessageEntry\n\t| LegacyV3BranchSummaryEntry\n\t| LegacyV3CompactionEntry;\n\ntype DiscardedLegacyV3Entry =\n\t| LegacyV3ModelChangeEntry\n\t| LegacyV3ThinkingLevelChangeEntry\n\t| LegacyV3ActiveToolsChangeEntry\n\t| LegacyV3SessionInfoEntry\n\t| LegacyV3LabelEntry;\n\ntype LegacyV3Entry = RetainedLegacyV3Entry | DiscardedLegacyV3Entry;\n\nexport interface NormalizedLegacyV3Records {\n\twrites: CommittedWrite[];\n\timportedUsage: Usage;\n\tnextSeq: number;\n}\n\ntype JsonlSessionMetadataBase = Omit<JsonlSessionMetadata, \"path\" | \"modifiedAt\">;\n\nasync function resolveLegacyV3ParentSessionId(\n\tfileSystem: FileSystem,\n\tparentSessionPath: string,\n\tcontext: Context,\n): Promise<string | undefined> {\n\tconst lines = await fileSystem.readTextLines(parentSessionPath, { maxLines: 1 }, context);\n\tif (!lines.ok || lines.value[0] === undefined) return undefined;\n\tconst parsed = parseJsonlSessionHeader(lines.value[0]);\n\treturn parsed.ok ? parsed.value.header.id : undefined;\n}\n\nexport async function metadataFromLegacyV3Header(\n\tfileSystem: FileSystem,\n\theader: LegacyV3SessionHeader,\n\tcontext: Context,\n): Promise<JsonlSessionMetadataBase> {\n\tconst metadata: JsonlSessionMetadataBase = {\n\t\tid: header.id,\n\t\tcreatedAt: Date.parse(header.timestamp),\n\t\tstorageVersion: JSONL_STORAGE_VERSION,\n\t\tcwd: header.cwd,\n\t};\n\tif (header.parentSession !== undefined) {\n\t\tconst parentSessionId = await resolveLegacyV3ParentSessionId(fileSystem, header.parentSession, context);\n\t\tif (parentSessionId !== undefined) metadata.parentSessionId = parentSessionId;\n\t\telse metadata.legacyParentSessionPath = header.parentSession;\n\t}\n\treturn metadata;\n}\n\nexport async function normalizeLegacyV3Header(\n\tfileSystem: FileSystem,\n\theader: LegacyV3SessionHeader,\n\tcontext: Context,\n): Promise<JsonlStorageHeader> {\n\treturn {\n\t\tv: JSONL_FORMAT_VERSION,\n\t\tkind: \"header\",\n\t\t...(await metadataFromLegacyV3Header(fileSystem, header, context)),\n\t};\n}\n\nfunction parseLegacyV3Entry(line: string, lineNumber: number): LegacyV3Entry {\n\tlet entry: LegacyV3Entry;\n\ttry {\n\t\tentry = JSON.parse(line) as LegacyV3Entry;\n\t} catch (error) {\n\t\tthrow new Error(`Invalid legacy v3 JSONL record at line ${lineNumber}: not valid JSON`, { cause: error });\n\t}\n\tconst recordType: unknown = entry.type;\n\tif (\n\t\trecordType !== \"message\" &&\n\t\trecordType !== \"custom\" &&\n\t\trecordType !== \"custom_message\" &&\n\t\trecordType !== \"branch_summary\" &&\n\t\trecordType !== \"compaction\" &&\n\t\trecordType !== \"model_change\" &&\n\t\trecordType !== \"thinking_level_change\" &&\n\t\trecordType !== \"active_tools_change\" &&\n\t\trecordType !== \"session_info\" &&\n\t\trecordType !== \"label\"\n\t) {\n\t\tthrow new Error(`Unsupported legacy v3 record type at line ${lineNumber}: ${String(recordType)}`);\n\t}\n\treturn entry;\n}\n\nfunction importedCustomMessage(entry: LegacyV3CustomMessageEntry): AgentMessage {\n\tconst message: ImportedCustomMessage = {\n\t\trole: \"custom\",\n\t\tcustomType: entry.customType,\n\t\tcontent: entry.content,\n\t\tdetails: entry.details,\n\t\tdisplay: entry.display,\n\t\ttimestamp: Date.parse(entry.timestamp),\n\t};\n\t// The coding-agent CustomAgentMessages declaration merge is not visible in this package.\n\treturn message as unknown as AgentMessage;\n}\n\nfunction isRetainedEntry(entry: LegacyV3Entry): entry is RetainedLegacyV3Entry {\n\treturn (\n\t\tentry.type !== \"model_change\" &&\n\t\tentry.type !== \"thinking_level_change\" &&\n\t\tentry.type !== \"active_tools_change\" &&\n\t\tentry.type !== \"session_info\" &&\n\t\tentry.type !== \"label\"\n\t);\n}\n\nclass RetainedIdResolver {\n\t/** Caches the reminted ID of each discarded legacy node's nearest retained ancestor, or null. */\n\tprivate resolvedIds = new Map<string, string | null>();\n\tprivate entriesById: Map<string, LegacyV3Entry>;\n\tprivate remintedIds: Map<string, string>;\n\n\t/**\n\t * @param entriesById Complete inventory of legacy physical nodes, including discarded nodes.\n\t * @param remintedIds Legacy-to-current ID map containing retained nodes only.\n\t */\n\tconstructor(entriesById: Map<string, LegacyV3Entry>, remintedIds: Map<string, string>) {\n\t\tthis.entriesById = entriesById;\n\t\tthis.remintedIds = remintedIds;\n\t}\n\n\t/**\n\t * Resolve a legacy node to its reminted ID, or to its nearest retained ancestor when discarded.\n\t * Returns null when the reference is null or no retained ancestor exists.\n\t */\n\tresolve(legacyId: string | null): string | null {\n\t\tconst traversedIds: string[] = [];\n\t\tconst visitedIds = new Set<string>();\n\t\tlet currentId = legacyId;\n\t\tlet resolvedId: string | null = null;\n\n\t\twhile (currentId !== null) {\n\t\t\tconst remintedId = this.remintedIds.get(currentId);\n\t\t\tif (remintedId !== undefined) {\n\t\t\t\tresolvedId = remintedId;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (this.resolvedIds.has(currentId)) {\n\t\t\t\tresolvedId = this.resolvedIds.get(currentId) ?? null;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (visitedIds.has(currentId)) throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);\n\t\t\tvisitedIds.add(currentId);\n\t\t\tconst entry = this.entriesById.get(currentId);\n\t\t\tif (entry === undefined) throw new Error(`Missing legacy v3 entry reference: ${currentId}`);\n\t\t\ttraversedIds.push(currentId);\n\t\t\tcurrentId = entry.parentId;\n\t\t}\n\n\t\tfor (const traversedId of traversedIds) this.resolvedIds.set(traversedId, resolvedId);\n\t\treturn resolvedId;\n\t}\n}\n\nfunction requireRetainedId(resolver: RetainedIdResolver, legacyId: string): string {\n\tconst importedId = resolver.resolve(legacyId);\n\tif (importedId === null) throw new Error(`Legacy v3 entry reference has no retained ancestor: ${legacyId}`);\n\treturn importedId;\n}\n\nfunction resolveBranchSummaryFromId(resolver: RetainedIdResolver, legacyFromId: string): string | null {\n\t// Legacy branchWithSummary() encoded a root source as the \"root\" sentinel instead of null.\n\treturn legacyFromId === \"root\" ? null : resolver.resolve(legacyFromId);\n}\n\nfunction projectContextMessages(entry: LegacyV3Entry, resolver: RetainedIdResolver): AgentMessage[] {\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\treturn [entry.message];\n\t\tcase \"custom_message\":\n\t\t\treturn [importedCustomMessage(entry)];\n\t\tcase \"branch_summary\":\n\t\t\treturn entry.summary\n\t\t\t\t? [\n\t\t\t\t\t\tcreateBranchSummaryMessage(\n\t\t\t\t\t\t\tentry.summary,\n\t\t\t\t\t\t\tresolveBranchSummaryFromId(resolver, entry.fromId),\n\t\t\t\t\t\t\tentry.timestamp,\n\t\t\t\t\t\t),\n\t\t\t\t\t]\n\t\t\t\t: [];\n\t\tcase \"compaction\":\n\t\t\treturn [createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp)];\n\t\tcase \"custom\":\n\t\tcase \"model_change\":\n\t\tcase \"thinking_level_change\":\n\t\tcase \"active_tools_change\":\n\t\tcase \"session_info\":\n\t\tcase \"label\":\n\t\t\treturn [];\n\t}\n}\n\nfunction materializeRetainedTail(\n\tcompaction: LegacyV3CompactionEntry,\n\tentriesById: ReadonlyMap<string, LegacyV3Entry>,\n\tresolver: RetainedIdResolver,\n): AgentMessage[] {\n\tconst reversedTail: LegacyV3Entry[] = [];\n\tconst visited = new Set<string>();\n\tlet currentId = compaction.parentId;\n\twhile (currentId !== null) {\n\t\tif (visited.has(currentId)) throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);\n\t\tvisited.add(currentId);\n\t\tconst entry = entriesById.get(currentId);\n\t\tif (entry === undefined) throw new Error(`Missing legacy v3 parent entry: ${currentId}`);\n\t\treversedTail.push(entry);\n\t\tif (currentId === compaction.firstKeptEntryId) {\n\t\t\treturn reversedTail.reverse().flatMap((tailEntry) => projectContextMessages(tailEntry, resolver));\n\t\t}\n\t\tcurrentId = entry.parentId;\n\t}\n\tthrow new Error(\n\t\t`Legacy v3 compaction ${compaction.id} firstKeptEntryId is not on its parent branch: ${compaction.firstKeptEntryId}`,\n\t);\n}\n\nfunction normalizeRetainedEntry(\n\tentry: RetainedLegacyV3Entry,\n\tseq: number,\n\tentriesById: ReadonlyMap<string, LegacyV3Entry>,\n\tresolver: RetainedIdResolver,\n): CommittedEntryWrite {\n\tconst committedBase = {\n\t\tkind: \"entry\" as const,\n\t\tid: requireRetainedId(resolver, entry.id),\n\t\tparentId: resolver.resolve(entry.parentId),\n\t\tseq,\n\t\ttimestamp: Date.parse(entry.timestamp),\n\t};\n\tif (entry.type === \"message\") {\n\t\treturn { ...committedBase, type: \"message\", message: entry.message };\n\t}\n\tif (entry.type === \"custom_message\") {\n\t\treturn {\n\t\t\t...committedBase,\n\t\t\ttype: \"message\",\n\t\t\tmessage: importedCustomMessage(entry),\n\t\t};\n\t}\n\tif (entry.type === \"branch_summary\") {\n\t\treturn {\n\t\t\t...committedBase,\n\t\t\ttype: \"branch_summary\",\n\t\t\tfromId: resolveBranchSummaryFromId(resolver, entry.fromId),\n\t\t\tsummary: entry.summary,\n\t\t\tdetails: entry.details,\n\t\t\tusage: entry.usage,\n\t\t\tfromHook: entry.fromHook ?? false,\n\t\t};\n\t}\n\tif (entry.type === \"compaction\") {\n\t\treturn {\n\t\t\t...committedBase,\n\t\t\ttype: \"compaction\",\n\t\t\tsummary: entry.summary,\n\t\t\tretainedTail: materializeRetainedTail(entry, entriesById, resolver),\n\t\t\ttokensBefore: entry.tokensBefore,\n\t\t\tdetails: entry.details,\n\t\t\tusage: entry.usage,\n\t\t\tfromHook: entry.fromHook ?? false,\n\t\t};\n\t}\n\treturn {\n\t\t...committedBase,\n\t\ttype: \"custom\",\n\t\tcustomType: entry.customType,\n\t\tdata: entry.data,\n\t};\n}\n\nconst THINKING_LEVELS = new Set<ThinkingLevel>([\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\n\nfunction selectedConfiguration(\n\tentriesById: ReadonlyMap<string, LegacyV3Entry>,\n\tselectedId: string | null,\n): LaneConfiguration | undefined {\n\tlet model: LaneConfiguration[\"model\"] | undefined;\n\tlet thinkingLevel: ThinkingLevel | undefined;\n\tlet activeToolNames: string[] | undefined;\n\tlet sawModel = false;\n\tlet sawThinkingLevel = false;\n\tlet sawActiveToolNames = false;\n\tconst visited = new Set<string>();\n\tlet currentId = selectedId;\n\twhile (currentId !== null && (!sawModel || !sawThinkingLevel || !sawActiveToolNames)) {\n\t\tif (visited.has(currentId)) throw new Error(`Cycle in legacy v3 parent chain at entry: ${currentId}`);\n\t\tvisited.add(currentId);\n\t\tconst entry = entriesById.get(currentId);\n\t\tif (entry === undefined) throw new Error(`Missing legacy v3 entry reference: ${currentId}`);\n\t\tif (entry.type === \"model_change\" && !sawModel) {\n\t\t\tsawModel = true;\n\t\t\tif (\n\t\t\t\ttypeof entry.provider === \"string\" &&\n\t\t\t\tentry.provider.length !== 0 &&\n\t\t\t\ttypeof entry.modelId === \"string\" &&\n\t\t\t\tentry.modelId.length !== 0\n\t\t\t) {\n\t\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t\t}\n\t\t} else if (entry.type === \"thinking_level_change\" && !sawThinkingLevel) {\n\t\t\tsawThinkingLevel = true;\n\t\t\tif (typeof entry.thinkingLevel === \"string\" && THINKING_LEVELS.has(entry.thinkingLevel as ThinkingLevel)) {\n\t\t\t\tthinkingLevel = entry.thinkingLevel as ThinkingLevel;\n\t\t\t}\n\t\t} else if (entry.type === \"active_tools_change\" && !sawActiveToolNames) {\n\t\t\tsawActiveToolNames = true;\n\t\t\tif (Array.isArray(entry.activeToolNames) && entry.activeToolNames.every((name) => typeof name === \"string\")) {\n\t\t\t\tactiveToolNames = [...entry.activeToolNames];\n\t\t\t}\n\t\t}\n\t\tcurrentId = entry.parentId;\n\t}\n\tif (model === undefined || thinkingLevel === undefined) return undefined;\n\treturn { model, thinkingLevel, activeToolNames: activeToolNames ?? [] };\n}\n\nfunction normalizeLegacyV3Values(\n\tentries: readonly LegacyV3Entry[],\n\tentriesById: ReadonlyMap<string, LegacyV3Entry>,\n\tresolver: RetainedIdResolver,\n\tfirstSeq: number,\n): CommittedValueSetWrite[] {\n\tconst writes: CommittedValueSetWrite[] = [];\n\tlet latestSessionInfo: LegacyV3SessionInfoEntry | undefined;\n\tfor (const entry of entries) {\n\t\tif (entry.type === \"session_info\") latestSessionInfo = entry;\n\t}\n\tif (latestSessionInfo?.name) {\n\t\twrites.push({\n\t\t\tkind: \"value\",\n\t\t\top: \"set\",\n\t\t\tseq: firstSeq + writes.length,\n\t\t\tnamespace: sessionName.namespace,\n\t\t\tkey: sessionName.key,\n\t\t\tvalue: latestSessionInfo.name,\n\t\t});\n\t}\n\n\tconst labels = new Map<string, string>();\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"label\") continue;\n\t\tconst targetId = resolver.resolve(entry.targetId);\n\t\t// Labels have no current address when their target has no retained ancestor.\n\t\tif (targetId === null) continue;\n\t\t// Legacy v3 treated both undefined and the empty string as clearing a label.\n\t\tif (entry.label) labels.set(targetId, entry.label);\n\t\telse labels.delete(targetId);\n\t}\n\tfor (const [targetId, label] of labels) {\n\t\tconst address = entryLabel(targetId);\n\t\twrites.push({\n\t\t\tkind: \"value\",\n\t\t\top: \"set\",\n\t\t\tseq: firstSeq + writes.length,\n\t\t\tnamespace: address.namespace,\n\t\t\tkey: address.key,\n\t\t\tvalue: label,\n\t\t});\n\t}\n\n\tconst finalEntry = entries.at(-1);\n\tconst tipAddress = branchTip(\"main\");\n\twrites.push({\n\t\tkind: \"value\",\n\t\top: \"set\",\n\t\tseq: firstSeq + writes.length,\n\t\tnamespace: tipAddress.namespace,\n\t\tkey: tipAddress.key,\n\t\tvalue: finalEntry === undefined ? null : resolver.resolve(finalEntry.id),\n\t});\n\tconst configuration = selectedConfiguration(entriesById, finalEntry?.id ?? null);\n\tif (configuration !== undefined) {\n\t\tconst configAddress = laneConfig(\"main\");\n\t\twrites.push({\n\t\t\tkind: \"value\",\n\t\t\top: \"set\",\n\t\t\tseq: firstSeq + writes.length,\n\t\t\tnamespace: configAddress.namespace,\n\t\t\tkey: configAddress.key,\n\t\t\tvalue: configuration,\n\t\t});\n\t\tconst stateAddress = laneState(\"main\");\n\t\twrites.push({\n\t\t\tkind: \"value\",\n\t\t\top: \"set\",\n\t\t\tseq: firstSeq + writes.length,\n\t\t\tnamespace: stateAddress.namespace,\n\t\t\tkey: stateAddress.key,\n\t\t\tvalue: { currentOperationId: null, lastOperationId: null, inbox: [] },\n\t\t});\n\t}\n\treturn writes;\n}\n\nfunction aggregateImportedUsage(entries: readonly LegacyV3Entry[]): Usage {\n\tlet aggregate = emptyUsage();\n\tfor (const entry of entries) {\n\t\tlet usage: Usage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tif (entry.message.role === \"assistant\") usage = entry.message.usage;\n\t\t\telse if (entry.message.role === \"toolResult\") usage = entry.message.usage;\n\t\t} else if (entry.type === \"compaction\" || entry.type === \"branch_summary\") {\n\t\t\tusage = entry.usage;\n\t\t}\n\t\tif (usage !== undefined) aggregate = addUsage(aggregate, usage);\n\t}\n\treturn aggregate;\n}\n\n/** Normalize the currently supported v3 records without touching their source file. */\nexport function normalizeLegacyV3Records(recordLines: readonly string[]): NormalizedLegacyV3Records {\n\tconst entries = recordLines.map((line, index) => parseLegacyV3Entry(line, index + 2));\n\tconst entriesById = new Map<string, LegacyV3Entry>();\n\tfor (const entry of entries) {\n\t\tif (entriesById.has(entry.id)) throw new Error(`Duplicate legacy v3 entry id: ${entry.id}`);\n\t\tentriesById.set(entry.id, entry);\n\t}\n\tconst retainedEntries = entries.filter(isRetainedEntry);\n\tconst remintedIds = new Map(retainedEntries.map((entry) => [entry.id, uuidv7(Date.parse(entry.timestamp))]));\n\tconst resolver = new RetainedIdResolver(entriesById, remintedIds);\n\tconst entryWrites = retainedEntries.map((entry, index) =>\n\t\tnormalizeRetainedEntry(entry, index + 1, entriesById, resolver),\n\t);\n\tconst valueWrites = normalizeLegacyV3Values(entries, entriesById, resolver, entryWrites.length + 1);\n\tconst writes: CommittedWrite[] = [...entryWrites, ...valueWrites];\n\treturn {\n\t\twrites,\n\t\timportedUsage: aggregateImportedUsage(entries),\n\t\tnextSeq: writes.length + 1,\n\t};\n}\n"]}
@@ -1,27 +1,34 @@
1
- import { Session } from "../session.ts";
2
- import { type ForkOptions, type SessionRepo } from "../types.ts";
3
- import { JsonlSessionStorage } from "./storage.ts";
4
- import type { JsonlSessionCreateOptions, JsonlSessionListOptions, JsonlSessionMetadata, JsonlSessionRepoOptions } from "./types.ts";
5
- export declare function listJsonlSessionMetadata(options: JsonlSessionRepoOptions, query?: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]>;
6
- export declare function loadJsonlSessionStorage(options: JsonlSessionRepoOptions, metadata: JsonlSessionMetadata): Promise<JsonlSessionStorage>;
1
+ import type { Context } from "../../context.ts";
2
+ import type { ForkOptions, Session, SessionRepo } from "../types.ts";
3
+ import { type JsonlSessionCreateOptions, type JsonlSessionListOptions, type JsonlSessionMetadata, type JsonlSessionRepoOptions } from "./types.ts";
4
+ /** File-backed format-4 session repository lifecycle. */
7
5
  export declare class JsonlSessionRepo implements SessionRepo<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions> {
8
- private readonly fs;
6
+ private readonly fileSystem;
9
7
  private readonly sessionsRootInput;
10
- private readonly activeCreateDestinations;
11
- private rootPromise;
8
+ private readonly now;
9
+ private readonly openSessions;
10
+ private readonly pendingCreates;
11
+ private closed;
12
+ private closePromise;
12
13
  constructor(options: JsonlSessionRepoOptions);
13
- create(options: JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>>;
14
- open(metadata: JsonlSessionMetadata): Promise<Session<JsonlSessionMetadata>>;
15
- list(options?: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]>;
16
- delete(metadata: JsonlSessionMetadata): Promise<void>;
17
- fork(source: JsonlSessionMetadata, options: ForkOptions & JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>>;
18
- private loadStorage;
14
+ create(options: JsonlSessionCreateOptions, context: Context): Promise<Session<JsonlSessionMetadata>>;
15
+ open(metadata: JsonlSessionMetadata, context: Context): Promise<Session<JsonlSessionMetadata>>;
16
+ list(options: JsonlSessionListOptions | undefined, context: Context): Promise<JsonlSessionMetadata[]>;
17
+ delete(metadata: JsonlSessionMetadata, context: Context): Promise<void>;
18
+ fork(source: JsonlSessionMetadata, options: ForkOptions, context: Context): Promise<Session<JsonlSessionMetadata>>;
19
+ close(_context: Context): Promise<void>;
19
20
  private resolveCreateDestination;
20
- private claimCreateDestination;
21
- private prepareCreate;
22
- private listDirect;
23
- private sessionIdExists;
21
+ private listDirectory;
22
+ private readSessionMetadata;
23
+ private sessionDirectories;
24
24
  private sessionDirectory;
25
+ private resolveNewSessionPath;
26
+ private assertSessionIdAvailable;
27
+ private loadClosedForkSourceSnapshot;
28
+ private publishOpenSession;
29
+ private sessionKey;
30
+ private loadStorage;
25
31
  private root;
32
+ private assertOpen;
26
33
  }
27
34
  //# sourceMappingURL=repo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/repo.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,OAAO,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,KAAK,WAAW,EAAgB,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EACX,yBAAyB,EACzB,uBAAuB,EACvB,oBAAoB,EAEpB,uBAAuB,EAEvB,MAAM,YAAY,CAAC;AAmDpB,wBAAsB,wBAAwB,CAC7C,OAAO,EAAE,uBAAuB,EAChC,KAAK,GAAE,uBAA4B,GACjC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAmBjC;AAED,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,uBAAuB,EAChC,QAAQ,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAU9B;AAOD,qBAAa,gBACZ,YAAW,WAAW,CAAC,oBAAoB,EAAE,yBAAyB,EAAE,uBAAuB,CAAC;IAEhG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA6B;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAqB;IAC9D,OAAO,CAAC,WAAW,CAA8B;IAEjD,YAAY,OAAO,EAAE,uBAAuB,EAG3C;IAEK,MAAM,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAMvF;IAEK,IAAI,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAEjF;IAEK,IAAI,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAEjF;IAEK,MAAM,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;IAEK,IAAI,CACT,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,WAAW,GAAG,yBAAyB,GAC9C,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAWxC;YAEa,WAAW;YAIX,wBAAwB;YAYxB,sBAAsB;YAgBtB,aAAa;YAgCb,UAAU;YAIV,eAAe;YAQf,gBAAgB;IAO9B,OAAO,CAAC,IAAI;CAMZ","sourcesContent":["import { uuidv7 } from \"@earendil-works/pi-ai\";\nimport { assertJsonSerializable, Session } from \"../session.ts\";\nimport { type ForkOptions, SessionError, type SessionRepo } from \"../types.ts\";\nimport { metadataFromHeader, parseHeader } from \"./codec.ts\";\nimport { fileResult } from \"./errors.ts\";\nimport { JsonlSessionStorage } from \"./storage.ts\";\nimport type {\n\tJsonlSessionCreateOptions,\n\tJsonlSessionListOptions,\n\tJsonlSessionMetadata,\n\tJsonlSessionRepoFileSystem,\n\tJsonlSessionRepoOptions,\n\tJsonlV4Header,\n} from \"./types.ts\";\n\nconst SESSION_ID_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;\n\nfunction validateSessionId(id: string): void {\n\tif (!SESSION_ID_PATTERN.test(id)) {\n\t\tthrow new SessionError(\n\t\t\t\"invalid_payload\",\n\t\t\t\"Session id must be non-empty, contain only alphanumeric characters, '-', '_', and '.', and start and end with an alphanumeric character\",\n\t\t);\n\t}\n}\n\nfunction jsonlSessionDirectoryName(cwd: string): string {\n\treturn `--${cwd.replace(/^[/\\\\]/, \"\").replace(/[/\\\\:]/g, \"-\")}--`;\n}\n\nasync function jsonlSessionsRoot(options: JsonlSessionRepoOptions): Promise<string> {\n\treturn fileResult(\n\t\tawait options.fs.absolutePath(options.sessionsRoot),\n\t\t`Failed to resolve sessions root ${options.sessionsRoot}`,\n\t);\n}\n\nasync function jsonlSessionDirectory(\n\tfs: JsonlSessionRepoFileSystem,\n\tsessionsRoot: string,\n\tcwd: string,\n): Promise<string> {\n\treturn fileResult(\n\t\tawait fs.joinPath([sessionsRoot, jsonlSessionDirectoryName(cwd)]),\n\t\t`Failed to resolve sessions directory for ${cwd}`,\n\t);\n}\n\nasync function jsonlSessionDirectories(options: JsonlSessionRepoOptions, cwd?: string): Promise<string[]> {\n\tconst sessionsRoot = await jsonlSessionsRoot(options);\n\tif (cwd !== undefined) {\n\t\tconst resolvedCwd = fileResult(await options.fs.absolutePath(cwd), `Failed to resolve session cwd ${cwd}`);\n\t\tconst directory = await jsonlSessionDirectory(options.fs, sessionsRoot, resolvedCwd);\n\t\treturn fileResult(await options.fs.exists(directory), `Failed to check sessions directory ${directory}`)\n\t\t\t? [directory]\n\t\t\t: [];\n\t}\n\tif (!fileResult(await options.fs.exists(sessionsRoot), `Failed to check sessions directory ${sessionsRoot}`))\n\t\treturn [];\n\treturn fileResult(await options.fs.listDir(sessionsRoot), `Failed to list sessions directory ${sessionsRoot}`)\n\t\t.filter((entry) => entry.kind === \"directory\" || entry.kind === \"symlink\")\n\t\t.map((entry) => entry.path);\n}\n\nexport async function listJsonlSessionMetadata(\n\toptions: JsonlSessionRepoOptions,\n\tquery: JsonlSessionListOptions = {},\n): Promise<JsonlSessionMetadata[]> {\n\tconst metadata: JsonlSessionMetadata[] = [];\n\tfor (const directory of await jsonlSessionDirectories(options, query.cwd)) {\n\t\tconst files = fileResult(\n\t\t\tawait options.fs.listDir(directory),\n\t\t\t`Failed to list sessions directory ${directory}`,\n\t\t).filter((entry) => entry.kind !== \"directory\" && entry.name.endsWith(\".jsonl\"));\n\t\tfor (const file of files) {\n\t\t\tconst [firstLine] = fileResult(\n\t\t\t\tawait options.fs.readTextLines(file.path, { maxLines: 1 }),\n\t\t\t\t`Failed to read session header ${file.path}`,\n\t\t\t);\n\t\t\tif (!firstLine) continue;\n\t\t\tconst headerResult = parseHeader(firstLine);\n\t\t\tif (!headerResult.ok) continue;\n\t\t\tmetadata.push(metadataFromHeader(headerResult.value, file.path, file.mtimeMs));\n\t\t}\n\t}\n\treturn metadata.sort((left, right) => right.modifiedAt - left.modifiedAt);\n}\n\nexport async function loadJsonlSessionStorage(\n\toptions: JsonlSessionRepoOptions,\n\tmetadata: JsonlSessionMetadata,\n): Promise<JsonlSessionStorage> {\n\tif (!fileResult(await options.fs.exists(metadata.path), `Failed to check session ${metadata.path}`)) {\n\t\tthrow new SessionError(\"not_found\", `Session not found: ${metadata.id}`);\n\t}\n\tconst storage = await JsonlSessionStorage.load(options.fs, metadata.path);\n\tconst loadedMetadata = await storage.getMetadata();\n\tif (loadedMetadata.id !== metadata.id) {\n\t\tthrow new SessionError(\"invalid_entry\", `Session id does not match header: ${metadata.id}`);\n\t}\n\treturn storage;\n}\n\nfunction sessionFileName(createdAt: number, id: string): string {\n\tconst timestamp = new Date(createdAt).toISOString().replace(/[:.]/g, \"-\");\n\treturn `${timestamp}_${id}.jsonl`;\n}\n\nexport class JsonlSessionRepo\n\timplements SessionRepo<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions>\n{\n\tprivate readonly fs: JsonlSessionRepoFileSystem;\n\tprivate readonly sessionsRootInput: string;\n\tprivate readonly activeCreateDestinations = new Set<string>();\n\tprivate rootPromise: Promise<string> | undefined;\n\n\tconstructor(options: JsonlSessionRepoOptions) {\n\t\tthis.fs = options.fs;\n\t\tthis.sessionsRootInput = options.sessionsRoot;\n\t}\n\n\tasync create(options: JsonlSessionCreateOptions): Promise<Session<JsonlSessionMetadata>> {\n\t\tconst destination = await this.resolveCreateDestination(options);\n\t\treturn this.claimCreateDestination(destination, async () => {\n\t\t\tconst { header, path } = await this.prepareCreate(destination, options);\n\t\t\treturn new Session(await JsonlSessionStorage.create(this.fs, path, header));\n\t\t});\n\t}\n\n\tasync open(metadata: JsonlSessionMetadata): Promise<Session<JsonlSessionMetadata>> {\n\t\treturn new Session(await this.loadStorage(metadata));\n\t}\n\n\tasync list(options: JsonlSessionListOptions = {}): Promise<JsonlSessionMetadata[]> {\n\t\treturn this.listDirect(options);\n\t}\n\n\tasync delete(metadata: JsonlSessionMetadata): Promise<void> {\n\t\tfileResult(await this.fs.remove(metadata.path, { force: true }), `Failed to delete session ${metadata.path}`);\n\t}\n\n\tasync fork(\n\t\tsource: JsonlSessionMetadata,\n\t\toptions: ForkOptions & JsonlSessionCreateOptions,\n\t): Promise<Session<JsonlSessionMetadata>> {\n\t\tconst sourceStorage = await this.loadStorage(source);\n\t\tconst createOptions = {\n\t\t\t...options,\n\t\t\tparentSessionId: options.parentSessionId ?? source.id,\n\t\t};\n\t\tconst destination = await this.resolveCreateDestination(createOptions);\n\t\treturn this.claimCreateDestination(destination, async () => {\n\t\t\tconst { header, path } = await this.prepareCreate(destination, createOptions);\n\t\t\treturn new Session(await sourceStorage.fork(path, header, options));\n\t\t});\n\t}\n\n\tprivate async loadStorage(metadata: JsonlSessionMetadata): Promise<JsonlSessionStorage> {\n\t\treturn loadJsonlSessionStorage({ fs: this.fs, sessionsRoot: this.sessionsRootInput }, metadata);\n\t}\n\n\tprivate async resolveCreateDestination(options: JsonlSessionCreateOptions): Promise<{ id: string; cwd: string }> {\n\t\tconst id = options.id ?? uuidv7();\n\t\tvalidateSessionId(id);\n\t\tconst cwd = fileResult(await this.fs.absolutePath(options.cwd), `Failed to resolve session cwd ${options.cwd}`);\n\t\treturn { id, cwd };\n\t}\n\n\t/**\n\t * Prevent same-process create/fork races for one logical destination. The durable filename includes a\n\t * timestamp, so the async filesystem existence check alone can let two concurrent calls both decide the\n\t * same {cwd, id} is free and publish duplicate sessions.\n\t */\n\tprivate async claimCreateDestination<T>(\n\t\tdestination: { id: string; cwd: string },\n\t\toperation: () => Promise<T>,\n\t): Promise<T> {\n\t\tconst key = `${destination.cwd}\\0${destination.id}`;\n\t\tif (this.activeCreateDestinations.has(key)) {\n\t\t\tthrow new SessionError(\"already_exists\", `Session already exists: ${destination.id}`);\n\t\t}\n\t\tthis.activeCreateDestinations.add(key);\n\t\ttry {\n\t\t\treturn await operation();\n\t\t} finally {\n\t\t\tthis.activeCreateDestinations.delete(key);\n\t\t}\n\t}\n\n\tprivate async prepareCreate(\n\t\tdestination: { id: string; cwd: string },\n\t\toptions: JsonlSessionCreateOptions,\n\t): Promise<{\n\t\theader: JsonlV4Header;\n\t\tpath: string;\n\t}> {\n\t\tconst { id, cwd } = destination;\n\t\tif (await this.sessionIdExists(id, cwd)) {\n\t\t\tthrow new SessionError(\"already_exists\", `Session already exists: ${id}`);\n\t\t}\n\n\t\tconst createdAt = Date.now();\n\t\tconst sessionDirectory = await this.sessionDirectory(cwd);\n\t\tconst path = fileResult(\n\t\t\tawait this.fs.joinPath([sessionDirectory, sessionFileName(createdAt, id)]),\n\t\t\t`Failed to resolve path for session ${id}`,\n\t\t);\n\t\tif (options.metadata !== undefined) assertJsonSerializable(options.metadata);\n\t\tconst header: JsonlV4Header = {\n\t\t\tkind: \"header\",\n\t\t\tversion: 4,\n\t\t\tid,\n\t\t\tcreatedAt,\n\t\t\tcwd,\n\t\t\tparentSessionId: options.parentSessionId,\n\t\t\tmetadata: options.metadata,\n\t\t};\n\t\tfileResult(await this.fs.createDir(sessionDirectory, { recursive: true }), `Failed to create sessions directory`);\n\t\treturn { header, path };\n\t}\n\n\tprivate async listDirect(options: JsonlSessionListOptions): Promise<JsonlSessionMetadata[]> {\n\t\treturn listJsonlSessionMetadata({ fs: this.fs, sessionsRoot: this.sessionsRootInput }, options);\n\t}\n\n\tprivate async sessionIdExists(id: string, cwd: string): Promise<boolean> {\n\t\tconst suffix = `_${id}.jsonl`;\n\t\tconst directory = await this.sessionDirectory(cwd);\n\t\tif (!fileResult(await this.fs.exists(directory), `Failed to check sessions directory ${directory}`)) return false;\n\t\tconst files = fileResult(await this.fs.listDir(directory), `Failed to list sessions directory ${directory}`);\n\t\treturn files.some((entry) => entry.kind !== \"directory\" && entry.name.endsWith(suffix));\n\t}\n\n\tprivate async sessionDirectory(cwd: string): Promise<string> {\n\t\treturn fileResult(\n\t\t\tawait this.fs.joinPath([await this.root(), jsonlSessionDirectoryName(cwd)]),\n\t\t\t`Failed to resolve sessions directory for ${cwd}`,\n\t\t);\n\t}\n\n\tprivate root(): Promise<string> {\n\t\tthis.rootPromise ??= this.fs\n\t\t\t.absolutePath(this.sessionsRootInput)\n\t\t\t.then((result) => fileResult(result, `Failed to resolve sessions root ${this.sessionsRootInput}`));\n\t\treturn this.rootPromise;\n\t}\n}\n"]}
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../../../src/harness/session/jsonl/repo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAIhD,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAIrE,OAAO,EAGN,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAE5B,MAAM,YAAY,CAAC;AA+BpB,yDAAyD;AACzD,qBAAa,gBACZ,YAAW,WAAW,CAAC,oBAAoB,EAAE,yBAAyB,EAAE,uBAAuB,CAAC;IAEhG,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAChE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAA4B;IAEhD,YAAY,OAAO,EAAE,uBAAuB,EAI3C;IAEK,MAAM,CAAC,OAAO,EAAE,yBAAyB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CA8BzG;IAEK,IAAI,CAAC,QAAQ,EAAE,oBAAoB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAYnG;IAEK,IAAI,CAAC,OAAO,EAAE,uBAAuB,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAoB1G;IAEK,MAAM,CAAC,QAAQ,EAAE,oBAAoB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5E;IAEK,IAAI,CACT,MAAM,EAAE,oBAAoB,EAC5B,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,OAAO,GACd,OAAO,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,CA2CxC;IAED,KAAK,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAMtC;YAEa,wBAAwB;YAcxB,aAAa;YAwBb,mBAAmB;YAenB,kBAAkB;YAMlB,gBAAgB;YAOhB,qBAAqB;YAarB,wBAAwB;YAcxB,4BAA4B;IAY1C,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,UAAU;YAIJ,WAAW;IA4BzB,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,UAAU;CAGlB","sourcesContent":["import { uuidv7 } from \"@earendil-works/pi-ai/utils/uuid\";\nimport type { Context } from \"../../context.ts\";\nimport type { FileError, FileInfo, FileSystem, Result } from \"../../types.ts\";\nimport { createForkSnapshot, type ForkSourceSnapshot } from \"../fork.ts\";\nimport { StorageBackedSession } from \"../session.ts\";\nimport type { ForkOptions, Session, SessionRepo } from \"../types.ts\";\nimport { parseJsonlSessionHeader } from \"./codec.ts\";\nimport { metadataFromLegacyV3Header } from \"./legacy-v3.ts\";\nimport { JsonlStorage } from \"./storage.ts\";\nimport {\n\tJSONL_FORMAT_VERSION,\n\tJSONL_STORAGE_VERSION,\n\ttype JsonlSessionCreateOptions,\n\ttype JsonlSessionListOptions,\n\ttype JsonlSessionMetadata,\n\ttype JsonlSessionRepoOptions,\n\ttype JsonlStorageHeader,\n} from \"./types.ts\";\n\nfunction fileValue<T>(result: Result<T, FileError>, action: string): T {\n\tif (!result.ok) throw new Error(`${action}: ${result.error.message}`, { cause: result.error });\n\treturn result.value;\n}\n\nfunction metadataFromHeader(header: JsonlStorageHeader, path: string, modifiedAt: number): JsonlSessionMetadata {\n\treturn {\n\t\tid: header.id,\n\t\tcreatedAt: header.createdAt,\n\t\tstorageVersion: header.storageVersion,\n\t\tcwd: header.cwd,\n\t\tpath,\n\t\tmodifiedAt,\n\t\t...(header.parentSessionId === undefined ? {} : { parentSessionId: header.parentSessionId }),\n\t\t...(header.legacyParentSessionPath === undefined\n\t\t\t? {}\n\t\t\t: { legacyParentSessionPath: header.legacyParentSessionPath }),\n\t};\n}\n\nfunction sessionDirectoryName(cwd: string): string {\n\treturn `--${cwd.replace(/^[/\\\\]/, \"\").replace(/[/\\\\:]/g, \"-\")}--`;\n}\n\nfunction sessionFileName(createdAt: number, id: string): string {\n\tconst timestamp = new Date(createdAt).toISOString().replace(/[:.]/g, \"-\");\n\treturn `${timestamp}_${encodeURIComponent(id)}.jsonl`;\n}\n\n/** File-backed format-4 session repository lifecycle. */\nexport class JsonlSessionRepo\n\timplements SessionRepo<JsonlSessionMetadata, JsonlSessionCreateOptions, JsonlSessionListOptions>\n{\n\tprivate readonly fileSystem: FileSystem;\n\tprivate readonly sessionsRootInput: string;\n\tprivate readonly now: () => number;\n\tprivate readonly openSessions = new Map<string, JsonlStorage>();\n\tprivate readonly pendingCreates = new Set<string>();\n\tprivate closed = false;\n\tprivate closePromise: Promise<void> | undefined;\n\n\tconstructor(options: JsonlSessionRepoOptions) {\n\t\tthis.fileSystem = options.fileSystem;\n\t\tthis.sessionsRootInput = options.sessionsRoot;\n\t\tthis.now = options.now ?? Date.now;\n\t}\n\n\tasync create(options: JsonlSessionCreateOptions, context: Context): Promise<Session<JsonlSessionMetadata>> {\n\t\tthis.assertOpen();\n\t\tconst createdAt = this.now();\n\t\tconst { cwd, id } = await this.resolveCreateDestination(options.cwd, options.id, createdAt, context);\n\t\tconst key = this.sessionKey(cwd, id);\n\t\tif (this.openSessions.has(key) || this.pendingCreates.has(key)) throw new Error(`Session already exists: ${id}`);\n\t\tthis.pendingCreates.add(key);\n\t\tlet path: string | undefined;\n\t\tlet storage: JsonlStorage | undefined;\n\t\ttry {\n\t\t\tpath = await this.resolveNewSessionPath(cwd, createdAt, id, context);\n\t\t\tconst header: JsonlStorageHeader = {\n\t\t\t\tv: JSONL_FORMAT_VERSION,\n\t\t\t\tkind: \"header\",\n\t\t\t\tid,\n\t\t\t\tstorageVersion: JSONL_STORAGE_VERSION,\n\t\t\t\tcreatedAt,\n\t\t\t\tcwd,\n\t\t\t\t...(options.parentSessionId === undefined ? {} : { parentSessionId: options.parentSessionId }),\n\t\t\t};\n\t\t\tstorage = await JsonlStorage.create({ fileSystem: this.fileSystem, path, now: this.now }, header, [], context);\n\t\t\tconst info = fileValue(await this.fileSystem.fileInfo(path, context), `Failed to read session ${path}`);\n\t\t\treturn this.publishOpenSession(metadataFromHeader(header, path, info.mtimeMs), storage, key);\n\t\t} catch (error) {\n\t\t\tawait storage?.close(context).catch(() => undefined);\n\t\t\tif (path !== undefined) await this.fileSystem.remove(path, { force: true }, context);\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis.pendingCreates.delete(key);\n\t\t}\n\t}\n\n\tasync open(metadata: JsonlSessionMetadata, context: Context): Promise<Session<JsonlSessionMetadata>> {\n\t\tthis.assertOpen();\n\t\tconst key = this.sessionKey(metadata.cwd, metadata.id);\n\t\tif (this.openSessions.has(key)) throw new Error(`Session is already open: ${metadata.id}`);\n\t\tlet storage: JsonlStorage | undefined;\n\t\ttry {\n\t\t\tstorage = await this.loadStorage(metadata, context);\n\t\t\treturn this.publishOpenSession(metadata, storage, key);\n\t\t} catch (error) {\n\t\t\tawait storage?.close(context);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync list(options: JsonlSessionListOptions | undefined, context: Context): Promise<JsonlSessionMetadata[]> {\n\t\toptions ??= {};\n\t\tthis.assertOpen();\n\t\tconst cwd =\n\t\t\toptions.cwd === undefined\n\t\t\t\t? undefined\n\t\t\t\t: fileValue(\n\t\t\t\t\t\tawait this.fileSystem.absolutePath(options.cwd, context),\n\t\t\t\t\t\t`Failed to resolve session cwd ${options.cwd}`,\n\t\t\t\t\t);\n\t\tconst root = await this.root(context);\n\t\tif (!fileValue(await this.fileSystem.exists(root, context), `Failed to check sessions root ${root}`)) return [];\n\t\tconst directories =\n\t\t\tcwd === undefined ? await this.sessionDirectories(root, context) : [await this.sessionDirectory(cwd, context)];\n\t\tconst metadata: JsonlSessionMetadata[] = [];\n\t\tfor (const directory of directories) metadata.push(...(await this.listDirectory(directory, cwd, context)));\n\t\treturn metadata.sort(\n\t\t\t(left, right) =>\n\t\t\t\tright.createdAt - left.createdAt || left.id.localeCompare(right.id) || left.cwd.localeCompare(right.cwd),\n\t\t);\n\t}\n\n\tasync delete(metadata: JsonlSessionMetadata, context: Context): Promise<void> {\n\t\tthis.assertOpen();\n\t\tconst key = this.sessionKey(metadata.cwd, metadata.id);\n\t\tif (this.openSessions.has(key)) throw new Error(`Session is open: ${metadata.id}`);\n\t\tif (\n\t\t\t!fileValue(await this.fileSystem.exists(metadata.path, context), `Failed to check session ${metadata.path}`)\n\t\t) {\n\t\t\tthrow new Error(`Session file does not exist: ${metadata.path}`);\n\t\t}\n\t\tfileValue(\n\t\t\tawait this.fileSystem.remove(metadata.path, undefined, context),\n\t\t\t`Failed to delete session ${metadata.path}`,\n\t\t);\n\t}\n\n\tasync fork(\n\t\tsource: JsonlSessionMetadata,\n\t\toptions: ForkOptions,\n\t\tcontext: Context,\n\t): Promise<Session<JsonlSessionMetadata>> {\n\t\tthis.assertOpen();\n\t\tconst createdAt = this.now();\n\t\tconst sourceStorage = this.openSessions.get(this.sessionKey(source.cwd, source.id));\n\t\tconst sourceSnapshot = await (sourceStorage === undefined\n\t\t\t? this.loadClosedForkSourceSnapshot(source, context)\n\t\t\t: sourceStorage.captureForkSource(context));\n\t\tconst { cwd, id } = await this.resolveCreateDestination(source.cwd, options.id, createdAt, context);\n\t\tconst destinationKey = this.sessionKey(cwd, id);\n\t\tif (this.openSessions.has(destinationKey) || this.pendingCreates.has(destinationKey)) {\n\t\t\tthrow new Error(`Session already exists: ${id}`);\n\t\t}\n\t\tthis.pendingCreates.add(destinationKey);\n\n\t\tlet path: string | undefined;\n\t\tlet storage: JsonlStorage | undefined;\n\t\ttry {\n\t\t\tpath = await this.resolveNewSessionPath(cwd, createdAt, id, context);\n\t\t\tconst snapshot = createForkSnapshot(sourceSnapshot, options);\n\t\t\tconst header: JsonlStorageHeader = {\n\t\t\t\tv: JSONL_FORMAT_VERSION,\n\t\t\t\tkind: \"header\",\n\t\t\t\tid,\n\t\t\t\tstorageVersion: JSONL_STORAGE_VERSION,\n\t\t\t\tcreatedAt,\n\t\t\t\tcwd,\n\t\t\t\tparentSessionId: source.id,\n\t\t\t};\n\t\t\tstorage = await JsonlStorage.createFromForkSnapshot(\n\t\t\t\t{ fileSystem: this.fileSystem, path, now: this.now },\n\t\t\t\theader,\n\t\t\t\tsnapshot,\n\t\t\t\tcontext,\n\t\t\t);\n\t\t\tconst info = fileValue(await this.fileSystem.fileInfo(path, context), `Failed to read session ${path}`);\n\t\t\treturn this.publishOpenSession(metadataFromHeader(header, path, info.mtimeMs), storage, destinationKey);\n\t\t} catch (error) {\n\t\t\tawait storage?.close(context).catch(() => undefined);\n\t\t\tif (path !== undefined) await this.fileSystem.remove(path, { force: true }, context);\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis.pendingCreates.delete(destinationKey);\n\t\t}\n\t}\n\n\tclose(_context: Context): Promise<void> {\n\t\tif (this.closePromise !== undefined) return this.closePromise;\n\t\tthis.closed = true;\n\t\t// TODO: Define ownership semantics before deciding whether repository close should close session handles.\n\t\tthis.closePromise = Promise.resolve();\n\t\treturn this.closePromise;\n\t}\n\n\tprivate async resolveCreateDestination(\n\t\tcwdInput: string,\n\t\tid: string | undefined,\n\t\tcreatedAt: number,\n\t\tcontext: Context,\n\t): Promise<{ cwd: string; id: string }> {\n\t\tconst destinationId = id ?? uuidv7(createdAt);\n\t\tconst cwd = fileValue(\n\t\t\tawait this.fileSystem.absolutePath(cwdInput, context),\n\t\t\t`Failed to resolve session cwd ${cwdInput}`,\n\t\t);\n\t\treturn { cwd, id: destinationId };\n\t}\n\n\tprivate async listDirectory(\n\t\tdirectory: string,\n\t\tcwd: string | undefined,\n\t\tcontext: Context,\n\t): Promise<JsonlSessionMetadata[]> {\n\t\tif (\n\t\t\t!fileValue(await this.fileSystem.exists(directory, context), `Failed to check sessions directory ${directory}`)\n\t\t) {\n\t\t\treturn [];\n\t\t}\n\t\tconst files = fileValue(\n\t\t\tawait this.fileSystem.listDir(directory, context),\n\t\t\t`Failed to list sessions directory ${directory}`,\n\t\t).filter((file) => file.kind !== \"directory\" && file.name.endsWith(\".jsonl\"));\n\t\tconst metadata: JsonlSessionMetadata[] = [];\n\t\tfor (const file of files) {\n\t\t\tconst discovered = await this.readSessionMetadata(file, context);\n\t\t\tif (discovered === undefined) continue;\n\t\t\t// Directory encoding is lossy: /a/b and /a-b both map to --a-b--.\n\t\t\tif (cwd === undefined || discovered.cwd === cwd) metadata.push(discovered);\n\t\t}\n\t\treturn metadata;\n\t}\n\n\tprivate async readSessionMetadata(file: FileInfo, context: Context): Promise<JsonlSessionMetadata | undefined> {\n\t\tconst lines = fileValue(\n\t\t\tawait this.fileSystem.readTextLines(file.path, { maxLines: 1 }, context),\n\t\t\t`Failed to read session header ${file.path}`,\n\t\t);\n\t\tif (lines[0] === undefined) return undefined;\n\t\tconst parsedHeader = parseJsonlSessionHeader(lines[0]);\n\t\tif (!parsedHeader.ok) return undefined;\n\t\tif (parsedHeader.value.format === \"v3-legacy\") {\n\t\t\tconst metadata = await metadataFromLegacyV3Header(this.fileSystem, parsedHeader.value.header, context);\n\t\t\treturn { ...metadata, path: file.path, modifiedAt: file.mtimeMs };\n\t\t}\n\t\treturn metadataFromHeader(parsedHeader.value.header, file.path, file.mtimeMs);\n\t}\n\n\tprivate async sessionDirectories(root: string, context: Context): Promise<string[]> {\n\t\treturn fileValue(await this.fileSystem.listDir(root, context), `Failed to list sessions root ${root}`)\n\t\t\t.filter((entry) => entry.kind === \"directory\")\n\t\t\t.map((entry) => entry.path);\n\t}\n\n\tprivate async sessionDirectory(cwd: string, context: Context): Promise<string> {\n\t\treturn fileValue(\n\t\t\tawait this.fileSystem.joinPath([await this.root(context), sessionDirectoryName(cwd)], context),\n\t\t\t`Failed to resolve sessions directory for ${cwd}`,\n\t\t);\n\t}\n\n\tprivate async resolveNewSessionPath(cwd: string, createdAt: number, id: string, context: Context): Promise<string> {\n\t\tconst directory = await this.sessionDirectory(cwd, context);\n\t\tawait this.assertSessionIdAvailable(directory, id, context);\n\t\tfileValue(\n\t\t\tawait this.fileSystem.createDir(directory, undefined, context),\n\t\t\t`Failed to create sessions directory ${directory}`,\n\t\t);\n\t\treturn fileValue(\n\t\t\tawait this.fileSystem.joinPath([directory, sessionFileName(createdAt, id)], context),\n\t\t\t`Failed to resolve path for session ${id}`,\n\t\t);\n\t}\n\n\tprivate async assertSessionIdAvailable(directory: string, id: string, context: Context): Promise<void> {\n\t\tif (\n\t\t\t!fileValue(await this.fileSystem.exists(directory, context), `Failed to check sessions directory ${directory}`)\n\t\t)\n\t\t\treturn;\n\n\t\tconst suffix = `_${encodeURIComponent(id)}.jsonl`;\n\t\tconst idExists = fileValue(\n\t\t\tawait this.fileSystem.listDir(directory, context),\n\t\t\t`Failed to list sessions directory ${directory}`,\n\t\t).some((entry) => entry.kind !== \"directory\" && entry.name.endsWith(suffix));\n\t\tif (idExists) throw new Error(`Session already exists: ${id}`);\n\t}\n\n\tprivate async loadClosedForkSourceSnapshot(\n\t\tsource: JsonlSessionMetadata,\n\t\tcontext: Context,\n\t): Promise<ForkSourceSnapshot> {\n\t\tconst storage = await this.loadStorage(source, context);\n\t\ttry {\n\t\t\treturn await storage.captureForkSource(context);\n\t\t} finally {\n\t\t\tawait storage.close(context);\n\t\t}\n\t}\n\n\tprivate publishOpenSession(\n\t\tmetadata: JsonlSessionMetadata,\n\t\tstorage: JsonlStorage,\n\t\tkey: string,\n\t): StorageBackedSession<JsonlSessionMetadata> {\n\t\tif (this.openSessions.has(key)) throw new Error(`Session is already open: ${metadata.id}`);\n\t\tconst session = new StorageBackedSession(metadata, storage, {\n\t\t\tonClose: () => {\n\t\t\t\tif (this.openSessions.get(key) === storage) this.openSessions.delete(key);\n\t\t\t},\n\t\t});\n\t\tthis.openSessions.set(key, storage);\n\t\treturn session;\n\t}\n\n\tprivate sessionKey(cwd: string, id: string): string {\n\t\treturn `${cwd}\\0${id}`;\n\t}\n\n\tprivate async loadStorage(metadata: JsonlSessionMetadata, context: Context): Promise<JsonlStorage> {\n\t\tif (\n\t\t\t!fileValue(await this.fileSystem.exists(metadata.path, context), `Failed to check session ${metadata.path}`)\n\t\t) {\n\t\t\tthrow new Error(`Session file does not exist: ${metadata.path}`);\n\t\t}\n\t\tconst storage = await JsonlStorage.open(\n\t\t\t{\n\t\t\t\tfileSystem: this.fileSystem,\n\t\t\t\tpath: metadata.path,\n\t\t\t\tnow: this.now,\n\t\t\t},\n\t\t\tcontext,\n\t\t);\n\t\ttry {\n\t\t\tif (storage.header.id !== metadata.id || storage.header.cwd !== metadata.cwd) {\n\t\t\t\tthrow new Error(`Session identity does not match header: ${metadata.id}`);\n\t\t\t}\n\t\t\tif (storage.header.storageVersion !== JSONL_STORAGE_VERSION) {\n\t\t\t\tthrow new Error(`Session ${metadata.id} uses unsupported storage version ${storage.header.storageVersion}`);\n\t\t\t}\n\t\t\treturn storage;\n\t\t} catch (error) {\n\t\t\tawait storage.close(context);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate root(context: Context): Promise<string> {\n\t\treturn this.fileSystem\n\t\t\t.absolutePath(this.sessionsRootInput, context)\n\t\t\t.then((result) => fileValue(result, `Failed to resolve sessions root ${this.sessionsRootInput}`));\n\t}\n\n\tprivate assertOpen(): void {\n\t\tif (this.closed) throw new Error(\"JsonlSessionRepo is closed\");\n\t}\n}\n"]}