apex-code 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (512) hide show
  1. package/CHANGELOG.md +263 -5
  2. package/README.md +624 -0
  3. package/dist/bun/cli.d.ts +3 -1
  4. package/dist/bun/cli.d.ts.map +1 -1
  5. package/dist/bun/cli.js +3 -9
  6. package/dist/bun/cli.js.map +1 -1
  7. package/dist/bun/runtime-setup.d.ts +2 -0
  8. package/dist/bun/runtime-setup.d.ts.map +1 -0
  9. package/dist/bun/runtime-setup.js +9 -0
  10. package/dist/bun/runtime-setup.js.map +1 -0
  11. package/dist/bun/sandbox-env-setup.d.ts +2 -0
  12. package/dist/bun/sandbox-env-setup.d.ts.map +1 -0
  13. package/dist/bun/sandbox-env-setup.js +4 -0
  14. package/dist/bun/sandbox-env-setup.js.map +1 -0
  15. package/dist/cli/args.d.ts.map +1 -1
  16. package/dist/cli/args.js +16 -5
  17. package/dist/cli/args.js.map +1 -1
  18. package/dist/cli/config-selector.d.ts.map +1 -1
  19. package/dist/cli/config-selector.js +2 -1
  20. package/dist/cli/config-selector.js.map +1 -1
  21. package/dist/cli/file-processor.d.ts +1 -1
  22. package/dist/cli/file-processor.d.ts.map +1 -1
  23. package/dist/cli/file-processor.js.map +1 -1
  24. package/dist/cli/session-picker.d.ts +1 -1
  25. package/dist/cli/session-picker.d.ts.map +1 -1
  26. package/dist/cli/session-picker.js.map +1 -1
  27. package/dist/cli/setup.d.ts +2 -0
  28. package/dist/cli/setup.d.ts.map +1 -0
  29. package/dist/cli/setup.js +13 -0
  30. package/dist/cli/setup.js.map +1 -0
  31. package/dist/config.d.ts +1 -0
  32. package/dist/config.d.ts.map +1 -1
  33. package/dist/config.js +1 -0
  34. package/dist/config.js.map +1 -1
  35. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  36. package/dist/core/agent-session-runtime.js +16 -8
  37. package/dist/core/agent-session-runtime.js.map +1 -1
  38. package/dist/core/agent-session.d.ts +85 -15
  39. package/dist/core/agent-session.d.ts.map +1 -1
  40. package/dist/core/agent-session.js +685 -266
  41. package/dist/core/agent-session.js.map +1 -1
  42. package/dist/core/auth-storage.d.ts +4 -0
  43. package/dist/core/auth-storage.d.ts.map +1 -1
  44. package/dist/core/auth-storage.js +55 -0
  45. package/dist/core/auth-storage.js.map +1 -1
  46. package/dist/core/bug-report.d.ts +176 -0
  47. package/dist/core/bug-report.d.ts.map +1 -0
  48. package/dist/core/bug-report.js +291 -0
  49. package/dist/core/bug-report.js.map +1 -0
  50. package/dist/core/cache-stats.d.ts.map +1 -1
  51. package/dist/core/cache-stats.js +12 -1
  52. package/dist/core/cache-stats.js.map +1 -1
  53. package/dist/core/cache-warmer.d.ts +103 -0
  54. package/dist/core/cache-warmer.d.ts.map +1 -0
  55. package/dist/core/cache-warmer.js +356 -0
  56. package/dist/core/cache-warmer.js.map +1 -0
  57. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  58. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  59. package/dist/core/compaction/branch-summarization.js +4 -3
  60. package/dist/core/compaction/branch-summarization.js.map +1 -1
  61. package/dist/core/compaction/compaction.d.ts +5 -3
  62. package/dist/core/compaction/compaction.d.ts.map +1 -1
  63. package/dist/core/compaction/compaction.js +163 -66
  64. package/dist/core/compaction/compaction.js.map +1 -1
  65. package/dist/core/context/pipeline.d.ts +8 -8
  66. package/dist/core/context/pipeline.d.ts.map +1 -1
  67. package/dist/core/context/pipeline.js +21 -12
  68. package/dist/core/context/pipeline.js.map +1 -1
  69. package/dist/core/crash-log.d.ts +27 -0
  70. package/dist/core/crash-log.d.ts.map +1 -0
  71. package/dist/core/crash-log.js +139 -0
  72. package/dist/core/crash-log.js.map +1 -0
  73. package/dist/core/export-html/template.js +6 -1
  74. package/dist/core/extensions/index.d.ts +1 -1
  75. package/dist/core/extensions/index.d.ts.map +1 -1
  76. package/dist/core/extensions/index.js.map +1 -1
  77. package/dist/core/extensions/jiti-loader.d.ts +2 -0
  78. package/dist/core/extensions/jiti-loader.d.ts.map +1 -0
  79. package/dist/core/extensions/jiti-loader.js +4 -0
  80. package/dist/core/extensions/jiti-loader.js.map +1 -0
  81. package/dist/core/extensions/jiti-static-loader.d.ts +2 -0
  82. package/dist/core/extensions/jiti-static-loader.d.ts.map +1 -0
  83. package/dist/core/extensions/jiti-static-loader.js +4 -0
  84. package/dist/core/extensions/jiti-static-loader.js.map +1 -0
  85. package/dist/core/extensions/loader.d.ts.map +1 -1
  86. package/dist/core/extensions/loader.js +40 -59
  87. package/dist/core/extensions/loader.js.map +1 -1
  88. package/dist/core/extensions/runner.d.ts +24 -8
  89. package/dist/core/extensions/runner.d.ts.map +1 -1
  90. package/dist/core/extensions/runner.js +195 -72
  91. package/dist/core/extensions/runner.js.map +1 -1
  92. package/dist/core/extensions/types.d.ts +142 -55
  93. package/dist/core/extensions/types.d.ts.map +1 -1
  94. package/dist/core/extensions/types.js.map +1 -1
  95. package/dist/core/extensions/virtual-modules.d.ts +3 -0
  96. package/dist/core/extensions/virtual-modules.d.ts.map +1 -0
  97. package/dist/core/extensions/virtual-modules.js +41 -0
  98. package/dist/core/extensions/virtual-modules.js.map +1 -0
  99. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  100. package/dist/core/extensions/wrapper.js +1 -20
  101. package/dist/core/extensions/wrapper.js.map +1 -1
  102. package/dist/core/http-dispatcher.d.ts.map +1 -1
  103. package/dist/core/http-dispatcher.js +2 -0
  104. package/dist/core/http-dispatcher.js.map +1 -1
  105. package/dist/core/index.d.ts +2 -1
  106. package/dist/core/index.d.ts.map +1 -1
  107. package/dist/core/index.js.map +1 -1
  108. package/dist/core/keybindings.d.ts +6 -1
  109. package/dist/core/keybindings.d.ts.map +1 -1
  110. package/dist/core/keybindings.js +5 -1
  111. package/dist/core/keybindings.js.map +1 -1
  112. package/dist/core/messages.d.ts +1 -1
  113. package/dist/core/messages.d.ts.map +1 -1
  114. package/dist/core/messages.js +1 -0
  115. package/dist/core/messages.js.map +1 -1
  116. package/dist/core/model-config.d.ts +168 -20
  117. package/dist/core/model-config.d.ts.map +1 -1
  118. package/dist/core/model-config.js +44 -18
  119. package/dist/core/model-config.js.map +1 -1
  120. package/dist/core/model-registry.d.ts +5 -1
  121. package/dist/core/model-registry.d.ts.map +1 -1
  122. package/dist/core/model-registry.js +8 -0
  123. package/dist/core/model-registry.js.map +1 -1
  124. package/dist/core/model-resolver.d.ts.map +1 -1
  125. package/dist/core/model-resolver.js +4 -3
  126. package/dist/core/model-resolver.js.map +1 -1
  127. package/dist/core/model-runtime.d.ts +1 -0
  128. package/dist/core/model-runtime.d.ts.map +1 -1
  129. package/dist/core/model-runtime.js +12 -6
  130. package/dist/core/model-runtime.js.map +1 -1
  131. package/dist/core/permissions/store.d.ts.map +1 -1
  132. package/dist/core/permissions/store.js +11 -8
  133. package/dist/core/permissions/store.js.map +1 -1
  134. package/dist/core/prompt-templates.d.ts +6 -1
  135. package/dist/core/prompt-templates.d.ts.map +1 -1
  136. package/dist/core/prompt-templates.js +61 -35
  137. package/dist/core/prompt-templates.js.map +1 -1
  138. package/dist/core/provider-composer.d.ts +4 -2
  139. package/dist/core/provider-composer.d.ts.map +1 -1
  140. package/dist/core/provider-composer.js +29 -2
  141. package/dist/core/provider-composer.js.map +1 -1
  142. package/dist/core/radius.d.ts +3 -0
  143. package/dist/core/radius.d.ts.map +1 -1
  144. package/dist/core/radius.js +6 -0
  145. package/dist/core/radius.js.map +1 -1
  146. package/dist/core/resource-loader.d.ts.map +1 -1
  147. package/dist/core/resource-loader.js +6 -2
  148. package/dist/core/resource-loader.js.map +1 -1
  149. package/dist/core/sdk.d.ts.map +1 -1
  150. package/dist/core/sdk.js +63 -42
  151. package/dist/core/sdk.js.map +1 -1
  152. package/dist/core/session-export.d.ts +6 -2
  153. package/dist/core/session-export.d.ts.map +1 -1
  154. package/dist/core/session-export.js +14 -13
  155. package/dist/core/session-export.js.map +1 -1
  156. package/dist/core/session-manager.d.ts +66 -15
  157. package/dist/core/session-manager.d.ts.map +1 -1
  158. package/dist/core/session-manager.js +295 -115
  159. package/dist/core/session-manager.js.map +1 -1
  160. package/dist/core/settings-manager.d.ts +27 -4
  161. package/dist/core/settings-manager.d.ts.map +1 -1
  162. package/dist/core/settings-manager.js +55 -7
  163. package/dist/core/settings-manager.js.map +1 -1
  164. package/dist/core/skills.d.ts +1 -1
  165. package/dist/core/skills.d.ts.map +1 -1
  166. package/dist/core/skills.js +5 -4
  167. package/dist/core/skills.js.map +1 -1
  168. package/dist/core/slash-commands.d.ts.map +1 -1
  169. package/dist/core/slash-commands.js +1 -0
  170. package/dist/core/slash-commands.js.map +1 -1
  171. package/dist/core/system-prompt.d.ts +49 -6
  172. package/dist/core/system-prompt.d.ts.map +1 -1
  173. package/dist/core/system-prompt.js +140 -79
  174. package/dist/core/system-prompt.js.map +1 -1
  175. package/dist/core/tools/bash.d.ts +7 -8
  176. package/dist/core/tools/bash.d.ts.map +1 -1
  177. package/dist/core/tools/bash.js +20 -115
  178. package/dist/core/tools/bash.js.map +1 -1
  179. package/dist/core/tools/edit.d.ts +1 -12
  180. package/dist/core/tools/edit.d.ts.map +1 -1
  181. package/dist/core/tools/edit.js +6 -204
  182. package/dist/core/tools/edit.js.map +1 -1
  183. package/dist/core/tools/find.d.ts.map +1 -1
  184. package/dist/core/tools/find.js +4 -55
  185. package/dist/core/tools/find.js.map +1 -1
  186. package/dist/core/tools/grep.d.ts.map +1 -1
  187. package/dist/core/tools/grep.js +4 -60
  188. package/dist/core/tools/grep.js.map +1 -1
  189. package/dist/core/tools/ls.d.ts.map +1 -1
  190. package/dist/core/tools/ls.js +4 -49
  191. package/dist/core/tools/ls.js.map +1 -1
  192. package/dist/core/tools/read.d.ts +4 -1
  193. package/dist/core/tools/read.d.ts.map +1 -1
  194. package/dist/core/tools/read.js +10 -125
  195. package/dist/core/tools/read.js.map +1 -1
  196. package/dist/core/tools/renderers/bash.d.ts +18 -0
  197. package/dist/core/tools/renderers/bash.d.ts.map +1 -0
  198. package/dist/core/tools/renderers/bash.js +126 -0
  199. package/dist/core/tools/renderers/bash.js.map +1 -0
  200. package/dist/core/tools/renderers/edit.d.ts +23 -0
  201. package/dist/core/tools/renderers/edit.d.ts.map +1 -0
  202. package/dist/core/tools/renderers/edit.js +207 -0
  203. package/dist/core/tools/renderers/edit.js.map +1 -0
  204. package/dist/core/tools/renderers/find.d.ts +10 -0
  205. package/dist/core/tools/renderers/find.d.ts.map +1 -0
  206. package/dist/core/tools/renderers/find.js +64 -0
  207. package/dist/core/tools/renderers/find.js.map +1 -0
  208. package/dist/core/tools/renderers/grep.d.ts +10 -0
  209. package/dist/core/tools/renderers/grep.d.ts.map +1 -0
  210. package/dist/core/tools/renderers/grep.js +69 -0
  211. package/dist/core/tools/renderers/grep.js.map +1 -0
  212. package/dist/core/tools/renderers/index.d.ts +34 -0
  213. package/dist/core/tools/renderers/index.d.ts.map +1 -0
  214. package/dist/core/tools/renderers/index.js +47 -0
  215. package/dist/core/tools/renderers/index.js.map +1 -0
  216. package/dist/core/tools/renderers/ls.d.ts +10 -0
  217. package/dist/core/tools/renderers/ls.d.ts.map +1 -0
  218. package/dist/core/tools/renderers/ls.js +58 -0
  219. package/dist/core/tools/renderers/ls.js.map +1 -0
  220. package/dist/core/tools/renderers/read.d.ts +11 -0
  221. package/dist/core/tools/renderers/read.d.ts.map +1 -0
  222. package/dist/core/tools/renderers/read.js +132 -0
  223. package/dist/core/tools/renderers/read.js.map +1 -0
  224. package/dist/core/tools/renderers/write.d.ts +10 -0
  225. package/dist/core/tools/renderers/write.d.ts.map +1 -0
  226. package/dist/core/tools/renderers/write.js +152 -0
  227. package/dist/core/tools/renderers/write.js.map +1 -0
  228. package/dist/core/tools/write.d.ts.map +1 -1
  229. package/dist/core/tools/write.js +6 -146
  230. package/dist/core/tools/write.js.map +1 -1
  231. package/dist/core/usage-totals.d.ts +1 -1
  232. package/dist/core/usage-totals.d.ts.map +1 -1
  233. package/dist/core/usage-totals.js +5 -1
  234. package/dist/core/usage-totals.js.map +1 -1
  235. package/dist/extensions/llama/client.d.ts +2 -0
  236. package/dist/extensions/llama/client.d.ts.map +1 -1
  237. package/dist/extensions/llama/client.js +7 -3
  238. package/dist/extensions/llama/client.js.map +1 -1
  239. package/dist/extensions/llama/provider.d.ts.map +1 -1
  240. package/dist/extensions/llama/provider.js +20 -5
  241. package/dist/extensions/llama/provider.js.map +1 -1
  242. package/dist/index.d.ts +5 -4
  243. package/dist/index.d.ts.map +1 -1
  244. package/dist/index.js +1 -1
  245. package/dist/index.js.map +1 -1
  246. package/dist/main.d.ts.map +1 -1
  247. package/dist/main.js +21 -13
  248. package/dist/main.js.map +1 -1
  249. package/dist/modes/interactive/bug-report.d.ts +16 -0
  250. package/dist/modes/interactive/bug-report.d.ts.map +1 -0
  251. package/dist/modes/interactive/bug-report.js +167 -0
  252. package/dist/modes/interactive/bug-report.js.map +1 -0
  253. package/dist/modes/interactive/chat-viewport.d.ts +20 -0
  254. package/dist/modes/interactive/chat-viewport.d.ts.map +1 -0
  255. package/dist/modes/interactive/chat-viewport.js +28 -0
  256. package/dist/modes/interactive/chat-viewport.js.map +1 -0
  257. package/dist/modes/interactive/components/assistant-message.d.ts +7 -0
  258. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  259. package/dist/modes/interactive/components/assistant-message.js +63 -17
  260. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  261. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  262. package/dist/modes/interactive/components/branch-summary-message.js +12 -5
  263. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  264. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  265. package/dist/modes/interactive/components/compaction-summary-message.js +12 -5
  266. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  267. package/dist/modes/interactive/components/custom-editor.d.ts +12 -0
  268. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  269. package/dist/modes/interactive/components/custom-editor.js +38 -0
  270. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  271. package/dist/modes/interactive/components/error-summary.d.ts +11 -0
  272. package/dist/modes/interactive/components/error-summary.d.ts.map +1 -0
  273. package/dist/modes/interactive/components/error-summary.js +31 -0
  274. package/dist/modes/interactive/components/error-summary.js.map +1 -0
  275. package/dist/modes/interactive/components/extension-editor.d.ts +4 -1
  276. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/extension-editor.js +6 -1
  278. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  279. package/dist/modes/interactive/components/extension-input.d.ts +2 -0
  280. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/extension-input.js +6 -0
  282. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  283. package/dist/modes/interactive/components/extension-selector.d.ts +2 -0
  284. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  285. package/dist/modes/interactive/components/extension-selector.js +4 -0
  286. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  287. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/footer.js +4 -1
  289. package/dist/modes/interactive/components/footer.js.map +1 -1
  290. package/dist/modes/interactive/components/index.d.ts +1 -1
  291. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  292. package/dist/modes/interactive/components/index.js.map +1 -1
  293. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  294. package/dist/modes/interactive/components/model-selector.js +3 -3
  295. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  296. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  297. package/dist/modes/interactive/components/scoped-models-selector.js +13 -15
  298. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  299. package/dist/modes/interactive/components/session-selector.d.ts +5 -5
  300. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  301. package/dist/modes/interactive/components/session-selector.js +74 -48
  302. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  303. package/dist/modes/interactive/components/settings-selector.d.ts +5 -1
  304. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  305. package/dist/modes/interactive/components/settings-selector.js +35 -10
  306. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  307. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  308. package/dist/modes/interactive/components/skill-invocation-message.js +11 -4
  309. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  310. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  311. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  312. package/dist/modes/interactive/components/status-indicator.js +10 -3
  313. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  314. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  315. package/dist/modes/interactive/components/thinking-selector.js +6 -6
  316. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  317. package/dist/modes/interactive/components/tool-execution.d.ts +27 -5
  318. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  319. package/dist/modes/interactive/components/tool-execution.js +64 -42
  320. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  321. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  322. package/dist/modes/interactive/components/tree-selector.js +9 -0
  323. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  324. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  325. package/dist/modes/interactive/components/trust-selector.js +2 -2
  326. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  327. package/dist/modes/interactive/interactive-mode.d.ts +43 -29
  328. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  329. package/dist/modes/interactive/interactive-mode.js +431 -166
  330. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  331. package/dist/modes/interactive/theme/dark.json +2 -1
  332. package/dist/modes/interactive/theme/light.json +2 -1
  333. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  334. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  335. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  336. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  337. package/dist/modes/interactive/theme/theme-json.d.ts +84 -0
  338. package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -0
  339. package/dist/modes/interactive/theme/theme-json.js +130 -0
  340. package/dist/modes/interactive/theme/theme-json.js.map +1 -0
  341. package/dist/modes/interactive/theme/theme-schema.json +6 -2
  342. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  343. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  344. package/dist/modes/interactive/theme/theme.js +18 -119
  345. package/dist/modes/interactive/theme/theme.js.map +1 -1
  346. package/dist/modes/interactive/tui-renderer.d.ts +21 -0
  347. package/dist/modes/interactive/tui-renderer.d.ts.map +1 -0
  348. package/dist/modes/interactive/tui-renderer.js +66 -0
  349. package/dist/modes/interactive/tui-renderer.js.map +1 -0
  350. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  351. package/dist/modes/rpc/rpc-mode.js +2 -2
  352. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  353. package/dist/package-manager-cli.d.ts.map +1 -1
  354. package/dist/package-manager-cli.js +31 -72
  355. package/dist/package-manager-cli.js.map +1 -1
  356. package/dist/testing/replay/recorded-provider.d.ts +2 -2
  357. package/dist/testing/replay/recorded-provider.d.ts.map +1 -1
  358. package/dist/testing/replay/recorded-provider.js +2 -5
  359. package/dist/testing/replay/recorded-provider.js.map +1 -1
  360. package/dist/testing/replay/runner.d.ts.map +1 -1
  361. package/dist/testing/replay/runner.js +0 -1
  362. package/dist/testing/replay/runner.js.map +1 -1
  363. package/dist/utils/clipboard-command.d.ts +7 -0
  364. package/dist/utils/clipboard-command.d.ts.map +1 -0
  365. package/dist/utils/clipboard-command.js +45 -0
  366. package/dist/utils/clipboard-command.js.map +1 -0
  367. package/dist/utils/clipboard-image.d.ts.map +1 -1
  368. package/dist/utils/clipboard-image.js +53 -81
  369. package/dist/utils/clipboard-image.js.map +1 -1
  370. package/dist/utils/clipboard.d.ts.map +1 -1
  371. package/dist/utils/clipboard.js +111 -121
  372. package/dist/utils/clipboard.js.map +1 -1
  373. package/dist/utils/exif-orientation.d.ts.map +1 -1
  374. package/dist/utils/exif-orientation.js +2 -3
  375. package/dist/utils/exif-orientation.js.map +1 -1
  376. package/dist/utils/mime.d.ts.map +1 -1
  377. package/dist/utils/mime.js +1 -1
  378. package/dist/utils/mime.js.map +1 -1
  379. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  380. package/dist/utils/syntax-highlight.js +21 -21
  381. package/dist/utils/syntax-highlight.js.map +1 -1
  382. package/dist/utils/tool-result-images.d.ts +3 -1
  383. package/dist/utils/tool-result-images.d.ts.map +1 -1
  384. package/dist/utils/tool-result-images.js +4 -1
  385. package/dist/utils/tool-result-images.js.map +1 -1
  386. package/dist/utils/tools-manager.d.ts.map +1 -1
  387. package/dist/utils/tools-manager.js +11 -2
  388. package/dist/utils/tools-manager.js.map +1 -1
  389. package/dist/utils/wsl.d.ts +3 -0
  390. package/dist/utils/wsl.d.ts.map +1 -0
  391. package/dist/utils/wsl.js +15 -0
  392. package/dist/utils/wsl.js.map +1 -0
  393. package/dist/utils/zip.d.ts +7 -0
  394. package/dist/utils/zip.d.ts.map +1 -0
  395. package/dist/utils/zip.js +60 -0
  396. package/dist/utils/zip.js.map +1 -0
  397. package/docs/cli-integration.md +107 -0
  398. package/docs/cli.md +269 -0
  399. package/docs/compaction.md +73 -24
  400. package/docs/configuration.md +45 -0
  401. package/docs/containerization.md +22 -21
  402. package/docs/custom-provider.md +21 -12
  403. package/docs/development.md +19 -0
  404. package/docs/docs.json +139 -95
  405. package/docs/extensions.md +243 -371
  406. package/docs/how-pi-works.md +49 -0
  407. package/docs/images/interactive-mode.png +0 -0
  408. package/docs/index.md +3 -3
  409. package/docs/json.md +7 -3
  410. package/docs/keybindings.md +57 -54
  411. package/docs/llama-cpp.md +2 -2
  412. package/docs/message-types.md +261 -0
  413. package/docs/models.md +103 -49
  414. package/docs/packages.md +49 -47
  415. package/docs/prompt-templates.md +29 -56
  416. package/docs/providers.md +91 -139
  417. package/docs/quickstart.md +42 -14
  418. package/docs/rpc-commands.md +854 -0
  419. package/docs/rpc-extension-ui.md +200 -0
  420. package/docs/rpc.md +121 -223
  421. package/docs/sdk.md +99 -124
  422. package/docs/security.md +7 -7
  423. package/docs/session-format.md +85 -82
  424. package/docs/sessions.md +41 -57
  425. package/docs/settings.md +94 -87
  426. package/docs/shell-aliases.md +68 -3
  427. package/docs/skills.md +62 -49
  428. package/docs/slash-commands.md +60 -0
  429. package/docs/terminal-setup.md +86 -55
  430. package/docs/termux.md +65 -75
  431. package/docs/themes.md +64 -86
  432. package/docs/tmux.md +29 -7
  433. package/docs/tui.md +67 -122
  434. package/docs/usage.md +31 -42
  435. package/docs/windows.md +41 -13
  436. package/examples/README.md +16 -2
  437. package/examples/extensions/README.md +0 -1
  438. package/examples/extensions/custom-provider-anthropic/index.ts +18 -12
  439. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  440. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  441. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  442. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  443. package/examples/extensions/dynamic-resources/dynamic.json +2 -0
  444. package/examples/extensions/gondolin/package-lock.json +2 -2
  445. package/examples/extensions/gondolin/package.json +1 -1
  446. package/examples/extensions/prompt-customizer.ts +19 -67
  447. package/examples/extensions/sandbox/package-lock.json +2 -2
  448. package/examples/extensions/sandbox/package.json +1 -1
  449. package/examples/extensions/with-deps/package-lock.json +2 -2
  450. package/examples/extensions/with-deps/package.json +1 -1
  451. package/examples/plugins/pi-example-plugin/README.md +38 -0
  452. package/examples/plugins/pi-example-plugin/package.json +10 -0
  453. package/examples/plugins/pi-example-plugin/src/contract.ts +13 -0
  454. package/examples/plugins/pi-example-plugin/src/session.ts +24 -0
  455. package/examples/plugins/pi-example-plugin/src/tui.ts +31 -0
  456. package/examples/rpc-client.ts +35 -0
  457. package/examples/rpc-extension-ui.ts +25 -5
  458. package/examples/sdk/README.md +1 -1
  459. package/npm-shrinkwrap.json +861 -513
  460. package/package.json +27 -18
  461. package/dist/bun/register-bedrock.d.ts +0 -2
  462. package/dist/bun/register-bedrock.d.ts.map +0 -1
  463. package/dist/bun/register-bedrock.js +0 -4
  464. package/dist/bun/register-bedrock.js.map +0 -1
  465. package/dist/cli/experimental/auth.d.ts +0 -16
  466. package/dist/cli/experimental/auth.d.ts.map +0 -1
  467. package/dist/cli/experimental/auth.js +0 -13
  468. package/dist/cli/experimental/auth.js.map +0 -1
  469. package/dist/cli/experimental/cli.d.ts +0 -6
  470. package/dist/cli/experimental/cli.d.ts.map +0 -1
  471. package/dist/cli/experimental/cli.js +0 -5
  472. package/dist/cli/experimental/cli.js.map +0 -1
  473. package/dist/cli/experimental/command-options.d.ts +0 -17
  474. package/dist/cli/experimental/command-options.d.ts.map +0 -1
  475. package/dist/cli/experimental/command-options.js +0 -35
  476. package/dist/cli/experimental/command-options.js.map +0 -1
  477. package/dist/cli/experimental/command.d.ts +0 -63
  478. package/dist/cli/experimental/command.d.ts.map +0 -1
  479. package/dist/cli/experimental/command.js +0 -130
  480. package/dist/cli/experimental/command.js.map +0 -1
  481. package/dist/cli/experimental/commands/client.d.ts +0 -13
  482. package/dist/cli/experimental/commands/client.d.ts.map +0 -1
  483. package/dist/cli/experimental/commands/client.js +0 -25
  484. package/dist/cli/experimental/commands/client.js.map +0 -1
  485. package/dist/cli/experimental/commands/pi.d.ts +0 -15
  486. package/dist/cli/experimental/commands/pi.d.ts.map +0 -1
  487. package/dist/cli/experimental/commands/pi.js +0 -28
  488. package/dist/cli/experimental/commands/pi.js.map +0 -1
  489. package/dist/cli/experimental/commands/server.d.ts +0 -13
  490. package/dist/cli/experimental/commands/server.d.ts.map +0 -1
  491. package/dist/cli/experimental/commands/server.js +0 -25
  492. package/dist/cli/experimental/commands/server.js.map +0 -1
  493. package/dist/cli/experimental/transport-address.d.ts +0 -10
  494. package/dist/cli/experimental/transport-address.d.ts.map +0 -1
  495. package/dist/cli/experimental/transport-address.js +0 -38
  496. package/dist/cli/experimental/transport-address.js.map +0 -1
  497. package/dist/client/index.d.ts +0 -3
  498. package/dist/client/index.d.ts.map +0 -1
  499. package/dist/client/index.js +0 -3
  500. package/dist/client/index.js.map +0 -1
  501. package/dist/client/remote-session.d.ts +0 -53
  502. package/dist/client/remote-session.d.ts.map +0 -1
  503. package/dist/client/remote-session.js +0 -340
  504. package/dist/client/remote-session.js.map +0 -1
  505. package/dist/client/transcript.d.ts +0 -12
  506. package/dist/client/transcript.d.ts.map +0 -1
  507. package/dist/client/transcript.js +0 -98
  508. package/dist/client/transcript.js.map +0 -1
  509. package/dist/utils/clipboard-native.d.ts +0 -11
  510. package/dist/utils/clipboard-native.d.ts.map +0 -1
  511. package/dist/utils/clipboard-native.js +0 -20
  512. package/dist/utils/clipboard-native.js.map +0 -1
