@bastani/atomic 0.9.8 → 0.9.9-alpha.1

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 (348) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +6 -0
  3. package/dist/builtin/cursor/package.json +2 -2
  4. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  5. package/dist/builtin/intercom/package.json +1 -1
  6. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  7. package/dist/builtin/mcp/package.json +1 -1
  8. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  9. package/dist/builtin/subagents/package.json +1 -1
  10. package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +2 -0
  11. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +7 -1
  12. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +2 -0
  13. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +8 -2
  14. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +2 -0
  15. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +2 -0
  16. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +2 -1
  17. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +2 -0
  18. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
  19. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +2 -0
  20. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +2 -0
  21. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +6 -0
  22. package/dist/builtin/subagents/src/shared/types-config.ts +4 -0
  23. package/dist/builtin/subagents/src/shared/types-depth.ts +29 -2
  24. package/dist/builtin/web-access/CHANGELOG.md +6 -0
  25. package/dist/builtin/web-access/package.json +1 -1
  26. package/dist/builtin/workflows/CHANGELOG.md +22 -0
  27. package/dist/builtin/workflows/README.md +17 -8
  28. package/dist/builtin/workflows/builtin/goal-prompts.ts +8 -2
  29. package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +26 -9
  30. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +1 -1
  31. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +8 -3
  32. package/dist/builtin/workflows/builtin/ralph-runner.ts +2 -0
  33. package/dist/builtin/workflows/builtin/shared-prompts.ts +24 -1
  34. package/dist/builtin/workflows/package.json +1 -1
  35. package/dist/builtin/workflows/src/durable/backend.ts +53 -15
  36. package/dist/builtin/workflows/src/durable/completed-catalog.ts +245 -0
  37. package/dist/builtin/workflows/src/durable/completed-inspection.ts +213 -0
  38. package/dist/builtin/workflows/src/durable/dbos-backend.ts +120 -170
  39. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +84 -8
  40. package/dist/builtin/workflows/src/durable/dbos-metadata.ts +98 -0
  41. package/dist/builtin/workflows/src/durable/dbos-tombstone.ts +27 -0
  42. package/dist/builtin/workflows/src/durable/factory.ts +1 -1
  43. package/dist/builtin/workflows/src/durable/file-backend.ts +247 -241
  44. package/dist/builtin/workflows/src/durable/file-lock.ts +153 -0
  45. package/dist/builtin/workflows/src/durable/file-state.ts +104 -0
  46. package/dist/builtin/workflows/src/durable/format-version.ts +12 -0
  47. package/dist/builtin/workflows/src/durable/index.ts +12 -0
  48. package/dist/builtin/workflows/src/durable/resume-catalog.ts +16 -27
  49. package/dist/builtin/workflows/src/durable/resume-eligibility.ts +20 -0
  50. package/dist/builtin/workflows/src/durable/resume-runtime.ts +29 -29
  51. package/dist/builtin/workflows/src/durable/scoped-backend.ts +20 -3
  52. package/dist/builtin/workflows/src/durable/stage-primitive.ts +29 -8
  53. package/dist/builtin/workflows/src/durable/types.ts +3 -0
  54. package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
  55. package/dist/builtin/workflows/src/engine/run.ts +14 -14
  56. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +39 -9
  57. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +10 -0
  58. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +133 -0
  59. package/dist/builtin/workflows/src/extension/runtime.ts +15 -40
  60. package/dist/builtin/workflows/src/extension/wiring.ts +9 -6
  61. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +226 -0
  62. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +1 -0
  63. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +66 -112
  64. package/dist/builtin/workflows/src/extension/workflow-schema.ts +5 -5
  65. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +16 -1
  66. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +50 -43
  67. package/dist/builtin/workflows/src/runs/foreground/executor-direct-output.ts +91 -0
  68. package/dist/builtin/workflows/src/runs/foreground/executor-direct.ts +95 -46
  69. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +3 -1
  70. package/dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts +1 -1
  71. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +3 -0
  72. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +1 -1
  73. package/dist/builtin/workflows/src/runs/foreground/stage-runner-options.ts +36 -7
  74. package/dist/builtin/workflows/src/runs/shared/worktree-cache-lifecycle.ts +25 -0
  75. package/dist/builtin/workflows/src/runs/shared/worktree-cwd.ts +103 -0
  76. package/dist/builtin/workflows/src/runs/shared/worktree-generation.ts +57 -0
  77. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +134 -8
  78. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -0
  79. package/dist/builtin/workflows/src/shared/resumable-workflow-notices.ts +43 -0
  80. package/dist/builtin/workflows/src/shared/timing.ts +4 -0
  81. package/dist/builtin/workflows/src/shared/types.ts +5 -3
  82. package/dist/builtin/workflows/src/tui/graph-view-constants.ts +1 -0
  83. package/dist/builtin/workflows/src/tui/graph-view-input.ts +24 -14
  84. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +43 -13
  85. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  86. package/dist/core/agent-session-auto-compaction.js +5 -7
  87. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  88. package/dist/core/agent-session-compaction.d.ts +6 -34
  89. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  90. package/dist/core/agent-session-compaction.js +95 -233
  91. package/dist/core/agent-session-compaction.js.map +1 -1
  92. package/dist/core/agent-session-message-queue.d.ts +0 -4
  93. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  94. package/dist/core/agent-session-message-queue.js +1 -5
  95. package/dist/core/agent-session-message-queue.js.map +1 -1
  96. package/dist/core/agent-session-methods.d.ts +5 -6
  97. package/dist/core/agent-session-methods.d.ts.map +1 -1
  98. package/dist/core/agent-session-methods.js.map +1 -1
  99. package/dist/core/agent-session-types.d.ts +2 -12
  100. package/dist/core/agent-session-types.d.ts.map +1 -1
  101. package/dist/core/agent-session-types.js.map +1 -1
  102. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  103. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  104. package/dist/core/compaction/branch-summarization.js +1 -2
  105. package/dist/core/compaction/branch-summarization.js.map +1 -1
  106. package/dist/core/compaction/compaction-boundary.d.ts +9 -0
  107. package/dist/core/compaction/compaction-boundary.d.ts.map +1 -0
  108. package/dist/core/compaction/compaction-boundary.js +115 -0
  109. package/dist/core/compaction/compaction-boundary.js.map +1 -0
  110. package/dist/core/compaction/compaction-parameters.d.ts +5 -0
  111. package/dist/core/compaction/compaction-parameters.d.ts.map +1 -0
  112. package/dist/core/compaction/compaction-parameters.js +27 -0
  113. package/dist/core/compaction/compaction-parameters.js.map +1 -0
  114. package/dist/core/compaction/compaction-runner.d.ts +14 -0
  115. package/dist/core/compaction/compaction-runner.d.ts.map +1 -0
  116. package/dist/core/compaction/compaction-runner.js +29 -0
  117. package/dist/core/compaction/compaction-runner.js.map +1 -0
  118. package/dist/core/compaction/compaction-types.d.ts +79 -0
  119. package/dist/core/compaction/compaction-types.d.ts.map +1 -0
  120. package/dist/core/compaction/compaction-types.js +6 -0
  121. package/dist/core/compaction/compaction-types.js.map +1 -0
  122. package/dist/core/compaction/deleted-ranges.d.ts +6 -0
  123. package/dist/core/compaction/deleted-ranges.d.ts.map +1 -0
  124. package/dist/core/compaction/deleted-ranges.js +134 -0
  125. package/dist/core/compaction/deleted-ranges.js.map +1 -0
  126. package/dist/core/compaction/index.d.ts +7 -1
  127. package/dist/core/compaction/index.d.ts.map +1 -1
  128. package/dist/core/compaction/index.js +7 -1
  129. package/dist/core/compaction/index.js.map +1 -1
  130. package/dist/core/compaction/range-planner.d.ts +21 -0
  131. package/dist/core/compaction/range-planner.d.ts.map +1 -0
  132. package/dist/core/compaction/range-planner.js +145 -0
  133. package/dist/core/compaction/range-planner.js.map +1 -0
  134. package/dist/core/compaction/transcript-serialization.d.ts +11 -0
  135. package/dist/core/compaction/transcript-serialization.d.ts.map +1 -0
  136. package/dist/core/compaction/transcript-serialization.js +104 -0
  137. package/dist/core/compaction/transcript-serialization.js.map +1 -0
  138. package/dist/core/extensions/context-types.d.ts +2 -2
  139. package/dist/core/extensions/context-types.d.ts.map +1 -1
  140. package/dist/core/extensions/context-types.js.map +1 -1
  141. package/dist/core/extensions/event-results.d.ts +2 -2
  142. package/dist/core/extensions/event-results.d.ts.map +1 -1
  143. package/dist/core/extensions/event-results.js.map +1 -1
  144. package/dist/core/extensions/session-events.d.ts +7 -7
  145. package/dist/core/extensions/session-events.d.ts.map +1 -1
  146. package/dist/core/extensions/session-events.js.map +1 -1
  147. package/dist/core/index.d.ts +1 -1
  148. package/dist/core/index.d.ts.map +1 -1
  149. package/dist/core/index.js.map +1 -1
  150. package/dist/core/messages.d.ts +4 -0
  151. package/dist/core/messages.d.ts.map +1 -1
  152. package/dist/core/messages.js +11 -0
  153. package/dist/core/messages.js.map +1 -1
  154. package/dist/core/provider-context-usage.d.ts.map +1 -1
  155. package/dist/core/provider-context-usage.js +1 -2
  156. package/dist/core/provider-context-usage.js.map +1 -1
  157. package/dist/core/session-manager-archive.d.ts +2 -1
  158. package/dist/core/session-manager-archive.d.ts.map +1 -1
  159. package/dist/core/session-manager-archive.js +2 -2
  160. package/dist/core/session-manager-archive.js.map +1 -1
  161. package/dist/core/session-manager-classification.d.ts +9 -0
  162. package/dist/core/session-manager-classification.d.ts.map +1 -0
  163. package/dist/core/session-manager-classification.js +37 -0
  164. package/dist/core/session-manager-classification.js.map +1 -0
  165. package/dist/core/session-manager-core.d.ts +4 -4
  166. package/dist/core/session-manager-core.d.ts.map +1 -1
  167. package/dist/core/session-manager-core.js +13 -8
  168. package/dist/core/session-manager-core.js.map +1 -1
  169. package/dist/core/session-manager-entries.d.ts +4 -3
  170. package/dist/core/session-manager-entries.d.ts.map +1 -1
  171. package/dist/core/session-manager-entries.js +12 -10
  172. package/dist/core/session-manager-entries.js.map +1 -1
  173. package/dist/core/session-manager-history.d.ts +6 -23
  174. package/dist/core/session-manager-history.d.ts.map +1 -1
  175. package/dist/core/session-manager-history.js +23 -256
  176. package/dist/core/session-manager-history.js.map +1 -1
  177. package/dist/core/session-manager-list.d.ts.map +1 -1
  178. package/dist/core/session-manager-list.js +3 -3
  179. package/dist/core/session-manager-list.js.map +1 -1
  180. package/dist/core/session-manager-storage.d.ts +1 -1
  181. package/dist/core/session-manager-storage.d.ts.map +1 -1
  182. package/dist/core/session-manager-storage.js +3 -2
  183. package/dist/core/session-manager-storage.js.map +1 -1
  184. package/dist/core/session-manager-types.d.ts +14 -15
  185. package/dist/core/session-manager-types.d.ts.map +1 -1
  186. package/dist/core/session-manager-types.js.map +1 -1
  187. package/dist/core/session-manager.d.ts +3 -2
  188. package/dist/core/session-manager.d.ts.map +1 -1
  189. package/dist/core/session-manager.js +1 -1
  190. package/dist/core/session-manager.js.map +1 -1
  191. package/dist/core/slash-commands.d.ts.map +1 -1
  192. package/dist/core/slash-commands.js +1 -1
  193. package/dist/core/slash-commands.js.map +1 -1
  194. package/dist/index.d.ts +3 -2
  195. package/dist/index.d.ts.map +1 -1
  196. package/dist/index.js +2 -1
  197. package/dist/index.js.map +1 -1
  198. package/dist/main-session.d.ts +1 -0
  199. package/dist/main-session.d.ts.map +1 -1
  200. package/dist/main-session.js +7 -0
  201. package/dist/main-session.js.map +1 -1
  202. package/dist/main.d.ts.map +1 -1
  203. package/dist/main.js +2 -2
  204. package/dist/main.js.map +1 -1
  205. package/dist/modes/interactive/components/chat-message-renderer.d.ts +4 -1
  206. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/chat-message-renderer.js +12 -22
  208. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  209. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  210. package/dist/modes/interactive/components/chat-session-host-events.js +5 -11
  211. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  212. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  213. package/dist/modes/interactive/components/chat-session-host-rendering.js +3 -0
  214. package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
  215. package/dist/modes/interactive/components/chat-session-host-state.d.ts +1 -0
  216. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  217. package/dist/modes/interactive/components/chat-session-host-state.js +1 -0
  218. package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
  219. package/dist/modes/interactive/components/chat-session-host-utils.d.ts.map +1 -1
  220. package/dist/modes/interactive/components/chat-session-host-utils.js +1 -1
  221. package/dist/modes/interactive/components/chat-session-host-utils.js.map +1 -1
  222. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  223. package/dist/modes/interactive/components/chat-session-host.js +1 -0
  224. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  225. package/dist/modes/interactive/components/compaction-boundary-message.d.ts +20 -0
  226. package/dist/modes/interactive/components/compaction-boundary-message.d.ts.map +1 -0
  227. package/dist/modes/interactive/components/compaction-boundary-message.js +48 -0
  228. package/dist/modes/interactive/components/compaction-boundary-message.js.map +1 -0
  229. package/dist/modes/interactive/components/index.d.ts +1 -1
  230. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  231. package/dist/modes/interactive/components/index.js +1 -1
  232. package/dist/modes/interactive/components/index.js.map +1 -1
  233. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  234. package/dist/modes/interactive/components/session-selector-list.js +3 -0
  235. package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
  236. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  237. package/dist/modes/interactive/interactive-agent-events.js +3 -47
  238. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  239. package/dist/modes/interactive/interactive-mode-deps.d.ts +2 -2
  240. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  241. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  242. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  243. package/dist/modes/interactive/interactive-mode-surface.d.ts +5 -3
  244. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  245. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  246. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  247. package/dist/modes/interactive/interactive-render-chat.js +21 -12
  248. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  249. package/dist/modes/rpc/rpc-client.d.ts +3 -5
  250. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  251. package/dist/modes/rpc/rpc-client.js +1 -6
  252. package/dist/modes/rpc/rpc-client.js.map +1 -1
  253. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  254. package/dist/modes/rpc/rpc-command-handler.js +0 -4
  255. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  256. package/dist/modes/rpc/rpc-types.d.ts +2 -11
  257. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  258. package/dist/modes/rpc/rpc-types.js.map +1 -1
  259. package/docs/compaction.md +97 -772
  260. package/docs/extensions.md +25 -29
  261. package/docs/json.md +2 -2
  262. package/docs/rpc.md +32 -24
  263. package/docs/sdk.md +3 -3
  264. package/docs/session-format.md +49 -30
  265. package/docs/sessions.md +5 -3
  266. package/docs/settings.md +14 -4
  267. package/docs/usage.md +1 -1
  268. package/docs/workflows.md +30 -15
  269. package/examples/extensions/custom-compaction.ts +14 -58
  270. package/examples/extensions/handoff.ts +2 -3
  271. package/npm-shrinkwrap.json +23 -23
  272. package/package.json +2 -2
  273. package/dist/core/compaction/context-assistant-turns.d.ts +0 -42
  274. package/dist/core/compaction/context-assistant-turns.d.ts.map +0 -1
  275. package/dist/core/compaction/context-assistant-turns.js +0 -87
  276. package/dist/core/compaction/context-assistant-turns.js.map +0 -1
  277. package/dist/core/compaction/context-compaction-critical.d.ts +0 -15
  278. package/dist/core/compaction/context-compaction-critical.d.ts.map +0 -1
  279. package/dist/core/compaction/context-compaction-critical.js +0 -57
  280. package/dist/core/compaction/context-compaction-critical.js.map +0 -1
  281. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts +0 -18
  282. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts.map +0 -1
  283. package/dist/core/compaction/context-compaction-eviction-alternates.js +0 -186
  284. package/dist/core/compaction/context-compaction-eviction-alternates.js.map +0 -1
  285. package/dist/core/compaction/context-compaction-eviction.d.ts +0 -12
  286. package/dist/core/compaction/context-compaction-eviction.d.ts.map +0 -1
  287. package/dist/core/compaction/context-compaction-eviction.js +0 -222
  288. package/dist/core/compaction/context-compaction-eviction.js.map +0 -1
  289. package/dist/core/compaction/context-compaction-metrics.d.ts +0 -28
  290. package/dist/core/compaction/context-compaction-metrics.d.ts.map +0 -1
  291. package/dist/core/compaction/context-compaction-metrics.js +0 -107
  292. package/dist/core/compaction/context-compaction-metrics.js.map +0 -1
  293. package/dist/core/compaction/context-compaction-prompt.d.ts +0 -9
  294. package/dist/core/compaction/context-compaction-prompt.d.ts.map +0 -1
  295. package/dist/core/compaction/context-compaction-prompt.js +0 -163
  296. package/dist/core/compaction/context-compaction-prompt.js.map +0 -1
  297. package/dist/core/compaction/context-compaction-runner.d.ts +0 -11
  298. package/dist/core/compaction/context-compaction-runner.d.ts.map +0 -1
  299. package/dist/core/compaction/context-compaction-runner.js +0 -273
  300. package/dist/core/compaction/context-compaction-runner.js.map +0 -1
  301. package/dist/core/compaction/context-compaction-strategy.d.ts +0 -5
  302. package/dist/core/compaction/context-compaction-strategy.d.ts.map +0 -1
  303. package/dist/core/compaction/context-compaction-strategy.js +0 -27
  304. package/dist/core/compaction/context-compaction-strategy.js.map +0 -1
  305. package/dist/core/compaction/context-compaction-types.d.ts +0 -75
  306. package/dist/core/compaction/context-compaction-types.d.ts.map +0 -1
  307. package/dist/core/compaction/context-compaction-types.js +0 -6
  308. package/dist/core/compaction/context-compaction-types.js.map +0 -1
  309. package/dist/core/compaction/context-compaction.d.ts +0 -10
  310. package/dist/core/compaction/context-compaction.d.ts.map +0 -1
  311. package/dist/core/compaction/context-compaction.js +0 -8
  312. package/dist/core/compaction/context-compaction.js.map +0 -1
  313. package/dist/core/compaction/context-deletion-application.d.ts +0 -14
  314. package/dist/core/compaction/context-deletion-application.d.ts.map +0 -1
  315. package/dist/core/compaction/context-deletion-application.js +0 -261
  316. package/dist/core/compaction/context-deletion-application.js.map +0 -1
  317. package/dist/core/compaction/context-deletion-store.d.ts +0 -78
  318. package/dist/core/compaction/context-deletion-store.d.ts.map +0 -1
  319. package/dist/core/compaction/context-deletion-store.js +0 -162
  320. package/dist/core/compaction/context-deletion-store.js.map +0 -1
  321. package/dist/core/compaction/context-deletion-targets.d.ts +0 -43
  322. package/dist/core/compaction/context-deletion-targets.d.ts.map +0 -1
  323. package/dist/core/compaction/context-deletion-targets.js +0 -292
  324. package/dist/core/compaction/context-deletion-targets.js.map +0 -1
  325. package/dist/core/compaction/context-deletion-tool-definitions.d.ts +0 -193
  326. package/dist/core/compaction/context-deletion-tool-definitions.d.ts.map +0 -1
  327. package/dist/core/compaction/context-deletion-tool-definitions.js +0 -98
  328. package/dist/core/compaction/context-deletion-tool-definitions.js.map +0 -1
  329. package/dist/core/compaction/context-deletion-tool-helpers.d.ts +0 -19
  330. package/dist/core/compaction/context-deletion-tool-helpers.d.ts.map +0 -1
  331. package/dist/core/compaction/context-deletion-tool-helpers.js +0 -210
  332. package/dist/core/compaction/context-deletion-tool-helpers.js.map +0 -1
  333. package/dist/core/compaction/context-deletion-tools.d.ts +0 -4
  334. package/dist/core/compaction/context-deletion-tools.d.ts.map +0 -1
  335. package/dist/core/compaction/context-deletion-tools.js +0 -403
  336. package/dist/core/compaction/context-deletion-tools.js.map +0 -1
  337. package/dist/core/compaction/context-transcript-analysis.d.ts +0 -11
  338. package/dist/core/compaction/context-transcript-analysis.d.ts.map +0 -1
  339. package/dist/core/compaction/context-transcript-analysis.js +0 -228
  340. package/dist/core/compaction/context-transcript-analysis.js.map +0 -1
  341. package/dist/core/session-manager-tool-dependencies.d.ts +0 -10
  342. package/dist/core/session-manager-tool-dependencies.d.ts.map +0 -1
  343. package/dist/core/session-manager-tool-dependencies.js +0 -133
  344. package/dist/core/session-manager-tool-dependencies.js.map +0 -1
  345. package/dist/modes/interactive/components/context-compaction-summary-message.d.ts +0 -17
  346. package/dist/modes/interactive/components/context-compaction-summary-message.d.ts.map +0 -1
  347. package/dist/modes/interactive/components/context-compaction-summary-message.js +0 -83
  348. package/dist/modes/interactive/components/context-compaction-summary-message.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-history.js","sourceRoot":"","sources":["../../src/core/session-manager-history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport {\n\tcreateBranchSummaryMessage,\n\tcreateCustomMessage,\n\tmessageStartsLlmUserTurn,\n\tnormalizeMessageContent,\n\tuserLikeContentIsLlmVisible,\n} from \"./messages.ts\";\nimport { normalizeDerivedSessionEntries } from \"./session-entry-normalization.ts\";\nimport { contentArrayHasAssistantThinkingBlock } from \"./thinking-blocks.ts\";\nimport { reconcilePersistedToolDependencyFilters } from \"./session-manager-tool-dependencies.ts\";\nimport { analyzeAssistantToolUseTurns, type AssistantTurnEntry } from \"./compaction/context-assistant-turns.js\";\nimport type {\n\tContextCompactionEntry,\n\tContextDeletionFilters,\n\tFileEntry,\n\tSessionContext,\n\tSessionEntry,\n\tSessionTreeNode,\n} from \"./session-manager-types.ts\";\n\nexport function getLatestCompactionBoundaryEntry(entries: SessionEntry[]): ContextCompactionEntry | null {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"context_compaction\") {\n\t\t\treturn entry;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Build raw deletion filters from persisted context_compaction entries.\n *\n * These raw filters do not apply replay-safety repair for assistant\n * thinking/redacted_thinking turns or their paired tool results. Production\n * context rebuild paths should prefer `buildEffectiveContextDeletionFilters`\n * or `buildContextDeletionFilteredPath(path)` unless they intentionally need\n * the un-repaired historical deletion plan for diagnostics.\n */\nexport function buildContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\n\tfor (const entry of path) {\n\t\tif (entry.type !== \"context_compaction\") continue;\n\t\tfor (const target of entry.deletedTargets) {\n\t\t\tif (target.kind === \"entry\") {\n\t\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst existing = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\t\texisting.add(target.blockIndex);\n\t\t\tdeletedContentBlocks.set(target.entryId, existing);\n\t\t}\n\t}\n\n\treturn { deletedEntryIds, deletedContentBlocks };\n}\n\nfunction restoreEntry(filters: ContextDeletionFilters, entryId: string): void {\n\tfilters.deletedEntryIds.delete(entryId);\n\tfilters.deletedContentBlocks.delete(entryId);\n}\n\nfunction addDeletionTarget(\n\tfilters: ContextDeletionFilters,\n\ttarget: ContextCompactionEntry[\"deletedTargets\"][number],\n): void {\n\tif (target.kind === \"entry\") {\n\t\tfilters.deletedEntryIds.add(target.entryId);\n\t\tfilters.deletedContentBlocks.delete(target.entryId);\n\t\treturn;\n\t}\n\tif (filters.deletedEntryIds.has(target.entryId)) return;\n\tconst existing = filters.deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\texisting.add(target.blockIndex);\n\tfilters.deletedContentBlocks.set(target.entryId, existing);\n}\n\nfunction sessionBoundaryIsVisible(entry: SessionEntry, filters: ContextDeletionFilters): boolean {\n\tif (filters.deletedEntryIds.has(entry.id)) return false;\n\tconst deletedBlocks = filters.deletedContentBlocks.get(entry.id);\n\tif (entry.type === \"custom_message\") {\n\t\treturn entry.excludeFromContext !== true && userLikeContentIsLlmVisible(entry.content, deletedBlocks);\n\t}\n\tif (entry.type === \"branch_summary\") return typeof entry.summary === \"string\" && entry.summary.length > 0;\n\tif (entry.type !== \"message\") return false;\n\treturn messageStartsLlmUserTurn(entry.message, deletedBlocks);\n}\n\nfunction sessionEntryAsTurnEntry(\n\tentry: SessionEntry,\n\tfilters: ContextDeletionFilters,\n): AssistantTurnEntry | undefined {\n\tif (entry.type === \"custom_message\") {\n\t\tif (entry.excludeFromContext === true) return undefined;\n\t\treturn {\n\t\t\tentryId: entry.id,\n\t\t\trole: \"custom\",\n\t\t\thasSignedThinking: false,\n\t\t\tstartsNewTurn: sessionBoundaryIsVisible(entry, filters),\n\t\t};\n\t}\n\tif (entry.type === \"branch_summary\") {\n\t\treturn {\n\t\t\tentryId: entry.id,\n\t\t\trole: \"branchSummary\",\n\t\t\thasSignedThinking: false,\n\t\t\tstartsNewTurn: sessionBoundaryIsVisible(entry, filters),\n\t\t};\n\t}\n\tif (entry.type !== \"message\") return undefined;\n\tconst message = entry.message;\n\tif (message.role === \"compactionSummary\") return undefined;\n\tif (\n\t\t(message.role === \"bashExecution\" && message.excludeFromContext === true) ||\n\t\t(message.role === \"custom\" && (message as { excludeFromContext?: boolean }).excludeFromContext === true)\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tentryId: entry.id,\n\t\trole: message.role,\n\t\thasSignedThinking: message.role === \"assistant\" && contentArrayHasAssistantThinkingBlock(message.content),\n\t\tstartsNewTurn: sessionBoundaryIsVisible(entry, filters),\n\t};\n}\n\nfunction analyzeSessionAssistantTurns(path: SessionEntry[], filters: ContextDeletionFilters) {\n\treturn analyzeAssistantToolUseTurns(path.flatMap((entry) => sessionEntryAsTurnEntry(entry, filters) ?? []));\n}\n\nfunction repairSignedThinkingTurnFilters(path: SessionEntry[], filters: ContextDeletionFilters): ContextDeletionFilters {\n\tconst turns = analyzeSessionAssistantTurns(path, filters);\n\n\t// A retained signed assistant is byte-exact/all-or-nothing at message level.\n\t// Whole-entry deletion alone counts toward a safe historical omission.\n\tfor (const turn of turns) {\n\t\tfor (const entryId of turn.signedThinkingEntryIds) {\n\t\t\tif (!filters.deletedEntryIds.has(entryId)) filters.deletedContentBlocks.delete(entryId);\n\t\t}\n\t}\n\n\tfor (const turn of turns) {\n\t\tconst deletedSignedIds = turn.signedThinkingEntryIds.filter((entryId) => filters.deletedEntryIds.has(entryId));\n\t\tconst unsafe = turn.active\n\t\t\t? deletedSignedIds.length > 0\n\t\t\t: deletedSignedIds.length > 0 && deletedSignedIds.length < turn.signedThinkingEntryIds.length;\n\t\tif (!unsafe) continue;\n\t\tfor (const entryId of turn.signedThinkingEntryIds) restoreEntry(filters, entryId);\n\t}\n\treturn filters;\n}\n\nfunction signedTurnReplayPolicy(\n\tpath: SessionEntry[],\n\tfinalIntent: ContextDeletionFilters,\n): { restoredEntryIds: Set<string>; retainedEntryIds: Set<string> } {\n\tconst restoredEntryIds = new Set<string>();\n\tconst retainedEntryIds = new Set<string>();\n\tfor (const turn of analyzeSessionAssistantTurns(path, finalIntent)) {\n\t\tconst deleted = turn.signedThinkingEntryIds.filter((entryId) => finalIntent.deletedEntryIds.has(entryId));\n\t\tconst restoreTurn = turn.active\n\t\t\t? deleted.length > 0\n\t\t\t: deleted.length > 0 && deleted.length < turn.signedThinkingEntryIds.length;\n\t\tfor (const entryId of turn.signedThinkingEntryIds) {\n\t\t\tif (restoreTurn) restoredEntryIds.add(entryId);\n\t\t\tif (restoreTurn || !finalIntent.deletedEntryIds.has(entryId)) retainedEntryIds.add(entryId);\n\t\t}\n\t}\n\treturn { restoredEntryIds, retainedEntryIds };\n}\n\nexport function buildEffectiveContextDeletionFilters(path: SessionEntry[]): ContextDeletionFilters {\n\tconst derivedPath = normalizeDerivedSessionEntries(path);\n\tconst finalIntent = buildContextDeletionFilters(derivedPath);\n\tif (!derivedPath.some((entry) => entry.type === \"context_compaction\")) return finalIntent;\n\n\t// Decide turn-level restoration from the cumulative durable intent so safe\n\t// complete historical omissions may span multiple compaction entries. Then\n\t// replay each entry chronologically: reconciliation at each boundary restores\n\t// an unsafe paired whole-result deletion without erasing a later independent\n\t// content-block deletion on that result.\n\tconst { restoredEntryIds, retainedEntryIds } = signedTurnReplayPolicy(derivedPath, finalIntent);\n\tconst effective: ContextDeletionFilters = {\n\t\tdeletedEntryIds: new Set<string>(),\n\t\tdeletedContentBlocks: new Map<string, Set<number>>(),\n\t};\n\tfor (const entry of derivedPath) {\n\t\tif (entry.type !== \"context_compaction\") continue;\n\t\tfor (const target of entry.deletedTargets) {\n\t\t\tif (target.kind === \"entry\" && restoredEntryIds.has(target.entryId)) continue;\n\t\t\tif (target.kind === \"content_block\" && retainedEntryIds.has(target.entryId)) continue;\n\t\t\taddDeletionTarget(effective, target);\n\t\t}\n\t\treconcilePersistedToolDependencyFilters(derivedPath, effective);\n\t}\n\n\t// Defensive final repair handles malformed dependency graphs and keeps this\n\t// reconstruction path authoritative even if reconciliation behavior evolves.\n\trepairSignedThinkingTurnFilters(derivedPath, effective);\n\treturn reconcilePersistedToolDependencyFilters(derivedPath, effective);\n}\n\nfunction filterContentArray<T>(content: T[], deletedBlocks: ReadonlySet<number>): T[] {\n\treturn content.filter((_, index) => !deletedBlocks.has(index));\n}\n\nfunction filterMessageContentBlocks(\n\tmessage: AgentMessage,\n\tdeletedBlocks: ReadonlySet<number> | undefined,\n): AgentMessage | undefined {\n\tif (!deletedBlocks || deletedBlocks.size === 0) return message;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"toolResult\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"custom\": {\n\t\t\tif (!Array.isArray(message.content)) return message;\n\t\t\tconst content = filterContentArray(message.content, deletedBlocks);\n\t\t\tif (content.length === 0) return undefined;\n\t\t\treturn { ...message, content };\n\t\t}\n\t\tcase \"bashExecution\":\n\t\tcase \"branchSummary\":\n\t\t\treturn message;\n\t}\n}\n\n/**\n * Return the active branch path after applying logical context-deletion entries.\n * Whole-entry deletions remove the entry from the path. Content-block deletions\n * clone only affected message/custom-message entries so retained blocks stay verbatim.\n * The optional filters parameter is for callers that already computed effective\n * filters with `buildEffectiveContextDeletionFilters(path)` and want to avoid\n * repeating the repair pass.\n */\nexport function buildContextDeletionFilteredPath(\n\tpath: SessionEntry[],\n\teffectiveFilters?: ContextDeletionFilters,\n): SessionEntry[] {\n\tconst derivedPath = normalizeDerivedSessionEntries(path);\n\tconst filters = effectiveFilters ?? buildEffectiveContextDeletionFilters(derivedPath);\n\tconst filteredPath: SessionEntry[] = [];\n\n\tfor (const entry of derivedPath) {\n\t\tif (filters.deletedEntryIds.has(entry.id)) continue;\n\n\t\tconst deletedBlocks = filters.deletedContentBlocks.get(entry.id);\n\t\tif (!deletedBlocks || deletedBlocks.size === 0) {\n\t\t\tfilteredPath.push(entry);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"message\") {\n\t\t\tconst message = filterMessageContentBlocks(entry.message, deletedBlocks);\n\t\t\tif (message) filteredPath.push({ ...entry, message });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"custom_message\" && Array.isArray(entry.content)) {\n\t\t\tconst content = filterContentArray(entry.content, deletedBlocks);\n\t\t\tif (content.length > 0) filteredPath.push({ ...entry, content });\n\t\t\tcontinue;\n\t\t}\n\n\t\tfilteredPath.push(entry);\n\t}\n\n\treturn filteredPath;\n}\n\n/**\n * Build the session context from entries using tree traversal.\n * If leafId is provided, walks from that entry to root.\n * Applies context-deletion filtering and includes branch summaries along the path.\n */\nexport function buildSessionContext(\n\tentries: SessionEntry[],\n\tleafId?: string | null,\n\tbyId?: Map<string, SessionEntry>,\n): SessionContext {\n\t// Build uuid index if not available\n\tif (!byId) {\n\t\tbyId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\t}\n\n\t// Find leaf\n\tlet leaf: SessionEntry | undefined;\n\tif (leafId === null) {\n\t\t// Explicitly null - return no messages (navigated to before first entry)\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\tif (leafId) {\n\t\tleaf = byId.get(leafId);\n\t}\n\tif (!leaf) {\n\t\t// Fallback to last entry (when leafId is undefined)\n\t\tleaf = entries[entries.length - 1];\n\t}\n\n\tif (!leaf) {\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\n\t// Walk from leaf to root, collecting path\n\tconst path = normalizeDerivedSessionEntries(getBranchPath(leaf.id, byId));\n\n\t// Extract settings\n\tlet thinkingLevel = \"off\";\n\tlet contextWindow: number | undefined;\n\tlet model: { provider: string; modelId: string } | null = null;\n\n\tfor (const entry of path) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"context_window_change\") {\n\t\t\tcontextWindow = entry.contextWindow;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t}\n\t}\n\n\tconst filteredPath = buildContextDeletionFilteredPath(path);\n\n\t// Build active context messages from the filtered path. Legacy \"compaction\"\n\t// entries are archival metadata and intentionally inert here.\n\tconst messages: AgentMessage[] = [];\n\n\tconst appendMessage = (entry: SessionEntry) => {\n\t\tlet message: AgentMessage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tmessage = normalizeMessageContent(entry.message);\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessage = createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content,\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t\tentry.excludeFromContext,\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && typeof entry.summary === \"string\" && entry.summary.length > 0) {\n\t\t\tmessage = createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t\t}\n\n\t\tif (message) messages.push(message);\n\t};\n\n\tfor (const entry of filteredPath) {\n\t\tappendMessage(entry);\n\t}\n\n\treturn { messages, thinkingLevel, contextWindow, model };\n}\n\nexport interface SessionIndex {\n\tbyId: Map<string, SessionEntry>;\n\tlabelsById: Map<string, string>;\n\tlabelTimestampsById: Map<string, string>;\n\tleafId: string | null;\n}\n\nexport function buildSessionIndex(fileEntries: FileEntry[]): SessionIndex {\n\tconst byId = new Map<string, SessionEntry>();\n\tconst labelsById = new Map<string, string>();\n\tconst labelTimestampsById = new Map<string, string>();\n\tlet leafId: string | null = null;\n\n\tfor (const entry of fileEntries) {\n\t\tif (entry.type === \"session\") continue;\n\t\tbyId.set(entry.id, entry);\n\t\tleafId = entry.id;\n\t\tif (entry.type === \"label\") {\n\t\t\tif (entry.label) {\n\t\t\t\tlabelsById.set(entry.targetId, entry.label);\n\t\t\t\tlabelTimestampsById.set(entry.targetId, entry.timestamp);\n\t\t\t} else {\n\t\t\t\tlabelsById.delete(entry.targetId);\n\t\t\t\tlabelTimestampsById.delete(entry.targetId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { byId, labelsById, labelTimestampsById, leafId };\n}\n\nexport function getBranchPath(fromId: string | null | undefined, byId: Map<string, SessionEntry>): SessionEntry[] {\n\tconst path: SessionEntry[] = [];\n\tlet current = fromId ? byId.get(fromId) : undefined;\n\twhile (current) {\n\t\tpath.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tpath.reverse();\n\treturn path;\n}\n\nexport function buildSessionTree(\n\tentries: SessionEntry[],\n\tlabelsById: ReadonlyMap<string, string>,\n\tlabelTimestampsById: ReadonlyMap<string, string>,\n): SessionTreeNode[] {\n\tconst nodeMap = new Map<string, SessionTreeNode>();\n\tconst roots: SessionTreeNode[] = [];\n\n\t// Create nodes with resolved labels\n\tfor (const entry of entries) {\n\t\tconst label = labelsById.get(entry.id);\n\t\tconst labelTimestamp = labelTimestampsById.get(entry.id);\n\t\tnodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });\n\t}\n\n\t// Build tree\n\tfor (const entry of entries) {\n\t\tconst node = nodeMap.get(entry.id)!;\n\t\tif (entry.parentId === null || entry.parentId === entry.id) {\n\t\t\troots.push(node);\n\t\t} else {\n\t\t\tconst parent = nodeMap.get(entry.parentId);\n\t\t\tif (parent) {\n\t\t\t\tparent.children.push(node);\n\t\t\t} else {\n\t\t\t\t// Orphan - treat as root\n\t\t\t\troots.push(node);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort children by timestamp (oldest first, newest at bottom)\n\t// Use iterative approach to avoid stack overflow on deep trees\n\tconst stack: SessionTreeNode[] = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop()!;\n\t\tnode.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());\n\t\tstack.push(...node.children);\n\t}\n\n\treturn roots;\n}\n"]}