@@ -1,8 +1,8 @@
1
- import { uuidv7 } from "@earendil-works/pi-ai";
1
+ import { getCurrentSystemMessage, uuidv7, } from "@earendil-works/pi-ai";
2
2
  import { randomUUID } from "crypto";
3
3
  import { appendFileSync, closeSync, createReadStream, existsSync, mkdirSync, openSync, readdirSync, readSync, renameSync, rmSync, statSync, writeFileSync, } from "fs";
4
4
  import { readdir, stat } from "fs/promises";
5
- import { join, resolve } from "path";
5
+ import { basename, join, resolve } from "path";
6
6
  import { createInterface } from "readline";
7
7
  import { StringDecoder } from "string_decoder";
8
8
  import { APP_NAME, getAgentDir as getDefaultAgentDir, getSessionsDir } from "../config.js";
@@ -184,6 +184,8 @@ export function sessionEntryToContextMessages(entry) {
184
184
  const message = entry.message;
185
185
  // Session files are parsed without validation; old versions, forks, or
186
186
  // hand-edited files can contain messages with null/missing content.
187
+ if (message.role === "system" && message.content == null)
188
+ return [{ ...message, content: "" }];
187
189
  if ((message.role === "user" || message.role === "assistant" || message.role === "toolResult") &&
188
190
  message.content == null) {
189
191
  return [{ ...message, content: [] }];
@@ -199,7 +201,8 @@ export function sessionEntryToContextMessages(entry) {
199
201
  return [createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)];
200
202
  }
201
203
  if (entry.type === "compaction") {
202
- return [createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp)];
204
+ const summary = createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp);
205
+ return entry.systemMessage ? [entry.systemMessage, summary] : [summary];
203
206
  }
204
207
  return [];
205
208
  }
@@ -233,7 +236,7 @@ export function buildContextEntries(entries, leafId, byId) {
233
236
  if (entry.id === compaction.firstKeptEntryId) {
234
237
  foundFirstKept = true;
235
238
  }
236
- if (foundFirstKept) {
239
+ if (foundFirstKept && !(entry.type === "message" && entry.message.role === "system")) {
237
240
  contextEntries.push(entry);
238
241
  }
239
242
  }
@@ -245,10 +248,55 @@ export function buildContextEntries(entries, leafId, byId) {
245
248
  * If leafId is provided, walks from that entry to root.
246
249
  * Handles compaction and branch summaries along the path.
247
250
  */
248
- export function buildSessionContext(entries, leafId, byId) {
251
+ function projectContextEntry(entry, edit) {
252
+ const messages = sessionEntryToContextMessages(entry);
253
+ if (!edit)
254
+ return messages;
255
+ const replacement = edit.replacement;
256
+ if (replacement === null)
257
+ return [];
258
+ return messages.map((message) => {
259
+ if (message.role !== "user" &&
260
+ message.role !== "assistant" &&
261
+ message.role !== "toolResult" &&
262
+ message.role !== "custom") {
263
+ return message;
264
+ }
265
+ const content = (message.role === "assistant" || message.role === "toolResult") && typeof replacement.content === "string"
266
+ ? [{ type: "text", text: replacement.content }]
267
+ : replacement.content;
268
+ return { ...message, content };
269
+ });
270
+ }
271
+ /** Build provenance-preserving, compaction-aware model context. */
272
+ export function buildSessionProjection(entries, leafId, byId) {
249
273
  const path = buildSessionPath(entries, leafId, byId);
250
274
  const { thinkingLevel, model } = getSessionContextSettings(path);
251
- const messages = buildContextEntries(entries, leafId, byId).flatMap(sessionEntryToContextMessages);
275
+ const contextEntries = buildContextEntries(entries, leafId, byId);
276
+ const edits = new Map();
277
+ for (const entry of contextEntries) {
278
+ if (entry.type === "context_edit")
279
+ edits.set(entry.targetId, entry);
280
+ }
281
+ const projectedEntries = contextEntries.map((sourceEntry, index) => ({
282
+ sourceEntry,
283
+ // buildContextEntries() may retain an older compaction entry because its
284
+ // raw ID lies inside the newest retained range. Only the newest compaction
285
+ // at index zero contributes a checkpoint and summary.
286
+ messages: sourceEntry.type === "compaction" && index > 0
287
+ ? []
288
+ : projectContextEntry(sourceEntry, edits.get(sourceEntry.id)),
289
+ }));
290
+ return {
291
+ entries: projectedEntries,
292
+ messages: projectedEntries.flatMap((entry) => entry.messages),
293
+ thinkingLevel,
294
+ model,
295
+ };
296
+ }
297
+ /** Build the finalized model context from the canonical session projection. */
298
+ export function buildSessionContext(entries, leafId, byId) {
299
+ const { messages, thinkingLevel, model } = buildSessionProjection(entries, leafId, byId);
252
300
  return { messages, thinkingLevel, model };
253
301
  }
254
302
  /**
@@ -413,14 +461,16 @@ export function findMostRecentSession(sessionDir, cwd) {
413
461
  const resolvedCwd = cwd ? resolvePath(cwd) : undefined;
414
462
  try {
415
463
  const files = readdirSync(resolvedSessionDir)
416
- .filter((f) => f.endsWith(".jsonl"))
417
- .map((f) => join(resolvedSessionDir, f))
418
- .map((path) => ({ path, header: readSessionHeaderForDiscovery(path) }))
419
- .filter((file) => file.header !== null &&
420
- (!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(file.header), resolvedCwd)))
421
- .map(({ path }) => ({ path, mtime: statSync(path).mtime }))
422
- .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
423
- return files[0]?.path || null;
464
+ .filter((file) => file.endsWith(".jsonl"))
465
+ .map((file) => join(resolvedSessionDir, file))
466
+ .map((path) => ({ path, mtime: statSync(path).mtimeMs }))
467
+ .sort((a, b) => b.mtime - a.mtime);
468
+ for (const { path } of files) {
469
+ const header = readSessionHeaderForDiscovery(path);
470
+ if (header && (!resolvedCwd || sessionCwdMatches(getSessionHeaderCwd(header), resolvedCwd)))
471
+ return path;
472
+ }
473
+ return null;
424
474
  }
425
475
  catch {
426
476
  // Directory access and stat races make recent-session discovery unavailable.
@@ -453,9 +503,9 @@ function getMessageActivityTime(entry) {
453
503
  const t = new Date(entry.timestamp).getTime();
454
504
  return Number.isNaN(t) ? undefined : t;
455
505
  }
456
- async function buildSessionInfo(filePath) {
506
+ async function buildSessionInfo(filePath, signal, fileStats) {
457
507
  try {
458
- const stats = await stat(filePath);
508
+ const stats = fileStats ?? (await stat(filePath));
459
509
  let header = null;
460
510
  let messageCount = 0;
461
511
  let firstMessage = "";
@@ -463,7 +513,7 @@ async function buildSessionInfo(filePath) {
463
513
  let name;
464
514
  let lastActivityTime;
465
515
  const rl = createInterface({
466
- input: createReadStream(filePath, { encoding: "utf8" }),
516
+ input: createReadStream(filePath, { encoding: "utf8", signal }),
467
517
  crlfDelay: Infinity,
468
518
  });
469
519
  for await (const line of rl) {
@@ -524,67 +574,63 @@ async function buildSessionInfo(filePath) {
524
574
  };
525
575
  }
526
576
  catch {
577
+ signal?.throwIfAborted();
527
578
  return null;
528
579
  }
529
580
  }
530
581
  const MAX_CONCURRENT_SESSION_INFO_LOADS = 10;
531
- async function buildSessionInfosWithConcurrency(files, onLoaded) {
532
- const results = new Array(files.length).fill(null);
533
- const inFlight = new Set();
582
+ const MAX_CONCURRENT_SESSION_DISCOVERY_LOADS = 64;
583
+ const CURRENT_SESSION_LIST_PUBLISH_INTERVAL = 10;
584
+ const ALL_SESSION_LIST_PUBLISH_INTERVAL = 100;
585
+ async function mapWithConcurrency(items, limit, map, signal) {
586
+ const results = new Array(items.length);
534
587
  let nextIndex = 0;
535
- const startNext = () => {
536
- const index = nextIndex++;
537
- const file = files[index];
538
- if (!file)
539
- return;
540
- let task;
541
- task = buildSessionInfo(file)
542
- .then((info) => {
543
- results[index] = info;
544
- })
545
- .catch(() => {
546
- results[index] = null;
547
- })
548
- .finally(() => {
549
- inFlight.delete(task);
550
- onLoaded();
551
- });
552
- inFlight.add(task);
553
- };
554
- while (nextIndex < files.length || inFlight.size > 0) {
555
- while (nextIndex < files.length && inFlight.size < MAX_CONCURRENT_SESSION_INFO_LOADS) {
556
- startNext();
588
+ const worker = async () => {
589
+ while (nextIndex < items.length) {
590
+ signal?.throwIfAborted();
591
+ const index = nextIndex++;
592
+ results[index] = await map(items[index], index);
557
593
  }
558
- if (inFlight.size > 0) {
559
- await Promise.race(inFlight);
560
- }
561
- }
594
+ };
595
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()));
562
596
  return results;
563
597
  }
564
- async function listSessionsFromDir(dir, onProgress, progressOffset = 0, progressTotal) {
565
- const sessions = [];
566
- if (!existsSync(dir)) {
567
- return sessions;
568
- }
598
+ function sortSessionInfos(sessions) {
599
+ return sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
600
+ }
601
+ function buildSessionInfosWithConcurrency(files, onLoaded, signal) {
602
+ return mapWithConcurrency(files, MAX_CONCURRENT_SESSION_INFO_LOADS, async (file, index) => {
603
+ const info = await buildSessionInfo(file.path, signal, file.stats);
604
+ onLoaded(info, index);
605
+ return info;
606
+ }, signal);
607
+ }
608
+ async function listSessionsFromDir(dir, onProgress, signal) {
609
+ signal?.throwIfAborted();
610
+ if (!existsSync(dir))
611
+ return [];
569
612
  try {
570
613
  const dirEntries = await readdir(dir);
571
- const files = dirEntries.filter((f) => f.endsWith(".jsonl")).map((f) => join(dir, f));
572
- const total = progressTotal ?? files.length;
614
+ const files = dirEntries
615
+ .filter((file) => file.endsWith(".jsonl"))
616
+ .sort((a, b) => b.localeCompare(a))
617
+ .map((file) => ({ path: join(dir, file) }));
618
+ const total = files.length;
619
+ const partialSessions = [];
573
620
  let loaded = 0;
574
- const results = await buildSessionInfosWithConcurrency(files, () => {
621
+ const results = await buildSessionInfosWithConcurrency(files, (info) => {
575
622
  loaded++;
576
- onProgress?.(progressOffset + loaded, total);
577
- });
578
- for (const info of results) {
579
- if (info) {
580
- sessions.push(info);
581
- }
582
- }
623
+ if (info)
624
+ partialSessions.push(info);
625
+ const publishPartial = loaded === 1 || loaded % CURRENT_SESSION_LIST_PUBLISH_INTERVAL === 0 || loaded === files.length;
626
+ onProgress?.(loaded, total, publishPartial ? sortSessionInfos([...partialSessions]) : undefined);
627
+ }, signal);
628
+ return results.filter((info) => info !== null);
583
629
  }
584
630
  catch {
585
- // Return empty list on error
631
+ signal?.throwIfAborted();
632
+ return [];
586
633
  }
587
- return sessions;
588
634
  }
589
635
  /**
590
636
  * Manages conversation sessions as append-only trees stored in JSONL files.
@@ -625,6 +671,9 @@ export class SessionManager {
625
671
  if (sessionFile) {
626
672
  this._setSessionFile(sessionFile, preloadedFileEntries);
627
673
  }
674
+ else if (preloadedFileEntries?.length) {
675
+ this._loadEntries(preloadedFileEntries, newSessionOptions);
676
+ }
628
677
  else {
629
678
  this.newSession(newSessionOptions);
630
679
  }
@@ -636,11 +685,10 @@ export class SessionManager {
636
685
  _setSessionFile(sessionFile, preloadedFileEntries) {
637
686
  this.sessionFile = resolvePath(sessionFile);
638
687
  if (existsSync(this.sessionFile)) {
639
- this.fileEntries = preloadedFileEntries ?? loadEntriesFromFile(this.sessionFile);
640
- this.entriesVersion++;
688
+ const entries = preloadedFileEntries ?? loadEntriesFromFile(this.sessionFile);
641
689
  // If file was empty, initialize it with a valid session header. If it was
642
690
  // non-empty but did not parse as a pi session, fail without modifying it.
643
- if (this.fileEntries.length === 0) {
691
+ if (entries.length === 0) {
644
692
  const explicitPath = this.sessionFile;
645
693
  if (statSync(explicitPath).size > 0) {
646
694
  throw new Error(`Session file is not a valid ${APP_NAME} session: ${explicitPath}`);
@@ -651,12 +699,7 @@ export class SessionManager {
651
699
  this.flushed = true;
652
700
  return;
653
701
  }
654
- const header = this.fileEntries.find((e) => e.type === "session");
655
- this.sessionId = header?.id ?? createSessionId();
656
- if (migrateToCurrentVersion(this.fileEntries)) {
657
- this._rewriteFile();
658
- }
659
- this._buildIndex();
702
+ this._loadEntries(entries);
660
703
  this.flushed = true;
661
704
  }
662
705
  else {
@@ -693,6 +736,22 @@ export class SessionManager {
693
736
  }
694
737
  return this.sessionFile;
695
738
  }
739
+ _loadEntries(entries, options) {
740
+ const header = entries.find((e) => e.type === "session");
741
+ if (header) {
742
+ this.fileEntries = entries;
743
+ this.sessionId = header.id;
744
+ if (migrateToCurrentVersion(this.fileEntries)) {
745
+ this._rewriteFile();
746
+ }
747
+ }
748
+ else {
749
+ this.newSession(options);
750
+ this.fileEntries = this.fileEntries.concat(entries);
751
+ }
752
+ this.entriesVersion++;
753
+ this._buildIndex();
754
+ }
696
755
  _buildIndex() {
697
756
  this.byId.clear();
698
757
  this.labelsById.clear();
@@ -833,19 +892,39 @@ export class SessionManager {
833
892
  this._appendEntry(entry);
834
893
  return entry.id;
835
894
  }
895
+ /** Append model-attributed usage that does not participate in LLM context. Returns the appended entry. */
896
+ appendUsage(kind, provider, model, usage, note) {
897
+ const entry = {
898
+ type: "usage",
899
+ id: generateId(this.byId),
900
+ parentId: this.leafId,
901
+ timestamp: new Date().toISOString(),
902
+ kind,
903
+ provider,
904
+ model,
905
+ usage,
906
+ ...(note ? { note } : {}),
907
+ };
908
+ this._appendEntry(entry);
909
+ return entry;
910
+ }
836
911
  /** Append a compaction summary as child of current leaf, then advance leaf. Returns entry id. */
837
912
  appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromHook, usage) {
913
+ const timestamp = new Date().toISOString();
914
+ const systemMessage = getCurrentSystemMessage(this.buildSessionProjection().messages);
915
+ const id = generateId(this.byId);
838
916
  const entry = {
839
917
  type: "compaction",
840
- id: generateId(this.byId),
918
+ id,
841
919
  parentId: this.leafId,
842
- timestamp: new Date().toISOString(),
920
+ timestamp,
843
921
  summary,
844
- firstKeptEntryId,
922
+ firstKeptEntryId: firstKeptEntryId ?? id,
845
923
  tokensBefore,
846
924
  details,
847
925
  usage,
848
926
  fromHook,
927
+ ...(systemMessage ? { systemMessage: { ...systemMessage, timestamp: new Date(timestamp).getTime() } } : {}),
849
928
  };
850
929
  this._appendEntry(entry);
851
930
  return entry.id;
@@ -943,6 +1022,44 @@ export class SessionManager {
943
1022
  this._appendEntry(entry);
944
1023
  return entry.id;
945
1024
  }
1025
+ /** Append a branch-local edit to an earlier model-visible entry. */
1026
+ appendContextEdit(targetId, replacement) {
1027
+ if (replacement !== null &&
1028
+ (typeof replacement !== "object" ||
1029
+ !("content" in replacement) ||
1030
+ (typeof replacement.content !== "string" && !Array.isArray(replacement.content)))) {
1031
+ throw new Error("Context edit replacement must be null or contain string/array content");
1032
+ }
1033
+ const target = this.byId.get(targetId);
1034
+ if (!target)
1035
+ throw new Error(`Entry ${targetId} not found`);
1036
+ if (!this.getBranch().some((entry) => entry.id === targetId)) {
1037
+ throw new Error(`Entry ${targetId} is not on the active branch`);
1038
+ }
1039
+ const editable = target.type === "custom_message" ||
1040
+ (target.type === "message" &&
1041
+ (target.message.role === "user" ||
1042
+ target.message.role === "assistant" ||
1043
+ target.message.role === "toolResult"));
1044
+ if (!editable)
1045
+ throw new Error(`Entry ${targetId} does not contribute editable model content`);
1046
+ const targetRole = target.type === "message" ? target.message.role : "custom";
1047
+ const normalizedReplacement = replacement !== null &&
1048
+ (targetRole === "assistant" || targetRole === "toolResult") &&
1049
+ typeof replacement.content === "string"
1050
+ ? { content: [{ type: "text", text: replacement.content }] }
1051
+ : replacement;
1052
+ const entry = {
1053
+ type: "context_edit",
1054
+ id: generateId(this.byId),
1055
+ parentId: this.leafId,
1056
+ timestamp: new Date().toISOString(),
1057
+ targetId,
1058
+ replacement: normalizedReplacement,
1059
+ };
1060
+ this._appendEntry(entry);
1061
+ return entry.id;
1062
+ }
946
1063
  // =========================================================================
947
1064
  // Tree Traversal
948
1065
  // =========================================================================
@@ -1032,8 +1149,12 @@ export class SessionManager {
1032
1149
  * Build the session context (what gets sent to the LLM).
1033
1150
  * Uses tree traversal from current leaf.
1034
1151
  */
1152
+ buildSessionProjection() {
1153
+ return buildSessionProjection(this.getEntries(), this.leafId, this.byId);
1154
+ }
1035
1155
  buildSessionContext() {
1036
- return buildSessionContext(this.getEntries(), this.leafId, this.byId);
1156
+ const { messages, thinkingLevel, model } = this.buildSessionProjection();
1157
+ return { messages, thinkingLevel, model };
1037
1158
  }
1038
1159
  /**
1039
1160
  * Get session header.
@@ -1167,11 +1288,27 @@ export class SessionManager {
1167
1288
  // Because labels are real tree entries, later entries can be children of labels;
1168
1289
  // removing labels requires re-chaining the retained path to avoid orphaned subtrees.
1169
1290
  const pathWithoutLabels = [];
1291
+ const replacementByLabelId = new Map();
1292
+ const pendingLabelIds = [];
1170
1293
  let pathParentId = null;
1171
1294
  for (const entry of path) {
1172
- if (entry.type === "label")
1295
+ if (entry.type === "label") {
1296
+ pendingLabelIds.push(entry.id);
1173
1297
  continue;
1174
- pathWithoutLabels.push({ ...entry, parentId: pathParentId });
1298
+ }
1299
+ for (const labelId of pendingLabelIds) {
1300
+ replacementByLabelId.set(labelId, entry.id);
1301
+ }
1302
+ pendingLabelIds.length = 0;
1303
+ pathWithoutLabels.push(entry.type === "compaction"
1304
+ ? {
1305
+ ...entry,
1306
+ parentId: pathParentId,
1307
+ firstKeptEntryId: entry.firstKeptEntryId === entry.id
1308
+ ? entry.id
1309
+ : (replacementByLabelId.get(entry.firstKeptEntryId) ?? entry.firstKeptEntryId),
1310
+ }
1311
+ : { ...entry, parentId: pathParentId });
1175
1312
  pathParentId = entry.id;
1176
1313
  }
1177
1314
  const newSessionId = createSessionId();
@@ -1305,9 +1442,9 @@ export class SessionManager {
1305
1442
  }
1306
1443
  return new SessionManager(cwd, dir, undefined, true);
1307
1444
  }
1308
- /** Create an in-memory session (no file persistence) */
1309
- static inMemory(cwd = process.cwd(), options) {
1310
- return new SessionManager(cwd, "", undefined, false, options);
1445
+ /** Create an in-memory session (no file persistence), optionally from entries held outside the filesystem. */
1446
+ static inMemory(cwd = process.cwd(), options, entries) {
1447
+ return new SessionManager(cwd, "", undefined, false, options, entries);
1311
1448
  }
1312
1449
  /**
1313
1450
  * Fork a session from another project directory into the current project.
@@ -1357,67 +1494,110 @@ export class SessionManager {
1357
1494
  }
1358
1495
  return new SessionManager(resolvedTargetCwd, dir, newSessionFile, true);
1359
1496
  }
1497
+ /**
1498
+ * Find an exact session ID without loading transcript bodies.
1499
+ * @param cwd Working directory (used to compute default session directory)
1500
+ * @param id Exact session ID
1501
+ * @param sessionDir Optional session directory. If omitted, uses default (~/.pi/agent/sessions/<encoded-cwd>/).
1502
+ */
1503
+ static findById(cwd, id, sessionDir) {
1504
+ const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
1505
+ const filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);
1506
+ const resolvedCwd = resolvePath(cwd);
1507
+ try {
1508
+ for (const file of readdirSync(dir)) {
1509
+ if (!file.endsWith(".jsonl"))
1510
+ continue;
1511
+ const path = join(dir, file);
1512
+ const header = readSessionHeaderForDiscovery(path);
1513
+ if (header?.id !== id)
1514
+ continue;
1515
+ if (filterCwd && !sessionCwdMatches(getSessionHeaderCwd(header), resolvedCwd))
1516
+ continue;
1517
+ return path;
1518
+ }
1519
+ }
1520
+ catch {
1521
+ // Exact session discovery is best-effort, matching list().
1522
+ }
1523
+ return undefined;
1524
+ }
1360
1525
  /**
1361
1526
  * List all sessions for a directory.
1362
1527
  * @param cwd Working directory (used to compute default session directory)
1363
1528
  * @param sessionDir Optional session directory. If omitted, uses default (~/.apex-code/agent/sessions/<encoded-cwd>/).
1364
1529
  * @param onProgress Optional callback for progress updates (loaded, total)
1365
1530
  */
1366
- static async list(cwd, sessionDir, onProgress) {
1531
+ static async list(cwd, sessionDir, onProgress, signal) {
1367
1532
  const dir = sessionDir ? normalizePath(sessionDir) : getDefaultSessionDir(cwd);
1368
1533
  const filterCwd = sessionDir !== undefined && dir !== getDefaultSessionDirPath(cwd);
1369
1534
  const resolvedCwd = resolvePath(cwd);
1370
- const sessions = (await listSessionsFromDir(dir, onProgress)).filter((session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd));
1371
- sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
1372
- return sessions;
1373
- }
1374
- static async listAll(sessionDirOrOnProgress, onProgress) {
1535
+ const includeSession = (session) => !filterCwd || sessionCwdMatches(session.cwd, resolvedCwd);
1536
+ const progress = onProgress
1537
+ ? (loaded, total, partialSessions) => onProgress(loaded, total, partialSessions?.filter(includeSession))
1538
+ : undefined;
1539
+ const sessions = (await listSessionsFromDir(dir, progress, signal)).filter(includeSession);
1540
+ return sortSessionInfos(sessions);
1541
+ }
1542
+ static async listAll(sessionDirOrOnProgress, onProgressOrSignal, signal) {
1375
1543
  const customSessionDir = typeof sessionDirOrOnProgress === "string" ? normalizePath(sessionDirOrOnProgress) : undefined;
1376
- const progress = typeof sessionDirOrOnProgress === "function" ? sessionDirOrOnProgress : onProgress;
1544
+ const progress = typeof sessionDirOrOnProgress === "function"
1545
+ ? sessionDirOrOnProgress
1546
+ : typeof onProgressOrSignal === "function"
1547
+ ? onProgressOrSignal
1548
+ : undefined;
1549
+ const abortSignal = typeof sessionDirOrOnProgress === "string" || typeof onProgressOrSignal === "function"
1550
+ ? signal
1551
+ : (onProgressOrSignal ?? signal);
1552
+ abortSignal?.throwIfAborted();
1377
1553
  if (customSessionDir) {
1378
- const sessions = await listSessionsFromDir(customSessionDir, progress);
1379
- sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
1380
- return sessions;
1554
+ return sortSessionInfos(await listSessionsFromDir(customSessionDir, progress, abortSignal));
1381
1555
  }
1382
1556
  const sessionsDir = getSessionsDir();
1383
1557
  try {
1384
- if (!existsSync(sessionsDir)) {
1558
+ if (!existsSync(sessionsDir))
1385
1559
  return [];
1386
- }
1387
1560
  const entries = await readdir(sessionsDir, { withFileTypes: true });
1388
1561
  const dirs = entries
1389
1562
  .filter((entry) => entry.isDirectory() || entry.isSymbolicLink())
1390
1563
  .map((entry) => join(sessionsDir, entry.name));
1391
- // Count total files first for accurate progress
1392
- let totalFiles = 0;
1393
- const dirFiles = [];
1394
- for (const dir of dirs) {
1564
+ const dirFiles = await mapWithConcurrency(dirs, MAX_CONCURRENT_SESSION_DISCOVERY_LOADS, async (dir) => {
1395
1565
  try {
1396
- const files = (await readdir(dir)).filter((f) => f.endsWith(".jsonl"));
1397
- dirFiles.push(files.map((f) => join(dir, f)));
1398
- totalFiles += files.length;
1566
+ return (await readdir(dir)).filter((file) => file.endsWith(".jsonl")).map((file) => join(dir, file));
1399
1567
  }
1400
1568
  catch {
1401
- dirFiles.push([]);
1569
+ return [];
1402
1570
  }
1403
- }
1404
- // Process all files with progress tracking
1405
- let loaded = 0;
1406
- const sessions = [];
1571
+ }, abortSignal);
1407
1572
  const allFiles = dirFiles.flat();
1408
- const results = await buildSessionInfosWithConcurrency(allFiles, () => {
1409
- loaded++;
1410
- progress?.(loaded, totalFiles);
1411
- });
1412
- for (const info of results) {
1413
- if (info) {
1414
- sessions.push(info);
1573
+ const candidates = await mapWithConcurrency(allFiles, MAX_CONCURRENT_SESSION_DISCOVERY_LOADS, async (path) => {
1574
+ try {
1575
+ return { path, stats: await stat(path) };
1415
1576
  }
1416
- }
1417
- sessions.sort((a, b) => b.modified.getTime() - a.modified.getTime());
1418
- return sessions;
1577
+ catch {
1578
+ return { path };
1579
+ }
1580
+ }, abortSignal);
1581
+ candidates.sort((a, b) => (b.stats?.mtimeMs ?? Number.NEGATIVE_INFINITY) - (a.stats?.mtimeMs ?? Number.NEGATIVE_INFINITY) ||
1582
+ basename(b.path).localeCompare(basename(a.path)));
1583
+ const totalFiles = candidates.length;
1584
+ let loaded = 0;
1585
+ let firstCandidateLoaded = false;
1586
+ const partialSessions = [];
1587
+ const results = await buildSessionInfosWithConcurrency(candidates, (info, index) => {
1588
+ loaded++;
1589
+ if (index === 0)
1590
+ firstCandidateLoaded = true;
1591
+ if (info)
1592
+ partialSessions.push(info);
1593
+ const publishPartial = firstCandidateLoaded &&
1594
+ (index === 0 || loaded % ALL_SESSION_LIST_PUBLISH_INTERVAL === 0 || loaded === totalFiles);
1595
+ progress?.(loaded, totalFiles, publishPartial ? sortSessionInfos([...partialSessions]) : undefined);
1596
+ }, abortSignal);
1597
+ return sortSessionInfos(results.filter((info) => info !== null));
1419
1598
  }
1420
1599
  catch {
1600
+ abortSignal?.throwIfAborted();
1421
1601
  return [];
1422
1602
  }
1423
1603
  }