1
+ {"version":3,"file":"session-manager-history.js","sourceRoot":"","sources":["../../src/core/session-manager-history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { createBranchSummaryMessage, createCustomMessage, createVerbatimCompactionMessage, normalizeMessageContent } from \"./messages.ts\";\nimport { normalizeDerivedSessionEntries } from \"./session-entry-normalization.ts\";\nimport type { VerbatimCompactionDetails } from \"./compaction/compaction-types.js\";\nimport type { CompactionEntry, FileEntry, SessionContext, SessionEntry, SessionTreeNode } from \"./session-manager-types.ts\";\n\nexport function getLatestCompactionBoundaryEntry(\n\tentries: SessionEntry[],\n): CompactionEntry<VerbatimCompactionDetails> | null {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type !== \"compaction\") continue;\n\t\tconst details = (entry as CompactionEntry<{ strategy?: string }>).details;\n\t\tif (details?.strategy === \"verbatim-lines\") return entry as CompactionEntry<VerbatimCompactionDetails>;\n\t}\n\treturn null;\n}\n\n\n/**\n * Build the session context from entries using tree traversal.\n * If leafId is provided, walks from that entry to root.\n * Emits the latest verbatim compaction boundary (compacted string as a custom-role\n * boundary message) followed by the kept tail from firstKeptEntryId, and includes\n * branch summaries along the path.\n */\nexport function buildSessionContext(\n\tentries: SessionEntry[],\n\tleafId?: string | null,\n\tbyId?: Map<string, SessionEntry>,\n): SessionContext {\n\t// Build uuid index if not available\n\tif (!byId) {\n\t\tbyId = new Map<string, SessionEntry>();\n\t\tfor (const entry of entries) {\n\t\t\tbyId.set(entry.id, entry);\n\t\t}\n\t}\n\n\t// Find leaf\n\tlet leaf: SessionEntry | undefined;\n\tif (leafId === null) {\n\t\t// Explicitly null - return no messages (navigated to before first entry)\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\tif (leafId) {\n\t\tleaf = byId.get(leafId);\n\t}\n\tif (!leaf) {\n\t\t// Fallback to last entry (when leafId is undefined)\n\t\tleaf = entries[entries.length - 1];\n\t}\n\n\tif (!leaf) {\n\t\treturn { messages: [], thinkingLevel: \"off\", contextWindow: undefined, model: null };\n\t}\n\n\t// Walk from leaf to root, collecting path\n\tconst path = normalizeDerivedSessionEntries(getBranchPath(leaf.id, byId));\n\n\t// Extract settings\n\tlet thinkingLevel = \"off\";\n\tlet contextWindow: number | undefined;\n\tlet model: { provider: string; modelId: string } | null = null;\n\n\tfor (const entry of path) {\n\t\tif (entry.type === \"thinking_level_change\") {\n\t\t\tthinkingLevel = entry.thinkingLevel;\n\t\t} else if (entry.type === \"context_window_change\") {\n\t\t\tcontextWindow = entry.contextWindow;\n\t\t} else if (entry.type === \"model_change\") {\n\t\t\tmodel = { provider: entry.provider, modelId: entry.modelId };\n\t\t} else if (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\tmodel = { provider: entry.message.provider, modelId: entry.message.model };\n\t\t}\n\t}\n\n\tconst messages: AgentMessage[] = [];\n\tconst appendMessage = (entry: SessionEntry): void => {\n\t\tlet message: AgentMessage | undefined;\n\t\tif (entry.type === \"message\") {\n\t\t\tmessage = normalizeMessageContent(entry.message);\n\t\t} else if (entry.type === \"custom_message\") {\n\t\t\tmessage = createCustomMessage(\n\t\t\t\tentry.customType,\n\t\t\t\tentry.content,\n\t\t\t\tentry.display,\n\t\t\t\tentry.details,\n\t\t\t\tentry.timestamp,\n\t\t\t\tentry.excludeFromContext,\n\t\t\t);\n\t\t} else if (entry.type === \"branch_summary\" && typeof entry.summary === \"string\" && entry.summary.length > 0) {\n\t\t\tmessage = createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);\n\t\t}\n\t\tif (message) messages.push(message);\n\t};\n\n\tconst boundary = getLatestCompactionBoundaryEntry(path);\n\tif (!boundary) {\n\t\tfor (const entry of path) appendMessage(entry);\n\t\treturn { messages, thinkingLevel, contextWindow, model };\n\t}\n\n\tconst boundaryIndex = path.findIndex((entry) => entry.id === boundary.id);\n\tmessages.push(createVerbatimCompactionMessage(boundary.summary, boundary.tokensBefore, boundary.timestamp, boundary.details));\n\tconst firstKeptIndex = path.findIndex(\n\t\t(entry, index) => index < boundaryIndex && entry.id === boundary.firstKeptEntryId,\n\t);\n\tif (firstKeptIndex >= 0) {\n\t\tfor (let i = firstKeptIndex; i < boundaryIndex; i++) appendMessage(path[i]);\n\t}\n\tfor (let i = boundaryIndex + 1; i < path.length; i++) appendMessage(path[i]);\n\n\treturn { messages, thinkingLevel, contextWindow, model };\n}\n\nexport interface SessionIndex {\n\tbyId: Map<string, SessionEntry>;\n\tlabelsById: Map<string, string>;\n\tlabelTimestampsById: Map<string, string>;\n\tleafId: string | null;\n}\n\nexport function buildSessionIndex(fileEntries: FileEntry[]): SessionIndex {\n\tconst byId = new Map<string, SessionEntry>();\n\tconst labelsById = new Map<string, string>();\n\tconst labelTimestampsById = new Map<string, string>();\n\tlet leafId: string | null = null;\n\n\tfor (const entry of fileEntries) {\n\t\tif (entry.type === \"session\") continue;\n\t\tbyId.set(entry.id, entry);\n\t\tleafId = entry.id;\n\t\tif (entry.type === \"label\") {\n\t\t\tif (entry.label) {\n\t\t\t\tlabelsById.set(entry.targetId, entry.label);\n\t\t\t\tlabelTimestampsById.set(entry.targetId, entry.timestamp);\n\t\t\t} else {\n\t\t\t\tlabelsById.delete(entry.targetId);\n\t\t\t\tlabelTimestampsById.delete(entry.targetId);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { byId, labelsById, labelTimestampsById, leafId };\n}\n\nexport function getBranchPath(fromId: string | null | undefined, byId: Map<string, SessionEntry>): SessionEntry[] {\n\tconst path: SessionEntry[] = [];\n\tlet current = fromId ? byId.get(fromId) : undefined;\n\twhile (current) {\n\t\tpath.push(current);\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\tpath.reverse();\n\treturn path;\n}\n\nexport function buildSessionTree(\n\tentries: SessionEntry[],\n\tlabelsById: ReadonlyMap<string, string>,\n\tlabelTimestampsById: ReadonlyMap<string, string>,\n): SessionTreeNode[] {\n\tconst nodeMap = new Map<string, SessionTreeNode>();\n\tconst roots: SessionTreeNode[] = [];\n\n\t// Create nodes with resolved labels\n\tfor (const entry of entries) {\n\t\tconst label = labelsById.get(entry.id);\n\t\tconst labelTimestamp = labelTimestampsById.get(entry.id);\n\t\tnodeMap.set(entry.id, { entry, children: [], label, labelTimestamp });\n\t}\n\n\t// Build tree\n\tfor (const entry of entries) {\n\t\tconst node = nodeMap.get(entry.id)!;\n\t\tif (entry.parentId === null || entry.parentId === entry.id) {\n\t\t\troots.push(node);\n\t\t} else {\n\t\t\tconst parent = nodeMap.get(entry.parentId);\n\t\t\tif (parent) {\n\t\t\t\tparent.children.push(node);\n\t\t\t} else {\n\t\t\t\t// Orphan - treat as root\n\t\t\t\troots.push(node);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort children by timestamp (oldest first, newest at bottom)\n\t// Use iterative approach to avoid stack overflow on deep trees\n\tconst stack: SessionTreeNode[] = [...roots];\n\twhile (stack.length > 0) {\n\t\tconst node = stack.pop()!;\n\t\tnode.children.sort((a, b) => new Date(a.entry.timestamp).getTime() - new Date(b.entry.timestamp).getTime());\n\t\tstack.push(...node.children);\n\t}\n\n\treturn roots;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-list.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-list.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAIX,WAAW,EAEX,mBAAmB,EAGnB,MAAM,4BAA4B,CAAC;AAyHpC,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,mBAAmB,EAChC,cAAc,SAAI,EAClB,aAAa,CAAC,EAAE,MAAM,EACtB,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CAqCxB;AAED,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,mBAAmB,EAChC,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CASxB;AAED,wBAAsB,eAAe,CACpC,sBAAsB,CAAC,EAAE,MAAM,GAAG,mBAAmB,EACrD,UAAU,CAAC,EAAE,mBAAmB,EAChC,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CAgExB","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Message, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { existsSync } from \"fs\";\nimport { readdir, readFile, stat } from \"fs/promises\";\nimport { join } from \"path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { parseSessionEntries } from \"./session-manager-migrations.ts\";\nimport { getDefaultSessionDir, getDefaultSessionDirPath } from \"./session-manager-paths.ts\";\nimport {\n\tisInternalHeader,\n\treadSessionHeader,\n\tsessionCwdMatches,\n} from \"./session-manager-storage.ts\";\nimport type {\n\tFileEntry,\n\tSessionEntryBase,\n\tSessionHeader,\n\tSessionInfo,\n\tSessionInfoEntry,\n\tSessionListProgress,\n\tSessionMessageEntry,\n\tSessionWorkflowMetadata,\n} from \"./session-manager-types.ts\";\n\nfunction isMessageWithContent(message: AgentMessage): message is Message {\n\treturn typeof (message as Message).role === \"string\" && \"content\" in message;\n}\n\nfunction extractTextContent(message: Message): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\" \");\n}\n\nfunction getLastActivityTime(entries: FileEntry[]): number | undefined {\n\tlet lastActivityTime: number | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\") continue;\n\n\t\tconst message = (entry as SessionMessageEntry).message;\n\t\tif (!isMessageWithContent(message)) continue;\n\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\tconst msgTimestamp = (message as { timestamp?: number }).timestamp;\n\t\tif (typeof msgTimestamp === \"number\") {\n\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryTimestamp = (entry as SessionEntryBase).timestamp;\n\t\tif (typeof entryTimestamp === \"string\") {\n\t\t\tconst t = new Date(entryTimestamp).getTime();\n\t\t\tif (!Number.isNaN(t)) {\n\t\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lastActivityTime;\n}\n\nfunction getSessionModifiedDate(entries: FileEntry[], header: SessionHeader, statsMtime: Date): Date {\n\tconst lastActivityTime = getLastActivityTime(entries);\n\tif (typeof lastActivityTime === \"number\" && lastActivityTime > 0) {\n\t\treturn new Date(lastActivityTime);\n\t}\n\n\tconst headerTime = typeof header.timestamp === \"string\" ? new Date(header.timestamp).getTime() : NaN;\n\treturn !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;\n}\n\nasync function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf8\");\n\t\tconst entries = parseSessionEntries(content);\n\n\t\tif (entries.length === 0) return null;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\") return null;\n\n\t\tconst stats = await stat(filePath);\n\t\tlet messageCount = 0;\n\t\tlet firstMessage = \"\";\n\t\tconst allMessages: string[] = [];\n\t\tlet name: string | undefined;\n\n\t\tfor (const entry of entries) {\n\t\t\t// Extract session name (use latest, including explicit clears)\n\t\t\tif (entry.type === \"session_info\") {\n\t\t\t\tconst infoEntry = entry as SessionInfoEntry;\n\t\t\t\tname = infoEntry.name?.trim() || undefined;\n\t\t\t}\n\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tmessageCount++;\n\n\t\t\tconst message = (entry as SessionMessageEntry).message;\n\t\t\tif (!isMessageWithContent(message)) continue;\n\t\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\t\tconst textContent = extractTextContent(message);\n\t\t\tif (!textContent) continue;\n\n\t\t\tallMessages.push(textContent);\n\t\t\tif (!firstMessage && message.role === \"user\") {\n\t\t\t\tfirstMessage = textContent;\n\t\t\t}\n\t\t}\n\n\t\tconst cwd = typeof (header as SessionHeader).cwd === \"string\" ? (header as SessionHeader).cwd : \"\";\n\t\tconst parentSessionPath = (header as SessionHeader).parentSession;\n\t\tconst internal = (header as SessionHeader).internal === true ? true : undefined;\n\t\tconst workflowHeader = (header as SessionHeader).workflow as SessionWorkflowMetadata | undefined;\n\t\tconst workflow =\n\t\t\tworkflowHeader && typeof workflowHeader.runId === \"string\" ? workflowHeader : undefined;\n\n\t\tconst modified = getSessionModifiedDate(entries, header as SessionHeader, stats.mtime);\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tid: (header as SessionHeader).id,\n\t\t\tcwd,\n\t\t\tname,\n\t\t\tparentSessionPath,\n\t\t\t...(internal ? { internal } : {}),\n\t\t\t...(workflow ? { workflow } : {}),\n\t\t\tcreated: new Date((header as SessionHeader).timestamp),\n\t\t\tmodified,\n\t\t\tmessageCount,\n\t\t\tfirstMessage: firstMessage || \"(no messages)\",\n\t\t\tallMessagesText: allMessages.join(\" \"),\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function listSessionsFromDir(\n\tdir: string,\n\tonProgress?: SessionListProgress,\n\tprogressOffset = 0,\n\tprogressTotal?: number,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst sessions: SessionInfo[] = [];\n\tif (!existsSync(dir)) {\n\t\treturn sessions;\n\t}\n\n\ttry {\n\t\tconst dirEntries = await readdir(dir);\n\t\tconst files = dirEntries.filter((f) => f.endsWith(\".jsonl\")).map((f) => join(dir, f));\n\t\tconst total = progressTotal ?? files.length;\n\n\t\tlet loaded = 0;\n\t\tconst results = await Promise.all(\n\t\t\tfiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Return empty list on error\n\t}\n\n\treturn sessions;\n}\n\nexport async function listProjectSessions(\n\tcwd: string,\n\tsessionDir?: string,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);\n\tconst filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst sessions = (await listSessionsFromDir(dir, onProgress, 0, undefined, includeInternal)).filter(\n\t\t(session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd),\n\t);\n\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\treturn sessions;\n}\n\nexport async function listAllSessions(\n\tsessionDirOrOnProgress?: string | SessionListProgress,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst customSessionDir =\n\t\ttypeof sessionDirOrOnProgress === \"string\" ? normalizePath(sessionDirOrOnProgress) : undefined;\n\tconst progress = typeof sessionDirOrOnProgress === \"function\" ? sessionDirOrOnProgress : onProgress;\n\tif (customSessionDir) {\n\t\tconst sessions = await listSessionsFromDir(customSessionDir, progress, 0, undefined, includeInternal);\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t}\n\n\tconst sessionsDir = getSessionsDir();\n\n\ttry {\n\t\tif (!existsSync(sessionsDir)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst entries = await readdir(sessionsDir, { withFileTypes: true });\n\t\tconst dirs = entries.filter((e) => e.isDirectory()).map((e) => join(sessionsDir, e.name));\n\n\t\t// Count total files first for accurate progress\n\t\tlet totalFiles = 0;\n\t\tconst dirFiles: string[][] = [];\n\t\tfor (const dir of dirs) {\n\t\t\ttry {\n\t\t\t\tconst files = (await readdir(dir)).filter((f) => f.endsWith(\".jsonl\"));\n\t\t\t\tdirFiles.push(files.map((f) => join(dir, f)));\n\t\t\t\ttotalFiles += files.length;\n\t\t\t} catch {\n\t\t\t\tdirFiles.push([]);\n\t\t\t}\n\t\t}\n\n\t\t// Process all files with progress tracking\n\t\tlet loaded = 0;\n\t\tconst sessions: SessionInfo[] = [];\n\t\tconst allFiles = dirFiles.flat();\n\n\t\tconst results = await Promise.all(\n\t\t\tallFiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-manager-list.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-list.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAIX,WAAW,EAEX,mBAAmB,EAEnB,MAAM,4BAA4B,CAAC;AAuHpC,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,mBAAmB,EAChC,cAAc,SAAI,EAClB,aAAa,CAAC,EAAE,MAAM,EACtB,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CAqCxB;AAED,wBAAsB,mBAAmB,CACxC,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,mBAAmB,EAChC,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CASxB;AAED,wBAAsB,eAAe,CACpC,sBAAsB,CAAC,EAAE,MAAM,GAAG,mBAAmB,EACrD,UAAU,CAAC,EAAE,mBAAmB,EAChC,eAAe,UAAQ,GACrB,OAAO,CAAC,WAAW,EAAE,CAAC,CAgExB","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Message, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { existsSync } from \"fs\";\nimport { readdir, readFile, stat } from \"fs/promises\";\nimport { join } from \"path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { classifiedWorkflowMetadata } from \"./session-manager-classification.ts\";\nimport { parseSessionEntries } from \"./session-manager-migrations.ts\";\nimport { getDefaultSessionDir, getDefaultSessionDirPath } from \"./session-manager-paths.ts\";\nimport {\n\tisInternalHeader,\n\treadSessionHeader,\n\tsessionCwdMatches,\n} from \"./session-manager-storage.ts\";\nimport type {\n\tFileEntry,\n\tSessionEntryBase,\n\tSessionHeader,\n\tSessionInfo,\n\tSessionInfoEntry,\n\tSessionListProgress,\n\tSessionMessageEntry,\n} from \"./session-manager-types.ts\";\n\nfunction isMessageWithContent(message: AgentMessage): message is Message {\n\treturn typeof (message as Message).role === \"string\" && \"content\" in message;\n}\n\nfunction extractTextContent(message: Message): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\" \");\n}\n\nfunction getLastActivityTime(entries: FileEntry[]): number | undefined {\n\tlet lastActivityTime: number | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\") continue;\n\n\t\tconst message = (entry as SessionMessageEntry).message;\n\t\tif (!isMessageWithContent(message)) continue;\n\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\tconst msgTimestamp = (message as { timestamp?: number }).timestamp;\n\t\tif (typeof msgTimestamp === \"number\") {\n\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryTimestamp = (entry as SessionEntryBase).timestamp;\n\t\tif (typeof entryTimestamp === \"string\") {\n\t\t\tconst t = new Date(entryTimestamp).getTime();\n\t\t\tif (!Number.isNaN(t)) {\n\t\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lastActivityTime;\n}\n\nfunction getSessionModifiedDate(entries: FileEntry[], header: SessionHeader, statsMtime: Date): Date {\n\tconst lastActivityTime = getLastActivityTime(entries);\n\tif (typeof lastActivityTime === \"number\" && lastActivityTime > 0) {\n\t\treturn new Date(lastActivityTime);\n\t}\n\n\tconst headerTime = typeof header.timestamp === \"string\" ? new Date(header.timestamp).getTime() : NaN;\n\treturn !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;\n}\n\nasync function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf8\");\n\t\tconst entries = parseSessionEntries(content);\n\n\t\tif (entries.length === 0) return null;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\") return null;\n\n\t\tconst stats = await stat(filePath);\n\t\tlet messageCount = 0;\n\t\tlet firstMessage = \"\";\n\t\tconst allMessages: string[] = [];\n\t\tlet name: string | undefined;\n\n\t\tfor (const entry of entries) {\n\t\t\t// Extract session name (use latest, including explicit clears)\n\t\t\tif (entry.type === \"session_info\") {\n\t\t\t\tconst infoEntry = entry as SessionInfoEntry;\n\t\t\t\tname = infoEntry.name?.trim() || undefined;\n\t\t\t}\n\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tmessageCount++;\n\n\t\t\tconst message = (entry as SessionMessageEntry).message;\n\t\t\tif (!isMessageWithContent(message)) continue;\n\t\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\t\tconst textContent = extractTextContent(message);\n\t\t\tif (!textContent) continue;\n\n\t\t\tallMessages.push(textContent);\n\t\t\tif (!firstMessage && message.role === \"user\") {\n\t\t\t\tfirstMessage = textContent;\n\t\t\t}\n\t\t}\n\n\t\tconst cwd = typeof (header as SessionHeader).cwd === \"string\" ? (header as SessionHeader).cwd : \"\";\n\t\tconst parentSessionPath = (header as SessionHeader).parentSession;\n\t\tconst workflow = classifiedWorkflowMetadata(header as SessionHeader);\n\t\tconst internal = workflow ? true : undefined;\n\n\t\tconst modified = getSessionModifiedDate(entries, header as SessionHeader, stats.mtime);\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tid: (header as SessionHeader).id,\n\t\t\tcwd,\n\t\t\tname,\n\t\t\tparentSessionPath,\n\t\t\t...(internal ? { internal } : {}),\n\t\t\t...(workflow ? { workflow } : {}),\n\t\t\tcreated: new Date((header as SessionHeader).timestamp),\n\t\t\tmodified,\n\t\t\tmessageCount,\n\t\t\tfirstMessage: firstMessage || \"(no messages)\",\n\t\t\tallMessagesText: allMessages.join(\" \"),\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function listSessionsFromDir(\n\tdir: string,\n\tonProgress?: SessionListProgress,\n\tprogressOffset = 0,\n\tprogressTotal?: number,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst sessions: SessionInfo[] = [];\n\tif (!existsSync(dir)) {\n\t\treturn sessions;\n\t}\n\n\ttry {\n\t\tconst dirEntries = await readdir(dir);\n\t\tconst files = dirEntries.filter((f) => f.endsWith(\".jsonl\")).map((f) => join(dir, f));\n\t\tconst total = progressTotal ?? files.length;\n\n\t\tlet loaded = 0;\n\t\tconst results = await Promise.all(\n\t\t\tfiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Return empty list on error\n\t}\n\n\treturn sessions;\n}\n\nexport async function listProjectSessions(\n\tcwd: string,\n\tsessionDir?: string,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);\n\tconst filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst sessions = (await listSessionsFromDir(dir, onProgress, 0, undefined, includeInternal)).filter(\n\t\t(session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd),\n\t);\n\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\treturn sessions;\n}\n\nexport async function listAllSessions(\n\tsessionDirOrOnProgress?: string | SessionListProgress,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst customSessionDir =\n\t\ttypeof sessionDirOrOnProgress === \"string\" ? normalizePath(sessionDirOrOnProgress) : undefined;\n\tconst progress = typeof sessionDirOrOnProgress === \"function\" ? sessionDirOrOnProgress : onProgress;\n\tif (customSessionDir) {\n\t\tconst sessions = await listSessionsFromDir(customSessionDir, progress, 0, undefined, includeInternal);\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t}\n\n\tconst sessionsDir = getSessionsDir();\n\n\ttry {\n\t\tif (!existsSync(sessionsDir)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst entries = await readdir(sessionsDir, { withFileTypes: true });\n\t\tconst dirs = entries.filter((e) => e.isDirectory()).map((e) => join(sessionsDir, e.name));\n\n\t\t// Count total files first for accurate progress\n\t\tlet totalFiles = 0;\n\t\tconst dirFiles: string[][] = [];\n\t\tfor (const dir of dirs) {\n\t\t\ttry {\n\t\t\t\tconst files = (await readdir(dir)).filter((f) => f.endsWith(\".jsonl\"));\n\t\t\t\tdirFiles.push(files.map((f) => join(dir, f)));\n\t\t\t\ttotalFiles += files.length;\n\t\t\t} catch {\n\t\t\t\tdirFiles.push([]);\n\t\t\t}\n\t\t}\n\n\t\t// Process all files with progress tracking\n\t\tlet loaded = 0;\n\t\tconst sessions: SessionInfo[] = [];\n\t\tconst allFiles = dirFiles.flat();\n\n\t\tconst results = await Promise.all(\n\t\t\tallFiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
@@ -3,6 +3,7 @@ import { readdir, readFile, stat } from "fs/promises";
3
3
  import { join } from "path";
4
4
  import { getSessionsDir } from "../config.js";
5
5
  import { normalizePath, resolvePath } from "../utils/paths.js";
6
+ import { classifiedWorkflowMetadata } from "./session-manager-classification.js";
6
7
  import { parseSessionEntries } from "./session-manager-migrations.js";
7
8
  import { getDefaultSessionDir, getDefaultSessionDirPath } from "./session-manager-paths.js";
8
9
  import { isInternalHeader, readSessionHeader, sessionCwdMatches, } from "./session-manager-storage.js";
@@ -90,9 +91,8 @@ async function buildSessionInfo(filePath) {
90
91
  }
91
92
  const cwd = typeof header.cwd === "string" ? header.cwd : "";
92
93
  const parentSessionPath = header.parentSession;
93
- const internal = header.internal === true ? true : undefined;
94
- const workflowHeader = header.workflow;
95
- const workflow = workflowHeader && typeof workflowHeader.runId === "string" ? workflowHeader : undefined;
94
+ const workflow = classifiedWorkflowMetadata(header);
95
+ const internal = workflow ? true : undefined;
96
96
  const modified = getSessionModifiedDate(entries, header, stats.mtime);
97
97
  return {
98
98
  path: filePath,
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-list.js","sourceRoot":"","sources":["../../src/core/session-manager-list.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Message, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { existsSync } from \"fs\";\nimport { readdir, readFile, stat } from \"fs/promises\";\nimport { join } from \"path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { parseSessionEntries } from \"./session-manager-migrations.ts\";\nimport { getDefaultSessionDir, getDefaultSessionDirPath } from \"./session-manager-paths.ts\";\nimport {\n\tisInternalHeader,\n\treadSessionHeader,\n\tsessionCwdMatches,\n} from \"./session-manager-storage.ts\";\nimport type {\n\tFileEntry,\n\tSessionEntryBase,\n\tSessionHeader,\n\tSessionInfo,\n\tSessionInfoEntry,\n\tSessionListProgress,\n\tSessionMessageEntry,\n\tSessionWorkflowMetadata,\n} from \"./session-manager-types.ts\";\n\nfunction isMessageWithContent(message: AgentMessage): message is Message {\n\treturn typeof (message as Message).role === \"string\" && \"content\" in message;\n}\n\nfunction extractTextContent(message: Message): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\" \");\n}\n\nfunction getLastActivityTime(entries: FileEntry[]): number | undefined {\n\tlet lastActivityTime: number | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\") continue;\n\n\t\tconst message = (entry as SessionMessageEntry).message;\n\t\tif (!isMessageWithContent(message)) continue;\n\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\tconst msgTimestamp = (message as { timestamp?: number }).timestamp;\n\t\tif (typeof msgTimestamp === \"number\") {\n\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryTimestamp = (entry as SessionEntryBase).timestamp;\n\t\tif (typeof entryTimestamp === \"string\") {\n\t\t\tconst t = new Date(entryTimestamp).getTime();\n\t\t\tif (!Number.isNaN(t)) {\n\t\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lastActivityTime;\n}\n\nfunction getSessionModifiedDate(entries: FileEntry[], header: SessionHeader, statsMtime: Date): Date {\n\tconst lastActivityTime = getLastActivityTime(entries);\n\tif (typeof lastActivityTime === \"number\" && lastActivityTime > 0) {\n\t\treturn new Date(lastActivityTime);\n\t}\n\n\tconst headerTime = typeof header.timestamp === \"string\" ? new Date(header.timestamp).getTime() : NaN;\n\treturn !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;\n}\n\nasync function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf8\");\n\t\tconst entries = parseSessionEntries(content);\n\n\t\tif (entries.length === 0) return null;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\") return null;\n\n\t\tconst stats = await stat(filePath);\n\t\tlet messageCount = 0;\n\t\tlet firstMessage = \"\";\n\t\tconst allMessages: string[] = [];\n\t\tlet name: string | undefined;\n\n\t\tfor (const entry of entries) {\n\t\t\t// Extract session name (use latest, including explicit clears)\n\t\t\tif (entry.type === \"session_info\") {\n\t\t\t\tconst infoEntry = entry as SessionInfoEntry;\n\t\t\t\tname = infoEntry.name?.trim() || undefined;\n\t\t\t}\n\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tmessageCount++;\n\n\t\t\tconst message = (entry as SessionMessageEntry).message;\n\t\t\tif (!isMessageWithContent(message)) continue;\n\t\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\t\tconst textContent = extractTextContent(message);\n\t\t\tif (!textContent) continue;\n\n\t\t\tallMessages.push(textContent);\n\t\t\tif (!firstMessage && message.role === \"user\") {\n\t\t\t\tfirstMessage = textContent;\n\t\t\t}\n\t\t}\n\n\t\tconst cwd = typeof (header as SessionHeader).cwd === \"string\" ? (header as SessionHeader).cwd : \"\";\n\t\tconst parentSessionPath = (header as SessionHeader).parentSession;\n\t\tconst internal = (header as SessionHeader).internal === true ? true : undefined;\n\t\tconst workflowHeader = (header as SessionHeader).workflow as SessionWorkflowMetadata | undefined;\n\t\tconst workflow =\n\t\t\tworkflowHeader && typeof workflowHeader.runId === \"string\" ? workflowHeader : undefined;\n\n\t\tconst modified = getSessionModifiedDate(entries, header as SessionHeader, stats.mtime);\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tid: (header as SessionHeader).id,\n\t\t\tcwd,\n\t\t\tname,\n\t\t\tparentSessionPath,\n\t\t\t...(internal ? { internal } : {}),\n\t\t\t...(workflow ? { workflow } : {}),\n\t\t\tcreated: new Date((header as SessionHeader).timestamp),\n\t\t\tmodified,\n\t\t\tmessageCount,\n\t\t\tfirstMessage: firstMessage || \"(no messages)\",\n\t\t\tallMessagesText: allMessages.join(\" \"),\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function listSessionsFromDir(\n\tdir: string,\n\tonProgress?: SessionListProgress,\n\tprogressOffset = 0,\n\tprogressTotal?: number,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst sessions: SessionInfo[] = [];\n\tif (!existsSync(dir)) {\n\t\treturn sessions;\n\t}\n\n\ttry {\n\t\tconst dirEntries = await readdir(dir);\n\t\tconst files = dirEntries.filter((f) => f.endsWith(\".jsonl\")).map((f) => join(dir, f));\n\t\tconst total = progressTotal ?? files.length;\n\n\t\tlet loaded = 0;\n\t\tconst results = await Promise.all(\n\t\t\tfiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Return empty list on error\n\t}\n\n\treturn sessions;\n}\n\nexport async function listProjectSessions(\n\tcwd: string,\n\tsessionDir?: string,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);\n\tconst filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst sessions = (await listSessionsFromDir(dir, onProgress, 0, undefined, includeInternal)).filter(\n\t\t(session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd),\n\t);\n\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\treturn sessions;\n}\n\nexport async function listAllSessions(\n\tsessionDirOrOnProgress?: string | SessionListProgress,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst customSessionDir =\n\t\ttypeof sessionDirOrOnProgress === \"string\" ? normalizePath(sessionDirOrOnProgress) : undefined;\n\tconst progress = typeof sessionDirOrOnProgress === \"function\" ? sessionDirOrOnProgress : onProgress;\n\tif (customSessionDir) {\n\t\tconst sessions = await listSessionsFromDir(customSessionDir, progress, 0, undefined, includeInternal);\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t}\n\n\tconst sessionsDir = getSessionsDir();\n\n\ttry {\n\t\tif (!existsSync(sessionsDir)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst entries = await readdir(sessionsDir, { withFileTypes: true });\n\t\tconst dirs = entries.filter((e) => e.isDirectory()).map((e) => join(sessionsDir, e.name));\n\n\t\t// Count total files first for accurate progress\n\t\tlet totalFiles = 0;\n\t\tconst dirFiles: string[][] = [];\n\t\tfor (const dir of dirs) {\n\t\t\ttry {\n\t\t\t\tconst files = (await readdir(dir)).filter((f) => f.endsWith(\".jsonl\"));\n\t\t\t\tdirFiles.push(files.map((f) => join(dir, f)));\n\t\t\t\ttotalFiles += files.length;\n\t\t\t} catch {\n\t\t\t\tdirFiles.push([]);\n\t\t\t}\n\t\t}\n\n\t\t// Process all files with progress tracking\n\t\tlet loaded = 0;\n\t\tconst sessions: SessionInfo[] = [];\n\t\tconst allFiles = dirFiles.flat();\n\n\t\tconst results = await Promise.all(\n\t\t\tallFiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-manager-list.js","sourceRoot":"","sources":["../../src/core/session-manager-list.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Message, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport { existsSync } from \"fs\";\nimport { readdir, readFile, stat } from \"fs/promises\";\nimport { join } from \"path\";\nimport { getSessionsDir } from \"../config.ts\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { classifiedWorkflowMetadata } from \"./session-manager-classification.ts\";\nimport { parseSessionEntries } from \"./session-manager-migrations.ts\";\nimport { getDefaultSessionDir, getDefaultSessionDirPath } from \"./session-manager-paths.ts\";\nimport {\n\tisInternalHeader,\n\treadSessionHeader,\n\tsessionCwdMatches,\n} from \"./session-manager-storage.ts\";\nimport type {\n\tFileEntry,\n\tSessionEntryBase,\n\tSessionHeader,\n\tSessionInfo,\n\tSessionInfoEntry,\n\tSessionListProgress,\n\tSessionMessageEntry,\n} from \"./session-manager-types.ts\";\n\nfunction isMessageWithContent(message: AgentMessage): message is Message {\n\treturn typeof (message as Message).role === \"string\" && \"content\" in message;\n}\n\nfunction extractTextContent(message: Message): string {\n\tconst content = message.content;\n\tif (typeof content === \"string\") {\n\t\treturn content;\n\t}\n\treturn content\n\t\t.filter((block): block is TextContent => block.type === \"text\")\n\t\t.map((block) => block.text)\n\t\t.join(\" \");\n}\n\nfunction getLastActivityTime(entries: FileEntry[]): number | undefined {\n\tlet lastActivityTime: number | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"message\") continue;\n\n\t\tconst message = (entry as SessionMessageEntry).message;\n\t\tif (!isMessageWithContent(message)) continue;\n\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\tconst msgTimestamp = (message as { timestamp?: number }).timestamp;\n\t\tif (typeof msgTimestamp === \"number\") {\n\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, msgTimestamp);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst entryTimestamp = (entry as SessionEntryBase).timestamp;\n\t\tif (typeof entryTimestamp === \"string\") {\n\t\t\tconst t = new Date(entryTimestamp).getTime();\n\t\t\tif (!Number.isNaN(t)) {\n\t\t\t\tlastActivityTime = Math.max(lastActivityTime ?? 0, t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn lastActivityTime;\n}\n\nfunction getSessionModifiedDate(entries: FileEntry[], header: SessionHeader, statsMtime: Date): Date {\n\tconst lastActivityTime = getLastActivityTime(entries);\n\tif (typeof lastActivityTime === \"number\" && lastActivityTime > 0) {\n\t\treturn new Date(lastActivityTime);\n\t}\n\n\tconst headerTime = typeof header.timestamp === \"string\" ? new Date(header.timestamp).getTime() : NaN;\n\treturn !Number.isNaN(headerTime) ? new Date(headerTime) : statsMtime;\n}\n\nasync function buildSessionInfo(filePath: string): Promise<SessionInfo | null> {\n\ttry {\n\t\tconst content = await readFile(filePath, \"utf8\");\n\t\tconst entries = parseSessionEntries(content);\n\n\t\tif (entries.length === 0) return null;\n\t\tconst header = entries[0];\n\t\tif (header.type !== \"session\") return null;\n\n\t\tconst stats = await stat(filePath);\n\t\tlet messageCount = 0;\n\t\tlet firstMessage = \"\";\n\t\tconst allMessages: string[] = [];\n\t\tlet name: string | undefined;\n\n\t\tfor (const entry of entries) {\n\t\t\t// Extract session name (use latest, including explicit clears)\n\t\t\tif (entry.type === \"session_info\") {\n\t\t\t\tconst infoEntry = entry as SessionInfoEntry;\n\t\t\t\tname = infoEntry.name?.trim() || undefined;\n\t\t\t}\n\n\t\t\tif (entry.type !== \"message\") continue;\n\t\t\tmessageCount++;\n\n\t\t\tconst message = (entry as SessionMessageEntry).message;\n\t\t\tif (!isMessageWithContent(message)) continue;\n\t\t\tif (message.role !== \"user\" && message.role !== \"assistant\") continue;\n\n\t\t\tconst textContent = extractTextContent(message);\n\t\t\tif (!textContent) continue;\n\n\t\t\tallMessages.push(textContent);\n\t\t\tif (!firstMessage && message.role === \"user\") {\n\t\t\t\tfirstMessage = textContent;\n\t\t\t}\n\t\t}\n\n\t\tconst cwd = typeof (header as SessionHeader).cwd === \"string\" ? (header as SessionHeader).cwd : \"\";\n\t\tconst parentSessionPath = (header as SessionHeader).parentSession;\n\t\tconst workflow = classifiedWorkflowMetadata(header as SessionHeader);\n\t\tconst internal = workflow ? true : undefined;\n\n\t\tconst modified = getSessionModifiedDate(entries, header as SessionHeader, stats.mtime);\n\n\t\treturn {\n\t\t\tpath: filePath,\n\t\t\tid: (header as SessionHeader).id,\n\t\t\tcwd,\n\t\t\tname,\n\t\t\tparentSessionPath,\n\t\t\t...(internal ? { internal } : {}),\n\t\t\t...(workflow ? { workflow } : {}),\n\t\t\tcreated: new Date((header as SessionHeader).timestamp),\n\t\t\tmodified,\n\t\t\tmessageCount,\n\t\t\tfirstMessage: firstMessage || \"(no messages)\",\n\t\t\tallMessagesText: allMessages.join(\" \"),\n\t\t};\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport async function listSessionsFromDir(\n\tdir: string,\n\tonProgress?: SessionListProgress,\n\tprogressOffset = 0,\n\tprogressTotal?: number,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst sessions: SessionInfo[] = [];\n\tif (!existsSync(dir)) {\n\t\treturn sessions;\n\t}\n\n\ttry {\n\t\tconst dirEntries = await readdir(dir);\n\t\tconst files = dirEntries.filter((f) => f.endsWith(\".jsonl\")).map((f) => join(dir, f));\n\t\tconst total = progressTotal ?? files.length;\n\n\t\tlet loaded = 0;\n\t\tconst results = await Promise.all(\n\t\t\tfiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tonProgress?.(progressOffset + loaded, total);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// Return empty list on error\n\t}\n\n\treturn sessions;\n}\n\nexport async function listProjectSessions(\n\tcwd: string,\n\tsessionDir?: string,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);\n\tconst filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);\n\tconst resolvedCwd = resolvePath(cwd);\n\tconst sessions = (await listSessionsFromDir(dir, onProgress, 0, undefined, includeInternal)).filter(\n\t\t(session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd),\n\t);\n\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\treturn sessions;\n}\n\nexport async function listAllSessions(\n\tsessionDirOrOnProgress?: string | SessionListProgress,\n\tonProgress?: SessionListProgress,\n\tincludeInternal = false,\n): Promise<SessionInfo[]> {\n\tconst customSessionDir =\n\t\ttypeof sessionDirOrOnProgress === \"string\" ? normalizePath(sessionDirOrOnProgress) : undefined;\n\tconst progress = typeof sessionDirOrOnProgress === \"function\" ? sessionDirOrOnProgress : onProgress;\n\tif (customSessionDir) {\n\t\tconst sessions = await listSessionsFromDir(customSessionDir, progress, 0, undefined, includeInternal);\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t}\n\n\tconst sessionsDir = getSessionsDir();\n\n\ttry {\n\t\tif (!existsSync(sessionsDir)) {\n\t\t\treturn [];\n\t\t}\n\t\tconst entries = await readdir(sessionsDir, { withFileTypes: true });\n\t\tconst dirs = entries.filter((e) => e.isDirectory()).map((e) => join(sessionsDir, e.name));\n\n\t\t// Count total files first for accurate progress\n\t\tlet totalFiles = 0;\n\t\tconst dirFiles: string[][] = [];\n\t\tfor (const dir of dirs) {\n\t\t\ttry {\n\t\t\t\tconst files = (await readdir(dir)).filter((f) => f.endsWith(\".jsonl\"));\n\t\t\t\tdirFiles.push(files.map((f) => join(dir, f)));\n\t\t\t\ttotalFiles += files.length;\n\t\t\t} catch {\n\t\t\t\tdirFiles.push([]);\n\t\t\t}\n\t\t}\n\n\t\t// Process all files with progress tracking\n\t\tlet loaded = 0;\n\t\tconst sessions: SessionInfo[] = [];\n\t\tconst allFiles = dirFiles.flat();\n\n\t\tconst results = await Promise.all(\n\t\t\tallFiles.map(async (file) => {\n\t\t\t\t// Prefilter via the header so hidden/internal sessions are skipped\n\t\t\t\t// before the expensive full-transcript parse in buildSessionInfo.\n\t\t\t\tif (!includeInternal && isInternalHeader(readSessionHeader(file))) {\n\t\t\t\t\tloaded++;\n\t\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t\tconst info = await buildSessionInfo(file);\n\t\t\t\tloaded++;\n\t\t\t\tprogress?.(loaded, totalFiles);\n\t\t\t\treturn info;\n\t\t\t}),\n\t\t);\n\n\t\tfor (const info of results) {\n\t\t\tif (info && (includeInternal || !info.internal)) {\n\t\t\t\tsessions.push(info);\n\t\t\t}\n\t\t}\n\n\t\tsessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\treturn sessions;\n\t} catch {\n\t\treturn [];\n\t}\n}\n"]}
@@ -2,7 +2,7 @@ import type { FileEntry, SessionHeader } from "./session-manager-types.ts";
2
2
  /** Exported for testing */
3
3
  export declare function loadEntriesFromFile(filePath: string): FileEntry[];
4
4
  export declare function readSessionHeader(filePath: string): SessionHeader | null;
5
- /** Returns true when a session header marks the session as internal (e.g. a workflow stage session). */
5
+ /** Returns true only for workflow-owned sessions with a complete classification marker. */
6
6
  export declare function isInternalHeader(header: SessionHeader | null | undefined): boolean;
7
7
  export declare function getSessionHeaderCwd(header: SessionHeader): string | undefined;
8
8
  export declare function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-storage.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-storage.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAgB,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAoBzF,2BAA2B;AAC3B,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CA0CjE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA6CxE;AAED,wGAAwG;AACxG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAElF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAG7E;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAEvF;AAED,2BAA2B;AAC3B,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,eAAe,UAAQ,GAAG,MAAM,GAAG,IAAI,CAqB9G;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAEpE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAEhF;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAE3E;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAIjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAEjE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAIjD","sourcesContent":["import {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport type { FileEntry, SessionEntry, SessionHeader } from \"./session-manager-types.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\n/**\n * Dedicated small read chunk for header-only reads. Session headers are small\n * (typically a few KB), so reading in 64KB chunks avoids allocating/transferring\n * the full 1MiB transcript buffer just to inspect the first line during listing\n * and resume-history prefiltering.\n */\nconst HEADER_READ_BUFFER_SIZE = 64 * 1024;\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Exported for testing */\nexport function loadEntriesFromFile(filePath: string): FileEntry[] {\n\tconst resolvedFilePath = normalizePath(filePath);\n\tif (!existsSync(resolvedFilePath)) return [];\n\n\tconst entries: FileEntry[] = [];\n\tconst fd = openSync(resolvedFilePath, \"r\");\n\ttry {\n\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\t\tlet pending = \"\";\n\n\t\twhile (true) {\n\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\tif (bytesRead === 0) break;\n\n\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\tlet lineStart = 0;\n\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t}\n\t\t\tpending = pending.slice(lineStart);\n\t\t}\n\n\t\tpending += decoder.end();\n\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\tif (finalEntry) entries.push(finalEntry);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\n\t// Validate session header\n\tif (entries.length === 0) return entries;\n\tconst header = entries[0];\n\tif (header.type !== \"session\" || !(\"id\" in header) || typeof header.id !== \"string\") {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\nexport function readSessionHeader(filePath: string): SessionHeader | null {\n\ttry {\n\t\tconst fd = openSync(filePath, \"r\");\n\t\ttry {\n\t\t\t// Read the full first line rather than a fixed 512-byte window so very\n\t\t\t// long headers (e.g. internal workflow headers carrying stage metadata)\n\t\t\t// are not truncated and dropped from listing/resume filtering.\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\t// Use a small dedicated header buffer instead of the 1MiB transcript\n\t\t\t// buffer so prefiltering internal sessions during listing stays cheap.\n\t\t\t// The loop still reads in chunks until the first newline (or EOF) so\n\t\t\t// headers larger than one chunk are handled correctly.\n\t\t\tconst buffer = Buffer.allocUnsafe(HEADER_READ_BUFFER_SIZE);\n\t\t\tlet pending = \"\";\n\t\t\tlet foundNewline = false;\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tconst newlineIndex = pending.indexOf(\"\\n\");\n\t\t\t\tif (newlineIndex !== -1) {\n\t\t\t\t\tpending = pending.slice(0, newlineIndex);\n\t\t\t\t\tfoundNewline = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Only flush the decoder when we hit EOF without a newline. Once a\n\t\t\t// newline was found, any remaining decoder bytes belong to data after\n\t\t\t// the header line; flushing them would corrupt the parsed header.\n\t\t\tif (!foundNewline) {\n\t\t\t\tpending += decoder.end();\n\t\t\t}\n\t\t\tconst firstLine = pending.split(\"\\n\")[0];\n\t\t\tif (!firstLine) return null;\n\t\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\t\tif (header.type !== \"session\" || typeof header.id !== \"string\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn header as unknown as SessionHeader;\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Returns true when a session header marks the session as internal (e.g. a workflow stage session). */\nexport function isInternalHeader(header: SessionHeader | null | undefined): boolean {\n\treturn header?.internal === true;\n}\n\nexport function getSessionHeaderCwd(header: SessionHeader): string | undefined {\n\tconst cwd = (header as { cwd?: unknown }).cwd;\n\treturn typeof cwd === \"string\" ? cwd : undefined;\n}\n\nexport function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean {\n\treturn cwd !== undefined && cwd !== \"\" && resolvePath(cwd) === resolvedCwd;\n}\n\n/** Exported for testing */\nexport function findMostRecentSession(sessionDir: string, cwd?: string, includeInternal = false): string | null {\n\tconst resolvedSessionDir = normalizePath(sessionDir);\n\tconst resolvedCwd = cwd ? resolvePath(cwd) : undefined;\n\ttry {\n\t\tconst files = readdirSync(resolvedSessionDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedSessionDir, f))\n\t\t\t.map((path) => ({ path, header: readSessionHeader(path) }))\n\t\t\t.filter(\n\t\t\t\t(file): file is { path: string; header: SessionHeader } =>\n\t\t\t\t\tfile.header !== null &&\n\t\t\t\t\t(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)) &&\n\t\t\t\t\t(includeInternal || !isInternalHeader(file.header)),\n\t\t\t)\n\t\t\t.map(({ path }) => ({ path, mtime: statSync(path).mtime }))\n\t\t\t.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n\n\t\treturn files[0]?.path || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function serializeSessionEntries(entries: FileEntry[]): string {\n\treturn `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n}\n\nexport function writeSessionEntries(filePath: string, entries: FileEntry[]): void {\n\twriteFileSync(filePath, serializeSessionEntries(entries));\n}\n\nexport function appendSessionEntry(filePath: string, entry: FileEntry): void {\n\tappendFileSync(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\nexport function appendSessionEntries(filePath: string, entries: FileEntry[]): void {\n\tfor (const entry of entries) {\n\t\tappendSessionEntry(filePath, entry);\n\t}\n}\n\nexport function hasAssistantMessage(entries: FileEntry[]): boolean {\n\treturn entries.some((entry): entry is SessionEntry => entry.type === \"message\" && entry.message.role === \"assistant\");\n}\n\nexport function ensureDirectory(dir: string): void {\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-manager-storage.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-storage.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAgB,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAoBzF,2BAA2B;AAC3B,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,CA0CjE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA6CxE;AAED,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAElF;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAG7E;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAEvF;AAED,2BAA2B;AAC3B,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,eAAe,UAAQ,GAAG,MAAM,GAAG,IAAI,CAqB9G;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAEpE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAEhF;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAE3E;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAIjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAEjE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAIjD","sourcesContent":["import {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { isClassifiedWorkflowSession } from \"./session-manager-classification.ts\";\nimport type { FileEntry, SessionEntry, SessionHeader } from \"./session-manager-types.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\n/**\n * Dedicated small read chunk for header-only reads. Session headers are small\n * (typically a few KB), so reading in 64KB chunks avoids allocating/transferring\n * the full 1MiB transcript buffer just to inspect the first line during listing\n * and resume-history prefiltering.\n */\nconst HEADER_READ_BUFFER_SIZE = 64 * 1024;\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Exported for testing */\nexport function loadEntriesFromFile(filePath: string): FileEntry[] {\n\tconst resolvedFilePath = normalizePath(filePath);\n\tif (!existsSync(resolvedFilePath)) return [];\n\n\tconst entries: FileEntry[] = [];\n\tconst fd = openSync(resolvedFilePath, \"r\");\n\ttry {\n\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\t\tlet pending = \"\";\n\n\t\twhile (true) {\n\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\tif (bytesRead === 0) break;\n\n\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\tlet lineStart = 0;\n\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t}\n\t\t\tpending = pending.slice(lineStart);\n\t\t}\n\n\t\tpending += decoder.end();\n\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\tif (finalEntry) entries.push(finalEntry);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\n\t// Validate session header\n\tif (entries.length === 0) return entries;\n\tconst header = entries[0];\n\tif (header.type !== \"session\" || !(\"id\" in header) || typeof header.id !== \"string\") {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\nexport function readSessionHeader(filePath: string): SessionHeader | null {\n\ttry {\n\t\tconst fd = openSync(filePath, \"r\");\n\t\ttry {\n\t\t\t// Read the full first line rather than a fixed 512-byte window so very\n\t\t\t// long headers (e.g. internal workflow headers carrying stage metadata)\n\t\t\t// are not truncated and dropped from listing/resume filtering.\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\t// Use a small dedicated header buffer instead of the 1MiB transcript\n\t\t\t// buffer so prefiltering internal sessions during listing stays cheap.\n\t\t\t// The loop still reads in chunks until the first newline (or EOF) so\n\t\t\t// headers larger than one chunk are handled correctly.\n\t\t\tconst buffer = Buffer.allocUnsafe(HEADER_READ_BUFFER_SIZE);\n\t\t\tlet pending = \"\";\n\t\t\tlet foundNewline = false;\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tconst newlineIndex = pending.indexOf(\"\\n\");\n\t\t\t\tif (newlineIndex !== -1) {\n\t\t\t\t\tpending = pending.slice(0, newlineIndex);\n\t\t\t\t\tfoundNewline = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Only flush the decoder when we hit EOF without a newline. Once a\n\t\t\t// newline was found, any remaining decoder bytes belong to data after\n\t\t\t// the header line; flushing them would corrupt the parsed header.\n\t\t\tif (!foundNewline) {\n\t\t\t\tpending += decoder.end();\n\t\t\t}\n\t\t\tconst firstLine = pending.split(\"\\n\")[0];\n\t\t\tif (!firstLine) return null;\n\t\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\t\tif (header.type !== \"session\" || typeof header.id !== \"string\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn header as unknown as SessionHeader;\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Returns true only for workflow-owned sessions with a complete classification marker. */\nexport function isInternalHeader(header: SessionHeader | null | undefined): boolean {\n\treturn isClassifiedWorkflowSession(header);\n}\n\nexport function getSessionHeaderCwd(header: SessionHeader): string | undefined {\n\tconst cwd = (header as { cwd?: unknown }).cwd;\n\treturn typeof cwd === \"string\" ? cwd : undefined;\n}\n\nexport function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean {\n\treturn cwd !== undefined && cwd !== \"\" && resolvePath(cwd) === resolvedCwd;\n}\n\n/** Exported for testing */\nexport function findMostRecentSession(sessionDir: string, cwd?: string, includeInternal = false): string | null {\n\tconst resolvedSessionDir = normalizePath(sessionDir);\n\tconst resolvedCwd = cwd ? resolvePath(cwd) : undefined;\n\ttry {\n\t\tconst files = readdirSync(resolvedSessionDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedSessionDir, f))\n\t\t\t.map((path) => ({ path, header: readSessionHeader(path) }))\n\t\t\t.filter(\n\t\t\t\t(file): file is { path: string; header: SessionHeader } =>\n\t\t\t\t\tfile.header !== null &&\n\t\t\t\t\t(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)) &&\n\t\t\t\t\t(includeInternal || !isInternalHeader(file.header)),\n\t\t\t)\n\t\t\t.map(({ path }) => ({ path, mtime: statSync(path).mtime }))\n\t\t\t.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n\n\t\treturn files[0]?.path || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function serializeSessionEntries(entries: FileEntry[]): string {\n\treturn `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n}\n\nexport function writeSessionEntries(filePath: string, entries: FileEntry[]): void {\n\twriteFileSync(filePath, serializeSessionEntries(entries));\n}\n\nexport function appendSessionEntry(filePath: string, entry: FileEntry): void {\n\tappendFileSync(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\nexport function appendSessionEntries(filePath: string, entries: FileEntry[]): void {\n\tfor (const entry of entries) {\n\t\tappendSessionEntry(filePath, entry);\n\t}\n}\n\nexport function hasAssistantMessage(entries: FileEntry[]): boolean {\n\treturn entries.some((entry): entry is SessionEntry => entry.type === \"message\" && entry.message.role === \"assistant\");\n}\n\nexport function ensureDirectory(dir: string): void {\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n"]}
@@ -2,6 +2,7 @@ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync
2
2
  import { join } from "path";
3
3
  import { StringDecoder } from "string_decoder";
4
4
  import { normalizePath, resolvePath } from "../utils/paths.js";
5
+ import { isClassifiedWorkflowSession } from "./session-manager-classification.js";
5
6
  const SESSION_READ_BUFFER_SIZE = 1024 * 1024;
6
7
  /**
7
8
  * Dedicated small read chunk for header-only reads. Session headers are small
@@ -114,9 +115,9 @@ export function readSessionHeader(filePath) {
114
115
  return null;
115
116
  }
116
117
  }
117
- /** Returns true when a session header marks the session as internal (e.g. a workflow stage session). */
118
+ /** Returns true only for workflow-owned sessions with a complete classification marker. */
118
119
  export function isInternalHeader(header) {
119
- return header?.internal === true;
120
+ return isClassifiedWorkflowSession(header);
120
121
  }
121
122
  export function getSessionHeaderCwd(header) {
122
123
  const cwd = header.cwd;
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-storage.js","sourceRoot":"","sources":["../../src/core/session-manager-storage.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport type { FileEntry, SessionEntry, SessionHeader } from \"./session-manager-types.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\n/**\n * Dedicated small read chunk for header-only reads. Session headers are small\n * (typically a few KB), so reading in 64KB chunks avoids allocating/transferring\n * the full 1MiB transcript buffer just to inspect the first line during listing\n * and resume-history prefiltering.\n */\nconst HEADER_READ_BUFFER_SIZE = 64 * 1024;\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Exported for testing */\nexport function loadEntriesFromFile(filePath: string): FileEntry[] {\n\tconst resolvedFilePath = normalizePath(filePath);\n\tif (!existsSync(resolvedFilePath)) return [];\n\n\tconst entries: FileEntry[] = [];\n\tconst fd = openSync(resolvedFilePath, \"r\");\n\ttry {\n\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\t\tlet pending = \"\";\n\n\t\twhile (true) {\n\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\tif (bytesRead === 0) break;\n\n\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\tlet lineStart = 0;\n\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t}\n\t\t\tpending = pending.slice(lineStart);\n\t\t}\n\n\t\tpending += decoder.end();\n\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\tif (finalEntry) entries.push(finalEntry);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\n\t// Validate session header\n\tif (entries.length === 0) return entries;\n\tconst header = entries[0];\n\tif (header.type !== \"session\" || !(\"id\" in header) || typeof header.id !== \"string\") {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\nexport function readSessionHeader(filePath: string): SessionHeader | null {\n\ttry {\n\t\tconst fd = openSync(filePath, \"r\");\n\t\ttry {\n\t\t\t// Read the full first line rather than a fixed 512-byte window so very\n\t\t\t// long headers (e.g. internal workflow headers carrying stage metadata)\n\t\t\t// are not truncated and dropped from listing/resume filtering.\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\t// Use a small dedicated header buffer instead of the 1MiB transcript\n\t\t\t// buffer so prefiltering internal sessions during listing stays cheap.\n\t\t\t// The loop still reads in chunks until the first newline (or EOF) so\n\t\t\t// headers larger than one chunk are handled correctly.\n\t\t\tconst buffer = Buffer.allocUnsafe(HEADER_READ_BUFFER_SIZE);\n\t\t\tlet pending = \"\";\n\t\t\tlet foundNewline = false;\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tconst newlineIndex = pending.indexOf(\"\\n\");\n\t\t\t\tif (newlineIndex !== -1) {\n\t\t\t\t\tpending = pending.slice(0, newlineIndex);\n\t\t\t\t\tfoundNewline = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Only flush the decoder when we hit EOF without a newline. Once a\n\t\t\t// newline was found, any remaining decoder bytes belong to data after\n\t\t\t// the header line; flushing them would corrupt the parsed header.\n\t\t\tif (!foundNewline) {\n\t\t\t\tpending += decoder.end();\n\t\t\t}\n\t\t\tconst firstLine = pending.split(\"\\n\")[0];\n\t\t\tif (!firstLine) return null;\n\t\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\t\tif (header.type !== \"session\" || typeof header.id !== \"string\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn header as unknown as SessionHeader;\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Returns true when a session header marks the session as internal (e.g. a workflow stage session). */\nexport function isInternalHeader(header: SessionHeader | null | undefined): boolean {\n\treturn header?.internal === true;\n}\n\nexport function getSessionHeaderCwd(header: SessionHeader): string | undefined {\n\tconst cwd = (header as { cwd?: unknown }).cwd;\n\treturn typeof cwd === \"string\" ? cwd : undefined;\n}\n\nexport function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean {\n\treturn cwd !== undefined && cwd !== \"\" && resolvePath(cwd) === resolvedCwd;\n}\n\n/** Exported for testing */\nexport function findMostRecentSession(sessionDir: string, cwd?: string, includeInternal = false): string | null {\n\tconst resolvedSessionDir = normalizePath(sessionDir);\n\tconst resolvedCwd = cwd ? resolvePath(cwd) : undefined;\n\ttry {\n\t\tconst files = readdirSync(resolvedSessionDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedSessionDir, f))\n\t\t\t.map((path) => ({ path, header: readSessionHeader(path) }))\n\t\t\t.filter(\n\t\t\t\t(file): file is { path: string; header: SessionHeader } =>\n\t\t\t\t\tfile.header !== null &&\n\t\t\t\t\t(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)) &&\n\t\t\t\t\t(includeInternal || !isInternalHeader(file.header)),\n\t\t\t)\n\t\t\t.map(({ path }) => ({ path, mtime: statSync(path).mtime }))\n\t\t\t.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n\n\t\treturn files[0]?.path || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function serializeSessionEntries(entries: FileEntry[]): string {\n\treturn `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n}\n\nexport function writeSessionEntries(filePath: string, entries: FileEntry[]): void {\n\twriteFileSync(filePath, serializeSessionEntries(entries));\n}\n\nexport function appendSessionEntry(filePath: string, entry: FileEntry): void {\n\tappendFileSync(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\nexport function appendSessionEntries(filePath: string, entries: FileEntry[]): void {\n\tfor (const entry of entries) {\n\t\tappendSessionEntry(filePath, entry);\n\t}\n}\n\nexport function hasAssistantMessage(entries: FileEntry[]): boolean {\n\treturn entries.some((entry): entry is SessionEntry => entry.type === \"message\" && entry.message.role === \"assistant\");\n}\n\nexport function ensureDirectory(dir: string): void {\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n"]}
1
+ {"version":3,"file":"session-manager-storage.js","sourceRoot":"","sources":["../../src/core/session-manager-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,cAAc,EACd,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,aAAa,GACb,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAGlF,MAAM,wBAAwB,GAAG,IAAI,GAAG,IAAI,CAAC;AAC7C;;;;;GAKG;AACH,MAAM,uBAAuB,GAAG,EAAE,GAAG,IAAI,CAAC;AAE1C,SAAS,qBAAqB,CAAC,IAAY;IAC1C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAc,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IACnD,MAAM,gBAAgB,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,EAAE,CAAC;IAE7C,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,MAAM,EAAE,GAAG,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;IAC3C,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,wBAAwB,CAAC,CAAC;QAC5D,IAAI,OAAO,GAAG,EAAE,CAAC;QAEjB,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC/D,IAAI,SAAS,KAAK,CAAC;gBAAE,MAAM;YAE3B,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;YACxD,IAAI,SAAS,GAAG,CAAC,CAAC;YAClB,IAAI,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACpD,OAAO,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;gBAC5B,MAAM,KAAK,GAAG,qBAAqB,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;gBAC5E,IAAI,KAAK;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC/B,SAAS,GAAG,YAAY,GAAG,CAAC,CAAC;gBAC7B,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACjD,CAAC;YACD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QAED,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,UAAU;YAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC;YAAS,CAAC;QACV,SAAS,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;IAED,0BAA0B;IAC1B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;QACrF,OAAO,EAAE,CAAC;IACX,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IACjD,IAAI,CAAC;QACJ,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACnC,IAAI,CAAC;YACJ,uEAAuE;YACvE,wEAAwE;YACxE,+DAA+D;YAC/D,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;YAC1C,qEAAqE;YACrE,uEAAuE;YACvE,qEAAqE;YACrE,uDAAuD;YACvD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAC;YAC3D,IAAI,OAAO,GAAG,EAAE,CAAC;YACjB,IAAI,YAAY,GAAG,KAAK,CAAC;YACzB,OAAO,IAAI,EAAE,CAAC;gBACb,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC/D,IAAI,SAAS,KAAK,CAAC;oBAAE,MAAM;gBAC3B,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;gBACxD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;oBACzB,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;oBACzC,YAAY,GAAG,IAAI,CAAC;oBACpB,MAAM;gBACP,CAAC;YACF,CAAC;YACD,mEAAmE;YACnE,sEAAsE;YACtE,kEAAkE;YAClE,IAAI,CAAC,YAAY,EAAE,CAAC;gBACnB,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAC1B,CAAC;YACD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,SAAS;gBAAE,OAAO,IAAI,CAAC;YAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAA4B,CAAC;YAChE,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;gBAChE,OAAO,IAAI,CAAC;YACb,CAAC;YACD,OAAO,MAAkC,CAAC;QAC3C,CAAC;gBAAS,CAAC;YACV,SAAS,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAAC,MAAwC;IACxE,OAAO,2BAA2B,CAAC,MAAM,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAqB;IACxD,MAAM,GAAG,GAAI,MAA4B,CAAC,GAAG,CAAC;IAC9C,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAuB,EAAE,WAAmB;IAC7E,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,WAAW,CAAC;AAC5E,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,qBAAqB,CAAC,UAAkB,EAAE,GAAY,EAAE,eAAe,GAAG,KAAK;IAC9F,MAAM,kBAAkB,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG,WAAW,CAAC,kBAAkB,CAAC;aAC3C,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;aACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;aACvC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC1D,MAAM,CACN,CAAC,IAAI,EAAmD,EAAE,CACzD,IAAI,CAAC,MAAM,KAAK,IAAI;YACpB,CAAC,CAAC,WAAW,IAAI,iBAAiB,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;YAClF,CAAC,eAAe,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CACpD;aACA,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;aAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAExD,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAoB;IAC3D,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,OAAoB;IACzE,aAAa,CAAC,QAAQ,EAAE,uBAAuB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,KAAgB;IACpE,cAAc,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,OAAoB;IAC1E,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,kBAAkB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAoB;IACvD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAyB,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;AACvH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAW;IAC1C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrC,CAAC;AACF,CAAC","sourcesContent":["import {\n\tappendFileSync,\n\tcloseSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadSync,\n\tstatSync,\n\twriteFileSync,\n} from \"fs\";\nimport { join } from \"path\";\nimport { StringDecoder } from \"string_decoder\";\nimport { normalizePath, resolvePath } from \"../utils/paths.ts\";\nimport { isClassifiedWorkflowSession } from \"./session-manager-classification.ts\";\nimport type { FileEntry, SessionEntry, SessionHeader } from \"./session-manager-types.ts\";\n\nconst SESSION_READ_BUFFER_SIZE = 1024 * 1024;\n/**\n * Dedicated small read chunk for header-only reads. Session headers are small\n * (typically a few KB), so reading in 64KB chunks avoids allocating/transferring\n * the full 1MiB transcript buffer just to inspect the first line during listing\n * and resume-history prefiltering.\n */\nconst HEADER_READ_BUFFER_SIZE = 64 * 1024;\n\nfunction parseSessionEntryLine(line: string): FileEntry | null {\n\tif (!line.trim()) return null;\n\ttry {\n\t\treturn JSON.parse(line) as FileEntry;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Exported for testing */\nexport function loadEntriesFromFile(filePath: string): FileEntry[] {\n\tconst resolvedFilePath = normalizePath(filePath);\n\tif (!existsSync(resolvedFilePath)) return [];\n\n\tconst entries: FileEntry[] = [];\n\tconst fd = openSync(resolvedFilePath, \"r\");\n\ttry {\n\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\tconst buffer = Buffer.allocUnsafe(SESSION_READ_BUFFER_SIZE);\n\t\tlet pending = \"\";\n\n\t\twhile (true) {\n\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\tif (bytesRead === 0) break;\n\n\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\tlet lineStart = 0;\n\t\t\tlet newlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\twhile (newlineIndex !== -1) {\n\t\t\t\tconst entry = parseSessionEntryLine(pending.slice(lineStart, newlineIndex));\n\t\t\t\tif (entry) entries.push(entry);\n\t\t\t\tlineStart = newlineIndex + 1;\n\t\t\t\tnewlineIndex = pending.indexOf(\"\\n\", lineStart);\n\t\t\t}\n\t\t\tpending = pending.slice(lineStart);\n\t\t}\n\n\t\tpending += decoder.end();\n\t\tconst finalEntry = parseSessionEntryLine(pending);\n\t\tif (finalEntry) entries.push(finalEntry);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\n\t// Validate session header\n\tif (entries.length === 0) return entries;\n\tconst header = entries[0];\n\tif (header.type !== \"session\" || !(\"id\" in header) || typeof header.id !== \"string\") {\n\t\treturn [];\n\t}\n\n\treturn entries;\n}\n\nexport function readSessionHeader(filePath: string): SessionHeader | null {\n\ttry {\n\t\tconst fd = openSync(filePath, \"r\");\n\t\ttry {\n\t\t\t// Read the full first line rather than a fixed 512-byte window so very\n\t\t\t// long headers (e.g. internal workflow headers carrying stage metadata)\n\t\t\t// are not truncated and dropped from listing/resume filtering.\n\t\t\tconst decoder = new StringDecoder(\"utf8\");\n\t\t\t// Use a small dedicated header buffer instead of the 1MiB transcript\n\t\t\t// buffer so prefiltering internal sessions during listing stays cheap.\n\t\t\t// The loop still reads in chunks until the first newline (or EOF) so\n\t\t\t// headers larger than one chunk are handled correctly.\n\t\t\tconst buffer = Buffer.allocUnsafe(HEADER_READ_BUFFER_SIZE);\n\t\t\tlet pending = \"\";\n\t\t\tlet foundNewline = false;\n\t\t\twhile (true) {\n\t\t\t\tconst bytesRead = readSync(fd, buffer, 0, buffer.length, null);\n\t\t\t\tif (bytesRead === 0) break;\n\t\t\t\tpending += decoder.write(buffer.subarray(0, bytesRead));\n\t\t\t\tconst newlineIndex = pending.indexOf(\"\\n\");\n\t\t\t\tif (newlineIndex !== -1) {\n\t\t\t\t\tpending = pending.slice(0, newlineIndex);\n\t\t\t\t\tfoundNewline = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Only flush the decoder when we hit EOF without a newline. Once a\n\t\t\t// newline was found, any remaining decoder bytes belong to data after\n\t\t\t// the header line; flushing them would corrupt the parsed header.\n\t\t\tif (!foundNewline) {\n\t\t\t\tpending += decoder.end();\n\t\t\t}\n\t\t\tconst firstLine = pending.split(\"\\n\")[0];\n\t\t\tif (!firstLine) return null;\n\t\t\tconst header = JSON.parse(firstLine) as Record<string, unknown>;\n\t\t\tif (header.type !== \"session\" || typeof header.id !== \"string\") {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn header as unknown as SessionHeader;\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Returns true only for workflow-owned sessions with a complete classification marker. */\nexport function isInternalHeader(header: SessionHeader | null | undefined): boolean {\n\treturn isClassifiedWorkflowSession(header);\n}\n\nexport function getSessionHeaderCwd(header: SessionHeader): string | undefined {\n\tconst cwd = (header as { cwd?: unknown }).cwd;\n\treturn typeof cwd === \"string\" ? cwd : undefined;\n}\n\nexport function sessionCwdMatches(cwd: string | undefined, resolvedCwd: string): boolean {\n\treturn cwd !== undefined && cwd !== \"\" && resolvePath(cwd) === resolvedCwd;\n}\n\n/** Exported for testing */\nexport function findMostRecentSession(sessionDir: string, cwd?: string, includeInternal = false): string | null {\n\tconst resolvedSessionDir = normalizePath(sessionDir);\n\tconst resolvedCwd = cwd ? resolvePath(cwd) : undefined;\n\ttry {\n\t\tconst files = readdirSync(resolvedSessionDir)\n\t\t\t.filter((f) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f) => join(resolvedSessionDir, f))\n\t\t\t.map((path) => ({ path, header: readSessionHeader(path) }))\n\t\t\t.filter(\n\t\t\t\t(file): file is { path: string; header: SessionHeader } =>\n\t\t\t\t\tfile.header !== null &&\n\t\t\t\t\t(!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)) &&\n\t\t\t\t\t(includeInternal || !isInternalHeader(file.header)),\n\t\t\t)\n\t\t\t.map(({ path }) => ({ path, mtime: statSync(path).mtime }))\n\t\t\t.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());\n\n\t\treturn files[0]?.path || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function serializeSessionEntries(entries: FileEntry[]): string {\n\treturn `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n}\n\nexport function writeSessionEntries(filePath: string, entries: FileEntry[]): void {\n\twriteFileSync(filePath, serializeSessionEntries(entries));\n}\n\nexport function appendSessionEntry(filePath: string, entry: FileEntry): void {\n\tappendFileSync(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\nexport function appendSessionEntries(filePath: string, entries: FileEntry[]): void {\n\tfor (const entry of entries) {\n\t\tappendSessionEntry(filePath, entry);\n\t}\n}\n\nexport function hasAssistantMessage(entries: FileEntry[]): boolean {\n\treturn entries.some((entry): entry is SessionEntry => entry.type === \"message\" && entry.message.role === \"assistant\");\n}\n\nexport function ensureDirectory(dir: string): void {\n\tif (!existsSync(dir)) {\n\t\tmkdirSync(dir, { recursive: true });\n\t}\n}\n"]}
@@ -55,24 +55,17 @@ export interface ModelChangeEntry extends SessionEntryBase {
55
55
  provider: string;
56
56
  modelId: string;
57
57
  }
58
+ /** Durable compaction boundary. Active entries carry `details.strategy === "verbatim-lines"`. */
58
59
  export interface CompactionEntry<T = unknown> extends SessionEntryBase {
59
60
  type: "compaction";
60
61
  summary: string;
61
62
  firstKeptEntryId: string;
62
63
  tokensBefore: number;
63
- /** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */
64
64
  details?: T;
65
- /** True if generated by an extension, undefined/false if pi-generated (backward compatible) */
65
+ /** True when the compacted text was supplied by an extension hook. */
66
66
  fromHook?: boolean;
67
67
  }
68
- export type ContextDeletionTarget = {
69
- kind: "entry";
70
- entryId: string;
71
- } | {
72
- kind: "content_block";
73
- entryId: string;
74
- blockIndex: number;
75
- };
68
+ /** @deprecated Legacy archival statistics; never produced or applied. */
76
69
  export interface ContextCompactionStats {
77
70
  objectsBefore: number;
78
71
  objectsAfter: number;
@@ -81,10 +74,18 @@ export interface ContextCompactionStats {
81
74
  tokensAfter: number;
82
75
  percentReduction: number;
83
76
  }
77
+ /** @deprecated Legacy archival record; never produced or applied. */
84
78
  export interface ContextCompactionEntry extends SessionEntryBase {
85
79
  type: "context_compaction";
86
80
  promptVersion: 1;
87
- deletedTargets: ContextDeletionTarget[];
81
+ deletedTargets: Array<{
82
+ kind: "entry";
83
+ entryId: string;
84
+ } | {
85
+ kind: "content_block";
86
+ entryId: string;
87
+ blockIndex: number;
88
+ }>;
88
89
  protectedEntryIds: string[];
89
90
  stats: ContextCompactionStats;
90
91
  backupPath?: string;
@@ -184,10 +185,8 @@ export interface SessionInfo {
184
185
  messageCount: number;
185
186
  firstMessage: string;
186
187
  allMessagesText: string;
187
- }
188
- export interface ContextDeletionFilters {
189
- deletedEntryIds: Set<string>;
190
- deletedContentBlocks: Map<string, Set<number>>;
188
+ /** Optional semantic color for synthetic selector rows. */
189
+ messageColor?: "success" | "warning" | "accent";
191
190
  }
192
191
  export type SessionListProgress = (loaded: number, total: number) => void;
193
192
  export type ReadonlySessionManager = Pick<SessionManager, "getCwd" | "getSessionDir" | "usesDefaultSessionDir" | "getSessionId" | "getSessionFile" | "getLeafId" | "getLeafEntry" | "getEntry" | "getLabel" | "getBranch" | "getHeader" | "getEntries" | "getTree" | "getSessionName">;
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-types.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport type { SessionManager } from \"./session-manager-core.ts\";\n\nexport const CURRENT_SESSION_VERSION = 3;\n\n/**\n * Workflow linkage persisted on internal (workflow-created) session headers.\n * Used to exclude workflow stage sessions from the standard `/resume` history\n * while keeping them resumable through the workflow-specific resume path.\n */\nexport interface SessionWorkflowMetadata {\n\trunId: string;\n\tstageId: string;\n\tstageName: string;\n}\n\nexport interface SessionHeader {\n\ttype: \"session\";\n\tversion?: number; // v1 sessions don't have this\n\tid: string;\n\ttimestamp: string;\n\tcwd: string;\n\tparentSession?: string;\n\t/** Marks sessions created by automated machinery (e.g. workflow stages) so they can be excluded from standard resume history. */\n\tinternal?: boolean;\n\t/** When internal, links the session to its owning workflow run/stage. */\n\tworkflow?: SessionWorkflowMetadata;\n}\n\nexport interface NewSessionOptions {\n\tid?: string;\n\tparentSession?: string;\n\t/** Mark the new session as internal (e.g. a workflow stage session). */\n\tinternal?: boolean;\n\t/** Workflow run/stage linkage for internal workflow sessions. */\n\tworkflow?: SessionWorkflowMetadata;\n}\n\nexport interface SessionEntryBase {\n\ttype: string;\n\tid: string;\n\tparentId: string | null;\n\ttimestamp: string;\n}\n\nexport interface SessionMessageEntry extends SessionEntryBase {\n\ttype: \"message\";\n\tmessage: AgentMessage;\n}\n\nexport interface ThinkingLevelChangeEntry extends SessionEntryBase {\n\ttype: \"thinking_level_change\";\n\tthinkingLevel: string;\n}\n\nexport interface ContextWindowChangeEntry extends SessionEntryBase {\n\ttype: \"context_window_change\";\n\tcontextWindow: number;\n}\n\nexport interface ModelChangeEntry extends SessionEntryBase {\n\ttype: \"model_change\";\n\tprovider: string;\n\tmodelId: string;\n}\n\nexport interface CompactionEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"compaction\";\n\tsummary: string;\n\tfirstKeptEntryId: string;\n\ttokensBefore: number;\n\t/** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */\n\tdetails?: T;\n\t/** True if generated by an extension, undefined/false if pi-generated (backward compatible) */\n\tfromHook?: boolean;\n}\n\nexport type ContextDeletionTarget =\n\t| { kind: \"entry\"; entryId: string }\n\t| { kind: \"content_block\"; entryId: string; blockIndex: number };\n\nexport interface ContextCompactionStats {\n\tobjectsBefore: number;\n\tobjectsAfter: number;\n\tobjectsDeleted: number;\n\ttokensBefore: number;\n\ttokensAfter: number;\n\tpercentReduction: number;\n}\n\nexport interface ContextCompactionEntry extends SessionEntryBase {\n\ttype: \"context_compaction\";\n\tpromptVersion: 1;\n\tdeletedTargets: ContextDeletionTarget[];\n\tprotectedEntryIds: string[];\n\tstats: ContextCompactionStats;\n\tbackupPath?: string;\n}\n\nexport interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"branch_summary\";\n\tfromId: string;\n\tsummary: string;\n\t/** Extension-specific data (not sent to LLM) */\n\tdetails?: T;\n\t/** True if generated by an extension, false if pi-generated */\n\tfromHook?: boolean;\n}\n\n/**\n * Custom entry for extensions to store extension-specific data in the session.\n * Use customType to identify your extension's entries.\n *\n * Purpose: Persist extension state across session reloads. On reload, extensions can\n * scan entries for their customType and reconstruct internal state.\n *\n * Does NOT participate in LLM context (ignored by buildSessionContext).\n * For injecting content into context, see CustomMessageEntry.\n */\nexport interface CustomEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"custom\";\n\tcustomType: string;\n\tdata?: T;\n}\n\n/** Label entry for user-defined bookmarks/markers on entries. */\nexport interface LabelEntry extends SessionEntryBase {\n\ttype: \"label\";\n\ttargetId: string;\n\tlabel: string | undefined;\n}\n\n/** Session metadata entry (e.g., user-defined display name). */\nexport interface SessionInfoEntry extends SessionEntryBase {\n\ttype: \"session_info\";\n\tname?: string;\n}\n\n/**\n * Custom message entry for extensions to inject messages into LLM context.\n * Use customType to identify your extension's entries.\n *\n * Unlike CustomEntry, this usually participates in LLM context.\n * The content is converted to a user message in buildSessionContext() unless\n * excludeFromContext is true. Use details for extension-specific metadata (not sent to LLM).\n *\n * display controls TUI rendering:\n * - false: hidden entirely\n * - true: rendered with distinct styling (different from user messages)\n */\nexport interface CustomMessageEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"custom_message\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdetails?: T;\n\tdisplay: boolean;\n\texcludeFromContext?: boolean;\n}\n\n/** Session entry - has id/parentId for tree structure (returned by \"read\" methods in SessionManager) */\nexport type SessionEntry =\n\t| SessionMessageEntry\n\t| ThinkingLevelChangeEntry\n\t| ContextWindowChangeEntry\n\t| ModelChangeEntry\n\t| CompactionEntry\n\t| ContextCompactionEntry\n\t| BranchSummaryEntry\n\t| CustomEntry\n\t| CustomMessageEntry\n\t| LabelEntry\n\t| SessionInfoEntry;\n\n/** Raw file entry (includes header) */\nexport type FileEntry = SessionHeader | SessionEntry;\n\n/** Tree node for getTree() - defensive copy of session structure */\nexport interface SessionTreeNode {\n\tentry: SessionEntry;\n\tchildren: SessionTreeNode[];\n\t/** Resolved label for this entry, if any */\n\tlabel?: string;\n\t/** Timestamp of the latest label change for this entry, if any */\n\tlabelTimestamp?: string;\n}\n\nexport interface SessionContext {\n\tmessages: AgentMessage[];\n\tthinkingLevel: string;\n\tcontextWindow: number | undefined;\n\tmodel: { provider: string; modelId: string } | null;\n}\n\nexport interface SessionInfo {\n\tpath: string;\n\tid: string;\n\t/** Working directory where the session was started. Empty string for old sessions. */\n\tcwd: string;\n\t/** User-defined display name from session_info entries. */\n\tname?: string;\n\t/** Path to the parent session (if this session was forked). */\n\tparentSessionPath?: string;\n\t/** True when this session was created by automated machinery (e.g. a workflow stage). */\n\tinternal?: boolean;\n\t/** Workflow run/stage linkage when the session is an internal workflow session. */\n\tworkflow?: SessionWorkflowMetadata;\n\tcreated: Date;\n\tmodified: Date;\n\tmessageCount: number;\n\tfirstMessage: string;\n\tallMessagesText: string;\n}\n\nexport interface ContextDeletionFilters {\n\tdeletedEntryIds: Set<string>;\n\tdeletedContentBlocks: Map<string, Set<number>>;\n}\n\nexport type SessionListProgress = (loaded: number, total: number) => void;\n\nexport type ReadonlySessionManager = Pick<\n\tSessionManager,\n\t| \"getCwd\"\n\t| \"getSessionDir\"\n\t| \"usesDefaultSessionDir\"\n\t| \"getSessionId\"\n\t| \"getSessionFile\"\n\t| \"getLeafId\"\n\t| \"getLeafEntry\"\n\t| \"getEntry\"\n\t| \"getLabel\"\n\t| \"getBranch\"\n\t| \"getHeader\"\n\t| \"getEntries\"\n\t| \"getTree\"\n\t| \"getSessionName\"\n>;\n"]}
1
+ {"version":3,"file":"session-manager-types.d.ts","sourceRoot":"","sources":["../../src/core/session-manager-types.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport type { SessionManager } from \"./session-manager-core.ts\";\n\nexport const CURRENT_SESSION_VERSION = 3;\n\n/**\n * Workflow linkage persisted on internal (workflow-created) session headers.\n * Used to exclude workflow stage sessions from the standard `/resume` history\n * while keeping them resumable through the workflow-specific resume path.\n */\nexport interface SessionWorkflowMetadata {\n\trunId: string;\n\tstageId: string;\n\tstageName: string;\n}\n\nexport interface SessionHeader {\n\ttype: \"session\";\n\tversion?: number; // v1 sessions don't have this\n\tid: string;\n\ttimestamp: string;\n\tcwd: string;\n\tparentSession?: string;\n\t/** Marks sessions created by automated machinery (e.g. workflow stages) so they can be excluded from standard resume history. */\n\tinternal?: boolean;\n\t/** When internal, links the session to its owning workflow run/stage. */\n\tworkflow?: SessionWorkflowMetadata;\n}\n\nexport interface NewSessionOptions {\n\tid?: string;\n\tparentSession?: string;\n\t/** Mark the new session as internal (e.g. a workflow stage session). */\n\tinternal?: boolean;\n\t/** Workflow run/stage linkage for internal workflow sessions. */\n\tworkflow?: SessionWorkflowMetadata;\n}\n\nexport interface SessionEntryBase {\n\ttype: string;\n\tid: string;\n\tparentId: string | null;\n\ttimestamp: string;\n}\n\nexport interface SessionMessageEntry extends SessionEntryBase {\n\ttype: \"message\";\n\tmessage: AgentMessage;\n}\n\nexport interface ThinkingLevelChangeEntry extends SessionEntryBase {\n\ttype: \"thinking_level_change\";\n\tthinkingLevel: string;\n}\n\nexport interface ContextWindowChangeEntry extends SessionEntryBase {\n\ttype: \"context_window_change\";\n\tcontextWindow: number;\n}\n\nexport interface ModelChangeEntry extends SessionEntryBase {\n\ttype: \"model_change\";\n\tprovider: string;\n\tmodelId: string;\n}\n\n/** Durable compaction boundary. Active entries carry `details.strategy === \"verbatim-lines\"`. */\nexport interface CompactionEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"compaction\";\n\tsummary: string;\n\tfirstKeptEntryId: string;\n\ttokensBefore: number;\n\tdetails?: T;\n\t/** True when the compacted text was supplied by an extension hook. */\n\tfromHook?: boolean;\n}\n\n/** @deprecated Legacy archival statistics; never produced or applied. */\nexport interface ContextCompactionStats {\n\tobjectsBefore: number;\n\tobjectsAfter: number;\n\tobjectsDeleted: number;\n\ttokensBefore: number;\n\ttokensAfter: number;\n\tpercentReduction: number;\n}\n\n/** @deprecated Legacy archival record; never produced or applied. */\nexport interface ContextCompactionEntry extends SessionEntryBase {\n\ttype: \"context_compaction\";\n\tpromptVersion: 1;\n\tdeletedTargets: Array<{ kind: \"entry\"; entryId: string } | { kind: \"content_block\"; entryId: string; blockIndex: number }>;\n\tprotectedEntryIds: string[];\n\tstats: ContextCompactionStats;\n\tbackupPath?: string;\n}\n\nexport interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"branch_summary\";\n\tfromId: string;\n\tsummary: string;\n\t/** Extension-specific data (not sent to LLM) */\n\tdetails?: T;\n\t/** True if generated by an extension, false if pi-generated */\n\tfromHook?: boolean;\n}\n\n/**\n * Custom entry for extensions to store extension-specific data in the session.\n * Use customType to identify your extension's entries.\n *\n * Purpose: Persist extension state across session reloads. On reload, extensions can\n * scan entries for their customType and reconstruct internal state.\n *\n * Does NOT participate in LLM context (ignored by buildSessionContext).\n * For injecting content into context, see CustomMessageEntry.\n */\nexport interface CustomEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"custom\";\n\tcustomType: string;\n\tdata?: T;\n}\n\n/** Label entry for user-defined bookmarks/markers on entries. */\nexport interface LabelEntry extends SessionEntryBase {\n\ttype: \"label\";\n\ttargetId: string;\n\tlabel: string | undefined;\n}\n\n/** Session metadata entry (e.g., user-defined display name). */\nexport interface SessionInfoEntry extends SessionEntryBase {\n\ttype: \"session_info\";\n\tname?: string;\n}\n\n/**\n * Custom message entry for extensions to inject messages into LLM context.\n * Use customType to identify your extension's entries.\n *\n * Unlike CustomEntry, this usually participates in LLM context.\n * The content is converted to a user message in buildSessionContext() unless\n * excludeFromContext is true. Use details for extension-specific metadata (not sent to LLM).\n *\n * display controls TUI rendering:\n * - false: hidden entirely\n * - true: rendered with distinct styling (different from user messages)\n */\nexport interface CustomMessageEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"custom_message\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdetails?: T;\n\tdisplay: boolean;\n\texcludeFromContext?: boolean;\n}\n\n/** Session entry - has id/parentId for tree structure (returned by \"read\" methods in SessionManager) */\nexport type SessionEntry =\n\t| SessionMessageEntry\n\t| ThinkingLevelChangeEntry\n\t| ContextWindowChangeEntry\n\t| ModelChangeEntry\n\t| CompactionEntry\n\t| ContextCompactionEntry\n\t| BranchSummaryEntry\n\t| CustomEntry\n\t| CustomMessageEntry\n\t| LabelEntry\n\t| SessionInfoEntry;\n\n/** Raw file entry (includes header) */\nexport type FileEntry = SessionHeader | SessionEntry;\n\n/** Tree node for getTree() - defensive copy of session structure */\nexport interface SessionTreeNode {\n\tentry: SessionEntry;\n\tchildren: SessionTreeNode[];\n\t/** Resolved label for this entry, if any */\n\tlabel?: string;\n\t/** Timestamp of the latest label change for this entry, if any */\n\tlabelTimestamp?: string;\n}\n\nexport interface SessionContext {\n\tmessages: AgentMessage[];\n\tthinkingLevel: string;\n\tcontextWindow: number | undefined;\n\tmodel: { provider: string; modelId: string } | null;\n}\n\nexport interface SessionInfo {\n\tpath: string;\n\tid: string;\n\t/** Working directory where the session was started. Empty string for old sessions. */\n\tcwd: string;\n\t/** User-defined display name from session_info entries. */\n\tname?: string;\n\t/** Path to the parent session (if this session was forked). */\n\tparentSessionPath?: string;\n\t/** True when this session was created by automated machinery (e.g. a workflow stage). */\n\tinternal?: boolean;\n\t/** Workflow run/stage linkage when the session is an internal workflow session. */\n\tworkflow?: SessionWorkflowMetadata;\n\tcreated: Date;\n\tmodified: Date;\n\tmessageCount: number;\n\tfirstMessage: string;\n\tallMessagesText: string;\n\t/** Optional semantic color for synthetic selector rows. */\n\tmessageColor?: \"success\" | \"warning\" | \"accent\";\n}\n\n\nexport type SessionListProgress = (loaded: number, total: number) => void;\n\nexport type ReadonlySessionManager = Pick<\n\tSessionManager,\n\t| \"getCwd\"\n\t| \"getSessionDir\"\n\t| \"usesDefaultSessionDir\"\n\t| \"getSessionId\"\n\t| \"getSessionFile\"\n\t| \"getLeafId\"\n\t| \"getLeafEntry\"\n\t| \"getEntry\"\n\t| \"getLabel\"\n\t| \"getBranch\"\n\t| \"getHeader\"\n\t| \"getEntries\"\n\t| \"getTree\"\n\t| \"getSessionName\"\n>;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-manager-types.js","sourceRoot":"","sources":["../../src/core/session-manager-types.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport type { SessionManager } from \"./session-manager-core.ts\";\n\nexport const CURRENT_SESSION_VERSION = 3;\n\n/**\n * Workflow linkage persisted on internal (workflow-created) session headers.\n * Used to exclude workflow stage sessions from the standard `/resume` history\n * while keeping them resumable through the workflow-specific resume path.\n */\nexport interface SessionWorkflowMetadata {\n\trunId: string;\n\tstageId: string;\n\tstageName: string;\n}\n\nexport interface SessionHeader {\n\ttype: \"session\";\n\tversion?: number; // v1 sessions don't have this\n\tid: string;\n\ttimestamp: string;\n\tcwd: string;\n\tparentSession?: string;\n\t/** Marks sessions created by automated machinery (e.g. workflow stages) so they can be excluded from standard resume history. */\n\tinternal?: boolean;\n\t/** When internal, links the session to its owning workflow run/stage. */\n\tworkflow?: SessionWorkflowMetadata;\n}\n\nexport interface NewSessionOptions {\n\tid?: string;\n\tparentSession?: string;\n\t/** Mark the new session as internal (e.g. a workflow stage session). */\n\tinternal?: boolean;\n\t/** Workflow run/stage linkage for internal workflow sessions. */\n\tworkflow?: SessionWorkflowMetadata;\n}\n\nexport interface SessionEntryBase {\n\ttype: string;\n\tid: string;\n\tparentId: string | null;\n\ttimestamp: string;\n}\n\nexport interface SessionMessageEntry extends SessionEntryBase {\n\ttype: \"message\";\n\tmessage: AgentMessage;\n}\n\nexport interface ThinkingLevelChangeEntry extends SessionEntryBase {\n\ttype: \"thinking_level_change\";\n\tthinkingLevel: string;\n}\n\nexport interface ContextWindowChangeEntry extends SessionEntryBase {\n\ttype: \"context_window_change\";\n\tcontextWindow: number;\n}\n\nexport interface ModelChangeEntry extends SessionEntryBase {\n\ttype: \"model_change\";\n\tprovider: string;\n\tmodelId: string;\n}\n\nexport interface CompactionEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"compaction\";\n\tsummary: string;\n\tfirstKeptEntryId: string;\n\ttokensBefore: number;\n\t/** Extension-specific data (e.g., ArtifactIndex, version markers for structured compaction) */\n\tdetails?: T;\n\t/** True if generated by an extension, undefined/false if pi-generated (backward compatible) */\n\tfromHook?: boolean;\n}\n\nexport type ContextDeletionTarget =\n\t| { kind: \"entry\"; entryId: string }\n\t| { kind: \"content_block\"; entryId: string; blockIndex: number };\n\nexport interface ContextCompactionStats {\n\tobjectsBefore: number;\n\tobjectsAfter: number;\n\tobjectsDeleted: number;\n\ttokensBefore: number;\n\ttokensAfter: number;\n\tpercentReduction: number;\n}\n\nexport interface ContextCompactionEntry extends SessionEntryBase {\n\ttype: \"context_compaction\";\n\tpromptVersion: 1;\n\tdeletedTargets: ContextDeletionTarget[];\n\tprotectedEntryIds: string[];\n\tstats: ContextCompactionStats;\n\tbackupPath?: string;\n}\n\nexport interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"branch_summary\";\n\tfromId: string;\n\tsummary: string;\n\t/** Extension-specific data (not sent to LLM) */\n\tdetails?: T;\n\t/** True if generated by an extension, false if pi-generated */\n\tfromHook?: boolean;\n}\n\n/**\n * Custom entry for extensions to store extension-specific data in the session.\n * Use customType to identify your extension's entries.\n *\n * Purpose: Persist extension state across session reloads. On reload, extensions can\n * scan entries for their customType and reconstruct internal state.\n *\n * Does NOT participate in LLM context (ignored by buildSessionContext).\n * For injecting content into context, see CustomMessageEntry.\n */\nexport interface CustomEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"custom\";\n\tcustomType: string;\n\tdata?: T;\n}\n\n/** Label entry for user-defined bookmarks/markers on entries. */\nexport interface LabelEntry extends SessionEntryBase {\n\ttype: \"label\";\n\ttargetId: string;\n\tlabel: string | undefined;\n}\n\n/** Session metadata entry (e.g., user-defined display name). */\nexport interface SessionInfoEntry extends SessionEntryBase {\n\ttype: \"session_info\";\n\tname?: string;\n}\n\n/**\n * Custom message entry for extensions to inject messages into LLM context.\n * Use customType to identify your extension's entries.\n *\n * Unlike CustomEntry, this usually participates in LLM context.\n * The content is converted to a user message in buildSessionContext() unless\n * excludeFromContext is true. Use details for extension-specific metadata (not sent to LLM).\n *\n * display controls TUI rendering:\n * - false: hidden entirely\n * - true: rendered with distinct styling (different from user messages)\n */\nexport interface CustomMessageEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"custom_message\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdetails?: T;\n\tdisplay: boolean;\n\texcludeFromContext?: boolean;\n}\n\n/** Session entry - has id/parentId for tree structure (returned by \"read\" methods in SessionManager) */\nexport type SessionEntry =\n\t| SessionMessageEntry\n\t| ThinkingLevelChangeEntry\n\t| ContextWindowChangeEntry\n\t| ModelChangeEntry\n\t| CompactionEntry\n\t| ContextCompactionEntry\n\t| BranchSummaryEntry\n\t| CustomEntry\n\t| CustomMessageEntry\n\t| LabelEntry\n\t| SessionInfoEntry;\n\n/** Raw file entry (includes header) */\nexport type FileEntry = SessionHeader | SessionEntry;\n\n/** Tree node for getTree() - defensive copy of session structure */\nexport interface SessionTreeNode {\n\tentry: SessionEntry;\n\tchildren: SessionTreeNode[];\n\t/** Resolved label for this entry, if any */\n\tlabel?: string;\n\t/** Timestamp of the latest label change for this entry, if any */\n\tlabelTimestamp?: string;\n}\n\nexport interface SessionContext {\n\tmessages: AgentMessage[];\n\tthinkingLevel: string;\n\tcontextWindow: number | undefined;\n\tmodel: { provider: string; modelId: string } | null;\n}\n\nexport interface SessionInfo {\n\tpath: string;\n\tid: string;\n\t/** Working directory where the session was started. Empty string for old sessions. */\n\tcwd: string;\n\t/** User-defined display name from session_info entries. */\n\tname?: string;\n\t/** Path to the parent session (if this session was forked). */\n\tparentSessionPath?: string;\n\t/** True when this session was created by automated machinery (e.g. a workflow stage). */\n\tinternal?: boolean;\n\t/** Workflow run/stage linkage when the session is an internal workflow session. */\n\tworkflow?: SessionWorkflowMetadata;\n\tcreated: Date;\n\tmodified: Date;\n\tmessageCount: number;\n\tfirstMessage: string;\n\tallMessagesText: string;\n}\n\nexport interface ContextDeletionFilters {\n\tdeletedEntryIds: Set<string>;\n\tdeletedContentBlocks: Map<string, Set<number>>;\n}\n\nexport type SessionListProgress = (loaded: number, total: number) => void;\n\nexport type ReadonlySessionManager = Pick<\n\tSessionManager,\n\t| \"getCwd\"\n\t| \"getSessionDir\"\n\t| \"usesDefaultSessionDir\"\n\t| \"getSessionId\"\n\t| \"getSessionFile\"\n\t| \"getLeafId\"\n\t| \"getLeafEntry\"\n\t| \"getEntry\"\n\t| \"getLabel\"\n\t| \"getBranch\"\n\t| \"getHeader\"\n\t| \"getEntries\"\n\t| \"getTree\"\n\t| \"getSessionName\"\n>;\n"]}
1
+ {"version":3,"file":"session-manager-types.js","sourceRoot":"","sources":["../../src/core/session-manager-types.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai/compat\";\nimport type { SessionManager } from \"./session-manager-core.ts\";\n\nexport const CURRENT_SESSION_VERSION = 3;\n\n/**\n * Workflow linkage persisted on internal (workflow-created) session headers.\n * Used to exclude workflow stage sessions from the standard `/resume` history\n * while keeping them resumable through the workflow-specific resume path.\n */\nexport interface SessionWorkflowMetadata {\n\trunId: string;\n\tstageId: string;\n\tstageName: string;\n}\n\nexport interface SessionHeader {\n\ttype: \"session\";\n\tversion?: number; // v1 sessions don't have this\n\tid: string;\n\ttimestamp: string;\n\tcwd: string;\n\tparentSession?: string;\n\t/** Marks sessions created by automated machinery (e.g. workflow stages) so they can be excluded from standard resume history. */\n\tinternal?: boolean;\n\t/** When internal, links the session to its owning workflow run/stage. */\n\tworkflow?: SessionWorkflowMetadata;\n}\n\nexport interface NewSessionOptions {\n\tid?: string;\n\tparentSession?: string;\n\t/** Mark the new session as internal (e.g. a workflow stage session). */\n\tinternal?: boolean;\n\t/** Workflow run/stage linkage for internal workflow sessions. */\n\tworkflow?: SessionWorkflowMetadata;\n}\n\nexport interface SessionEntryBase {\n\ttype: string;\n\tid: string;\n\tparentId: string | null;\n\ttimestamp: string;\n}\n\nexport interface SessionMessageEntry extends SessionEntryBase {\n\ttype: \"message\";\n\tmessage: AgentMessage;\n}\n\nexport interface ThinkingLevelChangeEntry extends SessionEntryBase {\n\ttype: \"thinking_level_change\";\n\tthinkingLevel: string;\n}\n\nexport interface ContextWindowChangeEntry extends SessionEntryBase {\n\ttype: \"context_window_change\";\n\tcontextWindow: number;\n}\n\nexport interface ModelChangeEntry extends SessionEntryBase {\n\ttype: \"model_change\";\n\tprovider: string;\n\tmodelId: string;\n}\n\n/** Durable compaction boundary. Active entries carry `details.strategy === \"verbatim-lines\"`. */\nexport interface CompactionEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"compaction\";\n\tsummary: string;\n\tfirstKeptEntryId: string;\n\ttokensBefore: number;\n\tdetails?: T;\n\t/** True when the compacted text was supplied by an extension hook. */\n\tfromHook?: boolean;\n}\n\n/** @deprecated Legacy archival statistics; never produced or applied. */\nexport interface ContextCompactionStats {\n\tobjectsBefore: number;\n\tobjectsAfter: number;\n\tobjectsDeleted: number;\n\ttokensBefore: number;\n\ttokensAfter: number;\n\tpercentReduction: number;\n}\n\n/** @deprecated Legacy archival record; never produced or applied. */\nexport interface ContextCompactionEntry extends SessionEntryBase {\n\ttype: \"context_compaction\";\n\tpromptVersion: 1;\n\tdeletedTargets: Array<{ kind: \"entry\"; entryId: string } | { kind: \"content_block\"; entryId: string; blockIndex: number }>;\n\tprotectedEntryIds: string[];\n\tstats: ContextCompactionStats;\n\tbackupPath?: string;\n}\n\nexport interface BranchSummaryEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"branch_summary\";\n\tfromId: string;\n\tsummary: string;\n\t/** Extension-specific data (not sent to LLM) */\n\tdetails?: T;\n\t/** True if generated by an extension, false if pi-generated */\n\tfromHook?: boolean;\n}\n\n/**\n * Custom entry for extensions to store extension-specific data in the session.\n * Use customType to identify your extension's entries.\n *\n * Purpose: Persist extension state across session reloads. On reload, extensions can\n * scan entries for their customType and reconstruct internal state.\n *\n * Does NOT participate in LLM context (ignored by buildSessionContext).\n * For injecting content into context, see CustomMessageEntry.\n */\nexport interface CustomEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"custom\";\n\tcustomType: string;\n\tdata?: T;\n}\n\n/** Label entry for user-defined bookmarks/markers on entries. */\nexport interface LabelEntry extends SessionEntryBase {\n\ttype: \"label\";\n\ttargetId: string;\n\tlabel: string | undefined;\n}\n\n/** Session metadata entry (e.g., user-defined display name). */\nexport interface SessionInfoEntry extends SessionEntryBase {\n\ttype: \"session_info\";\n\tname?: string;\n}\n\n/**\n * Custom message entry for extensions to inject messages into LLM context.\n * Use customType to identify your extension's entries.\n *\n * Unlike CustomEntry, this usually participates in LLM context.\n * The content is converted to a user message in buildSessionContext() unless\n * excludeFromContext is true. Use details for extension-specific metadata (not sent to LLM).\n *\n * display controls TUI rendering:\n * - false: hidden entirely\n * - true: rendered with distinct styling (different from user messages)\n */\nexport interface CustomMessageEntry<T = unknown> extends SessionEntryBase {\n\ttype: \"custom_message\";\n\tcustomType: string;\n\tcontent: string | (TextContent | ImageContent)[];\n\tdetails?: T;\n\tdisplay: boolean;\n\texcludeFromContext?: boolean;\n}\n\n/** Session entry - has id/parentId for tree structure (returned by \"read\" methods in SessionManager) */\nexport type SessionEntry =\n\t| SessionMessageEntry\n\t| ThinkingLevelChangeEntry\n\t| ContextWindowChangeEntry\n\t| ModelChangeEntry\n\t| CompactionEntry\n\t| ContextCompactionEntry\n\t| BranchSummaryEntry\n\t| CustomEntry\n\t| CustomMessageEntry\n\t| LabelEntry\n\t| SessionInfoEntry;\n\n/** Raw file entry (includes header) */\nexport type FileEntry = SessionHeader | SessionEntry;\n\n/** Tree node for getTree() - defensive copy of session structure */\nexport interface SessionTreeNode {\n\tentry: SessionEntry;\n\tchildren: SessionTreeNode[];\n\t/** Resolved label for this entry, if any */\n\tlabel?: string;\n\t/** Timestamp of the latest label change for this entry, if any */\n\tlabelTimestamp?: string;\n}\n\nexport interface SessionContext {\n\tmessages: AgentMessage[];\n\tthinkingLevel: string;\n\tcontextWindow: number | undefined;\n\tmodel: { provider: string; modelId: string } | null;\n}\n\nexport interface SessionInfo {\n\tpath: string;\n\tid: string;\n\t/** Working directory where the session was started. Empty string for old sessions. */\n\tcwd: string;\n\t/** User-defined display name from session_info entries. */\n\tname?: string;\n\t/** Path to the parent session (if this session was forked). */\n\tparentSessionPath?: string;\n\t/** True when this session was created by automated machinery (e.g. a workflow stage). */\n\tinternal?: boolean;\n\t/** Workflow run/stage linkage when the session is an internal workflow session. */\n\tworkflow?: SessionWorkflowMetadata;\n\tcreated: Date;\n\tmodified: Date;\n\tmessageCount: number;\n\tfirstMessage: string;\n\tallMessagesText: string;\n\t/** Optional semantic color for synthetic selector rows. */\n\tmessageColor?: \"success\" | \"warning\" | \"accent\";\n}\n\n\nexport type SessionListProgress = (loaded: number, total: number) => void;\n\nexport type ReadonlySessionManager = Pick<\n\tSessionManager,\n\t| \"getCwd\"\n\t| \"getSessionDir\"\n\t| \"usesDefaultSessionDir\"\n\t| \"getSessionId\"\n\t| \"getSessionFile\"\n\t| \"getLeafId\"\n\t| \"getLeafEntry\"\n\t| \"getEntry\"\n\t| \"getLabel\"\n\t| \"getBranch\"\n\t| \"getHeader\"\n\t| \"getEntries\"\n\t| \"getTree\"\n\t| \"getSessionName\"\n>;\n"]}