@astralyn/pi 0.82.7 → 0.84.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (781) hide show
  1. package/CHANGELOG.md +78 -5099
  2. package/README.md +12 -111
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js.map +1 -1
  5. package/dist/bun/register-bedrock.d.ts.map +1 -1
  6. package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
  7. package/dist/bun/restore-sandbox-env.js.map +1 -1
  8. package/dist/cli/args.d.ts +2 -0
  9. package/dist/cli/args.d.ts.map +1 -1
  10. package/dist/cli/args.js +28 -2
  11. package/dist/cli/args.js.map +1 -1
  12. package/dist/cli/config-selector.d.ts.map +1 -1
  13. package/dist/cli/config-selector.js +2 -2
  14. package/dist/cli/config-selector.js.map +1 -1
  15. package/dist/cli/credential-print.d.ts +23 -0
  16. package/dist/cli/credential-print.d.ts.map +1 -0
  17. package/dist/cli/credential-print.js +117 -0
  18. package/dist/cli/credential-print.js.map +1 -0
  19. package/dist/cli/experimental/auth.d.ts +16 -0
  20. package/dist/cli/experimental/auth.d.ts.map +1 -0
  21. package/dist/cli/experimental/auth.js +13 -0
  22. package/dist/cli/experimental/auth.js.map +1 -0
  23. package/dist/cli/experimental/cli.d.ts +6 -0
  24. package/dist/cli/experimental/cli.d.ts.map +1 -0
  25. package/dist/cli/experimental/cli.js +5 -0
  26. package/dist/cli/experimental/cli.js.map +1 -0
  27. package/dist/cli/experimental/command-options.d.ts +17 -0
  28. package/dist/cli/experimental/command-options.d.ts.map +1 -0
  29. package/dist/cli/experimental/command-options.js +35 -0
  30. package/dist/cli/experimental/command-options.js.map +1 -0
  31. package/dist/cli/experimental/command.d.ts +63 -0
  32. package/dist/cli/experimental/command.d.ts.map +1 -0
  33. package/dist/cli/experimental/command.js +127 -0
  34. package/dist/cli/experimental/command.js.map +1 -0
  35. package/dist/cli/experimental/commands/client.d.ts +13 -0
  36. package/dist/cli/experimental/commands/client.d.ts.map +1 -0
  37. package/dist/cli/experimental/commands/client.js +25 -0
  38. package/dist/cli/experimental/commands/client.js.map +1 -0
  39. package/dist/cli/experimental/commands/pi.d.ts +15 -0
  40. package/dist/cli/experimental/commands/pi.d.ts.map +1 -0
  41. package/dist/cli/experimental/commands/pi.js +28 -0
  42. package/dist/cli/experimental/commands/pi.js.map +1 -0
  43. package/dist/cli/experimental/commands/server.d.ts +13 -0
  44. package/dist/cli/experimental/commands/server.d.ts.map +1 -0
  45. package/dist/cli/experimental/commands/server.js +25 -0
  46. package/dist/cli/experimental/commands/server.js.map +1 -0
  47. package/dist/cli/experimental/transport-address.d.ts +10 -0
  48. package/dist/cli/experimental/transport-address.d.ts.map +1 -0
  49. package/dist/cli/experimental/transport-address.js +38 -0
  50. package/dist/cli/experimental/transport-address.js.map +1 -0
  51. package/dist/cli/file-processor.d.ts.map +1 -1
  52. package/dist/cli/file-processor.js.map +1 -1
  53. package/dist/cli/initial-message.d.ts +1 -1
  54. package/dist/cli/initial-message.d.ts.map +1 -1
  55. package/dist/cli/initial-message.js.map +1 -1
  56. package/dist/cli/list-models.d.ts +1 -1
  57. package/dist/cli/list-models.d.ts.map +1 -1
  58. package/dist/cli/list-models.js +2 -2
  59. package/dist/cli/list-models.js.map +1 -1
  60. package/dist/cli/project-trust.d.ts.map +1 -1
  61. package/dist/cli/project-trust.js.map +1 -1
  62. package/dist/cli/session-picker.d.ts.map +1 -1
  63. package/dist/cli/session-picker.js.map +1 -1
  64. package/dist/cli/startup-ui.d.ts +1 -1
  65. package/dist/cli/startup-ui.d.ts.map +1 -1
  66. package/dist/cli/startup-ui.js +2 -2
  67. package/dist/cli/startup-ui.js.map +1 -1
  68. package/dist/cli.d.ts.map +1 -1
  69. package/dist/cli.js +1 -0
  70. package/dist/cli.js.map +1 -1
  71. package/dist/client/index.d.ts +3 -0
  72. package/dist/client/index.d.ts.map +1 -0
  73. package/dist/client/index.js +3 -0
  74. package/dist/client/index.js.map +1 -0
  75. package/dist/client/remote-session.d.ts +53 -0
  76. package/dist/client/remote-session.d.ts.map +1 -0
  77. package/dist/client/remote-session.js +340 -0
  78. package/dist/client/remote-session.js.map +1 -0
  79. package/dist/client/transcript.d.ts +12 -0
  80. package/dist/client/transcript.d.ts.map +1 -0
  81. package/dist/client/transcript.js +98 -0
  82. package/dist/client/transcript.js.map +1 -0
  83. package/dist/config.d.ts.map +1 -1
  84. package/dist/config.js.map +1 -1
  85. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  86. package/dist/core/agent-session-runtime.js +3 -8
  87. package/dist/core/agent-session-runtime.js.map +1 -1
  88. package/dist/core/agent-session-services.d.ts +1 -0
  89. package/dist/core/agent-session-services.d.ts.map +1 -1
  90. package/dist/core/agent-session-services.js +1 -0
  91. package/dist/core/agent-session-services.js.map +1 -1
  92. package/dist/core/agent-session.d.ts +31 -11
  93. package/dist/core/agent-session.d.ts.map +1 -1
  94. package/dist/core/agent-session.js +189 -192
  95. package/dist/core/agent-session.js.map +1 -1
  96. package/dist/core/auth-guidance.d.ts.map +1 -1
  97. package/dist/core/auth-guidance.js.map +1 -1
  98. package/dist/core/auth-storage.d.ts +15 -9
  99. package/dist/core/auth-storage.d.ts.map +1 -1
  100. package/dist/core/auth-storage.js +159 -41
  101. package/dist/core/auth-storage.js.map +1 -1
  102. package/dist/core/bash-executor.d.ts.map +1 -1
  103. package/dist/core/bash-executor.js.map +1 -1
  104. package/dist/core/cache-stats.d.ts.map +1 -1
  105. package/dist/core/cache-stats.js.map +1 -1
  106. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  107. package/dist/core/compaction/branch-summarization.js.map +1 -1
  108. package/dist/core/compaction/compaction.d.ts.map +1 -1
  109. package/dist/core/compaction/compaction.js.map +1 -1
  110. package/dist/core/compaction/index.d.ts.map +1 -1
  111. package/dist/core/compaction/utils.d.ts.map +1 -1
  112. package/dist/core/compaction/utils.js.map +1 -1
  113. package/dist/core/defaults.d.ts.map +1 -1
  114. package/dist/core/diagnostics.d.ts.map +1 -1
  115. package/dist/core/event-bus.d.ts.map +1 -1
  116. package/dist/core/event-bus.js.map +1 -1
  117. package/dist/core/exec.d.ts.map +1 -1
  118. package/dist/core/exec.js.map +1 -1
  119. package/dist/core/experimental.d.ts.map +1 -1
  120. package/dist/core/experimental.js.map +1 -1
  121. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  122. package/dist/core/export-html/ansi-to-html.js.map +1 -1
  123. package/dist/core/export-html/index.d.ts.map +1 -1
  124. package/dist/core/export-html/index.js.map +1 -1
  125. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  126. package/dist/core/export-html/tool-renderer.js.map +1 -1
  127. package/dist/core/extensions/index.d.ts +1 -1
  128. package/dist/core/extensions/index.d.ts.map +1 -1
  129. package/dist/core/extensions/index.js.map +1 -1
  130. package/dist/core/extensions/loader.d.ts.map +1 -1
  131. package/dist/core/extensions/loader.js +43 -20
  132. package/dist/core/extensions/loader.js.map +1 -1
  133. package/dist/core/extensions/runner.d.ts +3 -1
  134. package/dist/core/extensions/runner.d.ts.map +1 -1
  135. package/dist/core/extensions/runner.js +31 -28
  136. package/dist/core/extensions/runner.js.map +1 -1
  137. package/dist/core/extensions/types.d.ts +28 -3
  138. package/dist/core/extensions/types.d.ts.map +1 -1
  139. package/dist/core/extensions/types.js.map +1 -1
  140. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  141. package/dist/core/extensions/wrapper.js.map +1 -1
  142. package/dist/core/footer-data-provider.d.ts +10 -0
  143. package/dist/core/footer-data-provider.d.ts.map +1 -1
  144. package/dist/core/footer-data-provider.js +18 -19
  145. package/dist/core/footer-data-provider.js.map +1 -1
  146. package/dist/core/http-dispatcher.d.ts.map +1 -1
  147. package/dist/core/http-dispatcher.js +5 -0
  148. package/dist/core/http-dispatcher.js.map +1 -1
  149. package/dist/core/index.d.ts.map +1 -1
  150. package/dist/core/keybindings.d.ts +157 -125
  151. package/dist/core/keybindings.d.ts.map +1 -1
  152. package/dist/core/keybindings.js +0 -1
  153. package/dist/core/keybindings.js.map +1 -1
  154. package/dist/core/messages.d.ts.map +1 -1
  155. package/dist/core/messages.js.map +1 -1
  156. package/dist/core/model-config.d.ts +37 -13
  157. package/dist/core/model-config.d.ts.map +1 -1
  158. package/dist/core/model-config.js +4 -2
  159. package/dist/core/model-config.js.map +1 -1
  160. package/dist/core/model-registry.d.ts +5 -3
  161. package/dist/core/model-registry.d.ts.map +1 -1
  162. package/dist/core/model-registry.js +13 -11
  163. package/dist/core/model-registry.js.map +1 -1
  164. package/dist/core/model-resolver.d.ts +4 -3
  165. package/dist/core/model-resolver.d.ts.map +1 -1
  166. package/dist/core/model-resolver.js +41 -10
  167. package/dist/core/model-resolver.js.map +1 -1
  168. package/dist/core/model-runtime.d.ts +30 -13
  169. package/dist/core/model-runtime.d.ts.map +1 -1
  170. package/dist/core/model-runtime.js +239 -91
  171. package/dist/core/model-runtime.js.map +1 -1
  172. package/dist/core/models-store.d.ts +12 -7
  173. package/dist/core/models-store.d.ts.map +1 -1
  174. package/dist/core/models-store.js +84 -16
  175. package/dist/core/models-store.js.map +1 -1
  176. package/dist/core/output-guard.d.ts.map +1 -1
  177. package/dist/core/output-guard.js.map +1 -1
  178. package/dist/core/package-manager.d.ts +4 -1
  179. package/dist/core/package-manager.d.ts.map +1 -1
  180. package/dist/core/package-manager.js +76 -48
  181. package/dist/core/package-manager.js.map +1 -1
  182. package/dist/core/pi-manifest.d.ts +8 -0
  183. package/dist/core/pi-manifest.d.ts.map +1 -0
  184. package/dist/core/pi-manifest.js +25 -0
  185. package/dist/core/pi-manifest.js.map +1 -0
  186. package/dist/core/project-trust.d.ts.map +1 -1
  187. package/dist/core/project-trust.js.map +1 -1
  188. package/dist/core/prompt-templates.d.ts.map +1 -1
  189. package/dist/core/prompt-templates.js.map +1 -1
  190. package/dist/core/provider-attribution.d.ts.map +1 -1
  191. package/dist/core/provider-attribution.js.map +1 -1
  192. package/dist/core/provider-composer.d.ts +4 -1
  193. package/dist/core/provider-composer.d.ts.map +1 -1
  194. package/dist/core/provider-composer.js +36 -15
  195. package/dist/core/provider-composer.js.map +1 -1
  196. package/dist/core/radius.d.ts.map +1 -1
  197. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  198. package/dist/core/remote-catalog-provider.js +73 -67
  199. package/dist/core/remote-catalog-provider.js.map +1 -1
  200. package/dist/core/resolve-config-value.d.ts.map +1 -1
  201. package/dist/core/resolve-config-value.js.map +1 -1
  202. package/dist/core/resource-loader.d.ts +15 -0
  203. package/dist/core/resource-loader.d.ts.map +1 -1
  204. package/dist/core/resource-loader.js +68 -51
  205. package/dist/core/resource-loader.js.map +1 -1
  206. package/dist/core/runtime-credentials.d.ts +5 -5
  207. package/dist/core/runtime-credentials.d.ts.map +1 -1
  208. package/dist/core/runtime-credentials.js +12 -10
  209. package/dist/core/runtime-credentials.js.map +1 -1
  210. package/dist/core/sdk.d.ts.map +1 -1
  211. package/dist/core/sdk.js.map +1 -1
  212. package/dist/core/session-cwd.d.ts.map +1 -1
  213. package/dist/core/session-cwd.js +0 -1
  214. package/dist/core/session-cwd.js.map +1 -1
  215. package/dist/core/session-manager.d.ts.map +1 -1
  216. package/dist/core/session-manager.js +10 -12
  217. package/dist/core/session-manager.js.map +1 -1
  218. package/dist/core/settings-manager.d.ts +12 -0
  219. package/dist/core/settings-manager.d.ts.map +1 -1
  220. package/dist/core/settings-manager.js +47 -33
  221. package/dist/core/settings-manager.js.map +1 -1
  222. package/dist/core/skills.d.ts.map +1 -1
  223. package/dist/core/skills.js.map +1 -1
  224. package/dist/core/slash-commands.d.ts.map +1 -1
  225. package/dist/core/source-info.d.ts.map +1 -1
  226. package/dist/core/source-info.js.map +1 -1
  227. package/dist/core/system-prompt.d.ts.map +1 -1
  228. package/dist/core/system-prompt.js.map +1 -1
  229. package/dist/core/telemetry.d.ts.map +1 -1
  230. package/dist/core/telemetry.js.map +1 -1
  231. package/dist/core/timings.d.ts.map +1 -1
  232. package/dist/core/timings.js.map +1 -1
  233. package/dist/core/tools/bash.d.ts.map +1 -1
  234. package/dist/core/tools/bash.js +12 -10
  235. package/dist/core/tools/bash.js.map +1 -1
  236. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  237. package/dist/core/tools/edit-diff.js.map +1 -1
  238. package/dist/core/tools/edit.d.ts.map +1 -1
  239. package/dist/core/tools/edit.js +4 -3
  240. package/dist/core/tools/edit.js.map +1 -1
  241. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  242. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  243. package/dist/core/tools/find.d.ts +3 -0
  244. package/dist/core/tools/find.d.ts.map +1 -1
  245. package/dist/core/tools/find.js +47 -20
  246. package/dist/core/tools/find.js.map +1 -1
  247. package/dist/core/tools/grep.d.ts.map +1 -1
  248. package/dist/core/tools/grep.js.map +1 -1
  249. package/dist/core/tools/index.d.ts.map +1 -1
  250. package/dist/core/tools/index.js.map +1 -1
  251. package/dist/core/tools/ls.d.ts.map +1 -1
  252. package/dist/core/tools/ls.js.map +1 -1
  253. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  254. package/dist/core/tools/output-accumulator.js +12 -18
  255. package/dist/core/tools/output-accumulator.js.map +1 -1
  256. package/dist/core/tools/path-utils.d.ts.map +1 -1
  257. package/dist/core/tools/path-utils.js.map +1 -1
  258. package/dist/core/tools/read.d.ts.map +1 -1
  259. package/dist/core/tools/read.js +1 -1
  260. package/dist/core/tools/read.js.map +1 -1
  261. package/dist/core/tools/render-utils.d.ts.map +1 -1
  262. package/dist/core/tools/render-utils.js.map +1 -1
  263. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  264. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  265. package/dist/core/tools/truncate.d.ts.map +1 -1
  266. package/dist/core/tools/truncate.js.map +1 -1
  267. package/dist/core/tools/write.d.ts.map +1 -1
  268. package/dist/core/tools/write.js +0 -1
  269. package/dist/core/tools/write.js.map +1 -1
  270. package/dist/core/trust-manager.d.ts.map +1 -1
  271. package/dist/core/trust-manager.js +48 -10
  272. package/dist/core/trust-manager.js.map +1 -1
  273. package/dist/core/usage-totals.d.ts.map +1 -1
  274. package/dist/core/usage-totals.js.map +1 -1
  275. package/dist/extensions/biu/index.d.ts +19 -0
  276. package/dist/extensions/biu/index.d.ts.map +1 -0
  277. package/dist/extensions/biu/index.js +350 -0
  278. package/dist/extensions/biu/index.js.map +1 -0
  279. package/dist/extensions/biu/prompts.d.ts +26 -0
  280. package/dist/extensions/biu/prompts.d.ts.map +1 -0
  281. package/dist/extensions/biu/prompts.js +260 -0
  282. package/dist/extensions/biu/prompts.js.map +1 -0
  283. package/dist/extensions/biu/workspace.d.ts +122 -0
  284. package/dist/extensions/biu/workspace.d.ts.map +1 -0
  285. package/dist/extensions/biu/workspace.js +404 -0
  286. package/dist/extensions/biu/workspace.js.map +1 -0
  287. package/dist/extensions/deepwiki/client.d.ts.map +1 -1
  288. package/dist/extensions/deepwiki/client.js.map +1 -1
  289. package/dist/extensions/deepwiki/constants.d.ts.map +1 -1
  290. package/dist/extensions/deepwiki/constants.js.map +1 -1
  291. package/dist/extensions/deepwiki/contents.d.ts.map +1 -1
  292. package/dist/extensions/deepwiki/contents.js.map +1 -1
  293. package/dist/extensions/deepwiki/execute.d.ts.map +1 -1
  294. package/dist/extensions/deepwiki/execute.js.map +1 -1
  295. package/dist/extensions/deepwiki/http.d.ts.map +1 -1
  296. package/dist/extensions/deepwiki/http.js.map +1 -1
  297. package/dist/extensions/deepwiki/index.d.ts.map +1 -1
  298. package/dist/extensions/deepwiki/index.js.map +1 -1
  299. package/dist/extensions/deepwiki/render.d.ts.map +1 -1
  300. package/dist/extensions/deepwiki/render.js.map +1 -1
  301. package/dist/extensions/deepwiki/schema.d.ts +1 -1
  302. package/dist/extensions/deepwiki/schema.d.ts.map +1 -1
  303. package/dist/extensions/deepwiki/schema.js.map +1 -1
  304. package/dist/extensions/index.d.ts.map +1 -1
  305. package/dist/extensions/index.js +2 -2
  306. package/dist/extensions/index.js.map +1 -1
  307. package/dist/extensions/llama/client.d.ts.map +1 -1
  308. package/dist/extensions/llama/client.js +0 -2
  309. package/dist/extensions/llama/client.js.map +1 -1
  310. package/dist/extensions/llama/huggingface.d.ts.map +1 -1
  311. package/dist/extensions/llama/huggingface.js +0 -2
  312. package/dist/extensions/llama/huggingface.js.map +1 -1
  313. package/dist/extensions/llama/index.d.ts.map +1 -1
  314. package/dist/extensions/llama/index.js +11 -2
  315. package/dist/extensions/llama/index.js.map +1 -1
  316. package/dist/extensions/llama/provider.d.ts.map +1 -1
  317. package/dist/extensions/llama/provider.js +22 -8
  318. package/dist/extensions/llama/provider.js.map +1 -1
  319. package/dist/extensions/llama/ui.d.ts.map +1 -1
  320. package/dist/extensions/llama/ui.js +12 -28
  321. package/dist/extensions/llama/ui.js.map +1 -1
  322. package/dist/extensions/question/constants.d.ts.map +1 -1
  323. package/dist/extensions/question/constants.js.map +1 -1
  324. package/dist/extensions/question/dialog-primitives.d.ts.map +1 -1
  325. package/dist/extensions/question/dialog-primitives.js.map +1 -1
  326. package/dist/extensions/question/dialog.d.ts.map +1 -1
  327. package/dist/extensions/question/dialog.js.map +1 -1
  328. package/dist/extensions/question/index.d.ts.map +1 -1
  329. package/dist/extensions/question/index.js.map +1 -1
  330. package/dist/extensions/question/limits.d.ts.map +1 -1
  331. package/dist/extensions/question/render-cache.d.ts.map +1 -1
  332. package/dist/extensions/question/render-cache.js +3 -4
  333. package/dist/extensions/question/render-cache.js.map +1 -1
  334. package/dist/extensions/question/render.d.ts.map +1 -1
  335. package/dist/extensions/question/render.js.map +1 -1
  336. package/dist/extensions/question/results.d.ts.map +1 -1
  337. package/dist/extensions/question/results.js.map +1 -1
  338. package/dist/extensions/question/schema.d.ts.map +1 -1
  339. package/dist/extensions/question/schema.js.map +1 -1
  340. package/dist/extensions/question/state.d.ts.map +1 -1
  341. package/dist/extensions/question/state.js.map +1 -1
  342. package/dist/extensions/question/types.d.ts.map +1 -1
  343. package/dist/extensions/rewind/anchor.d.ts.map +1 -1
  344. package/dist/extensions/rewind/anchor.js.map +1 -1
  345. package/dist/extensions/rewind/config.d.ts.map +1 -1
  346. package/dist/extensions/rewind/config.js.map +1 -1
  347. package/dist/extensions/rewind/engine.d.ts.map +1 -1
  348. package/dist/extensions/rewind/engine.js.map +1 -1
  349. package/dist/extensions/rewind/gc.d.ts.map +1 -1
  350. package/dist/extensions/rewind/gc.js.map +1 -1
  351. package/dist/extensions/rewind/index.d.ts.map +1 -1
  352. package/dist/extensions/rewind/index.js.map +1 -1
  353. package/dist/extensions/rewind/menu.d.ts.map +1 -1
  354. package/dist/extensions/rewind/menu.js.map +1 -1
  355. package/dist/extensions/rewind/paths.d.ts.map +1 -1
  356. package/dist/extensions/rewind/paths.js.map +1 -1
  357. package/dist/extensions/rewind/restore.d.ts.map +1 -1
  358. package/dist/extensions/rewind/restore.js.map +1 -1
  359. package/dist/extensions/rewind/snapshot.d.ts.map +1 -1
  360. package/dist/extensions/rewind/snapshot.js.map +1 -1
  361. package/dist/extensions/rewind/storage.d.ts.map +1 -1
  362. package/dist/extensions/rewind/storage.js.map +1 -1
  363. package/dist/extensions/rewind/text.d.ts.map +1 -1
  364. package/dist/extensions/rewind/text.js.map +1 -1
  365. package/dist/extensions/rewind/tool-path.d.ts.map +1 -1
  366. package/dist/extensions/rewind/tool-path.js.map +1 -1
  367. package/dist/extensions/router/constants.d.ts +3 -0
  368. package/dist/extensions/router/constants.d.ts.map +1 -1
  369. package/dist/extensions/router/constants.js +3 -0
  370. package/dist/extensions/router/constants.js.map +1 -1
  371. package/dist/extensions/router/dialog.d.ts +14 -7
  372. package/dist/extensions/router/dialog.d.ts.map +1 -1
  373. package/dist/extensions/router/dialog.js +70 -132
  374. package/dist/extensions/router/dialog.js.map +1 -1
  375. package/dist/extensions/router/index.d.ts.map +1 -1
  376. package/dist/extensions/router/index.js.map +1 -1
  377. package/dist/extensions/router/presets.d.ts +8 -3
  378. package/dist/extensions/router/presets.d.ts.map +1 -1
  379. package/dist/extensions/router/presets.js +33 -17
  380. package/dist/extensions/router/presets.js.map +1 -1
  381. package/dist/extensions/router/probe.d.ts.map +1 -1
  382. package/dist/extensions/router/probe.js.map +1 -1
  383. package/dist/extensions/router/register.d.ts +3 -3
  384. package/dist/extensions/router/register.d.ts.map +1 -1
  385. package/dist/extensions/router/register.js.map +1 -1
  386. package/dist/extensions/router/store.d.ts.map +1 -1
  387. package/dist/extensions/router/store.js.map +1 -1
  388. package/dist/extensions/router/stream.d.ts.map +1 -1
  389. package/dist/extensions/router/stream.js.map +1 -1
  390. package/dist/extensions/router/types.d.ts.map +1 -1
  391. package/dist/extensions/router/ui.d.ts +11 -2
  392. package/dist/extensions/router/ui.d.ts.map +1 -1
  393. package/dist/extensions/router/ui.js +316 -171
  394. package/dist/extensions/router/ui.js.map +1 -1
  395. package/dist/extensions/statusline/index.d.ts.map +1 -1
  396. package/dist/extensions/statusline/index.js +17 -9
  397. package/dist/extensions/statusline/index.js.map +1 -1
  398. package/dist/extensions/subagent/activity.d.ts.map +1 -1
  399. package/dist/extensions/subagent/activity.js.map +1 -1
  400. package/dist/extensions/subagent/agents.d.ts.map +1 -1
  401. package/dist/extensions/subagent/agents.js.map +1 -1
  402. package/dist/extensions/subagent/constants.d.ts.map +1 -1
  403. package/dist/extensions/subagent/index.d.ts +3 -0
  404. package/dist/extensions/subagent/index.d.ts.map +1 -1
  405. package/dist/extensions/subagent/index.js +83 -12
  406. package/dist/extensions/subagent/index.js.map +1 -1
  407. package/dist/extensions/subagent/picker.d.ts.map +1 -1
  408. package/dist/extensions/subagent/picker.js +2 -9
  409. package/dist/extensions/subagent/picker.js.map +1 -1
  410. package/dist/extensions/subagent/render.d.ts.map +1 -1
  411. package/dist/extensions/subagent/render.js +33 -9
  412. package/dist/extensions/subagent/render.js.map +1 -1
  413. package/dist/extensions/subagent/resolve.d.ts.map +1 -1
  414. package/dist/extensions/subagent/resolve.js.map +1 -1
  415. package/dist/extensions/subagent/retry.d.ts.map +1 -1
  416. package/dist/extensions/subagent/retry.js.map +1 -1
  417. package/dist/extensions/subagent/runner.d.ts +2 -1
  418. package/dist/extensions/subagent/runner.d.ts.map +1 -1
  419. package/dist/extensions/subagent/runner.js +10 -12
  420. package/dist/extensions/subagent/runner.js.map +1 -1
  421. package/dist/extensions/subagent/schema.d.ts.map +1 -1
  422. package/dist/extensions/subagent/schema.js.map +1 -1
  423. package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
  424. package/dist/extensions/subagent/sdk-runner.js +41 -1
  425. package/dist/extensions/subagent/sdk-runner.js.map +1 -1
  426. package/dist/extensions/subagent/settings.d.ts.map +1 -1
  427. package/dist/extensions/subagent/settings.js +46 -36
  428. package/dist/extensions/subagent/settings.js.map +1 -1
  429. package/dist/extensions/subagent/types.d.ts +0 -1
  430. package/dist/extensions/subagent/types.d.ts.map +1 -1
  431. package/dist/extensions/subagent/types.js.map +1 -1
  432. package/dist/extensions/todo/constants.d.ts.map +1 -1
  433. package/dist/extensions/todo/constants.js.map +1 -1
  434. package/dist/extensions/todo/index.d.ts.map +1 -1
  435. package/dist/extensions/todo/index.js.map +1 -1
  436. package/dist/extensions/todo/overlay.d.ts.map +1 -1
  437. package/dist/extensions/todo/overlay.js +6 -10
  438. package/dist/extensions/todo/overlay.js.map +1 -1
  439. package/dist/extensions/todo/schema.d.ts +2 -2
  440. package/dist/extensions/todo/schema.d.ts.map +1 -1
  441. package/dist/extensions/todo/state.d.ts.map +1 -1
  442. package/dist/extensions/todo/state.js.map +1 -1
  443. package/dist/extensions/todo/view.d.ts.map +1 -1
  444. package/dist/extensions/todo/view.js.map +1 -1
  445. package/dist/index.d.ts +4 -4
  446. package/dist/index.d.ts.map +1 -1
  447. package/dist/index.js +1 -1
  448. package/dist/index.js.map +1 -1
  449. package/dist/main.d.ts.map +1 -1
  450. package/dist/main.js +57 -5
  451. package/dist/main.js.map +1 -1
  452. package/dist/migrations.d.ts.map +1 -1
  453. package/dist/migrations.js.map +1 -1
  454. package/dist/modes/index.d.ts +1 -0
  455. package/dist/modes/index.d.ts.map +1 -1
  456. package/dist/modes/index.js.map +1 -1
  457. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  458. package/dist/modes/interactive/components/armin.js +6 -10
  459. package/dist/modes/interactive/components/armin.js.map +1 -1
  460. package/dist/modes/interactive/components/assistant-message.d.ts +5 -2
  461. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  462. package/dist/modes/interactive/components/assistant-message.js +13 -11
  463. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  464. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  465. package/dist/modes/interactive/components/bash-execution.js +4 -10
  466. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  467. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  468. package/dist/modes/interactive/components/bordered-loader.js +0 -3
  469. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  470. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  471. package/dist/modes/interactive/components/branch-summary-message.js +1 -3
  472. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  473. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  474. package/dist/modes/interactive/components/compaction-summary-message.js +1 -3
  475. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  476. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  477. package/dist/modes/interactive/components/config-selector.js +5 -22
  478. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  479. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  480. package/dist/modes/interactive/components/countdown-timer.js +1 -7
  481. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  482. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  483. package/dist/modes/interactive/components/custom-editor.js +8 -8
  484. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  485. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  486. package/dist/modes/interactive/components/custom-entry.js +1 -4
  487. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  488. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  489. package/dist/modes/interactive/components/custom-message.js +1 -7
  490. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  491. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  492. package/dist/modes/interactive/components/daxnuts.js +6 -8
  493. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  494. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  495. package/dist/modes/interactive/components/diff.js.map +1 -1
  496. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  497. package/dist/modes/interactive/components/dynamic-border.js +0 -1
  498. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  499. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  500. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
  501. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  502. package/dist/modes/interactive/components/extension-editor.js +1 -7
  503. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  504. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  505. package/dist/modes/interactive/components/extension-input.js +2 -8
  506. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  507. package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
  508. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  509. package/dist/modes/interactive/components/extension-selector.js +1 -9
  510. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  511. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  512. package/dist/modes/interactive/components/first-time-setup.js +2 -4
  513. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  514. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  515. package/dist/modes/interactive/components/footer.js +2 -4
  516. package/dist/modes/interactive/components/footer.js.map +1 -1
  517. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  518. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  519. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  520. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  521. package/dist/modes/interactive/components/login-dialog.js +3 -9
  522. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  523. package/dist/modes/interactive/components/markdown-transform.d.ts +3 -0
  524. package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
  525. package/dist/modes/interactive/components/markdown-transform.js +19 -0
  526. package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
  527. package/dist/modes/interactive/components/mermaid.d.ts +11 -0
  528. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  529. package/dist/modes/interactive/components/mermaid.js +75 -0
  530. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  531. package/dist/modes/interactive/components/model-selector.d.ts +1 -1
  532. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  533. package/dist/modes/interactive/components/model-selector.js +32 -30
  534. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  535. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  536. package/dist/modes/interactive/components/oauth-selector.js +3 -11
  537. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  538. package/dist/modes/interactive/components/scoped-models-selector.d.ts +4 -0
  539. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  540. package/dist/modes/interactive/components/scoped-models-selector.js +34 -13
  541. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  542. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  543. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  544. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  545. package/dist/modes/interactive/components/session-selector.js +35 -60
  546. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  547. package/dist/modes/interactive/components/settings-selector.d.ts +8 -2
  548. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  549. package/dist/modes/interactive/components/settings-selector.js +30 -14
  550. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  551. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  552. package/dist/modes/interactive/components/show-images-selector.js +0 -1
  553. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  554. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  555. package/dist/modes/interactive/components/skill-invocation-message.js +1 -3
  556. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  557. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  558. package/dist/modes/interactive/components/status-indicator.js +0 -2
  559. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  560. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  561. package/dist/modes/interactive/components/theme-selector.js +0 -2
  562. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  563. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  564. package/dist/modes/interactive/components/thinking-selector.js +0 -1
  565. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  566. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  567. package/dist/modes/interactive/components/tool-execution.js +18 -36
  568. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  569. package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
  570. package/dist/modes/interactive/components/tool-group.js +2 -3
  571. package/dist/modes/interactive/components/tool-group.js.map +1 -1
  572. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  573. package/dist/modes/interactive/components/tree-selector.js +19 -39
  574. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  575. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  576. package/dist/modes/interactive/components/trust-selector.js +0 -6
  577. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  578. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  579. package/dist/modes/interactive/components/user-message-selector.js +3 -6
  580. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  581. package/dist/modes/interactive/components/user-message.d.ts +3 -1
  582. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  583. package/dist/modes/interactive/components/user-message.js +8 -5
  584. package/dist/modes/interactive/components/user-message.js.map +1 -1
  585. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  586. package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
  587. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  588. package/dist/modes/interactive/external-editor.js +40 -3
  589. package/dist/modes/interactive/external-editor.js.map +1 -1
  590. package/dist/modes/interactive/interactive-mode.d.ts +46 -4
  591. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  592. package/dist/modes/interactive/interactive-mode.js +538 -275
  593. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  594. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  595. package/dist/modes/interactive/model-search.js.map +1 -1
  596. package/dist/modes/interactive/theme/dark.json +1 -0
  597. package/dist/modes/interactive/theme/light.json +1 -0
  598. package/dist/modes/interactive/theme/theme-controller.d.ts +3 -0
  599. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  600. package/dist/modes/interactive/theme/theme-controller.js +11 -8
  601. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  602. package/dist/modes/interactive/theme/theme-schema.json +5 -1
  603. package/dist/modes/interactive/theme/theme.d.ts +4 -4
  604. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  605. package/dist/modes/interactive/theme/theme.js +13 -9
  606. package/dist/modes/interactive/theme/theme.js.map +1 -1
  607. package/dist/modes/json-event.d.ts +28 -0
  608. package/dist/modes/json-event.d.ts.map +1 -0
  609. package/dist/modes/json-event.js +12 -0
  610. package/dist/modes/json-event.js.map +1 -0
  611. package/dist/modes/print-mode.d.ts.map +1 -1
  612. package/dist/modes/print-mode.js +12 -2
  613. package/dist/modes/print-mode.js.map +1 -1
  614. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  615. package/dist/modes/rpc/jsonl.js.map +1 -1
  616. package/dist/modes/rpc/rpc-client.d.ts +5 -4
  617. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  618. package/dist/modes/rpc/rpc-client.js +7 -8
  619. package/dist/modes/rpc/rpc-client.js.map +1 -1
  620. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  621. package/dist/modes/rpc/rpc-mode.js +17 -3
  622. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  623. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  624. package/dist/package-manager-cli.d.ts.map +1 -1
  625. package/dist/package-manager-cli.js +11 -9
  626. package/dist/package-manager-cli.js.map +1 -1
  627. package/dist/rpc-entry.d.ts.map +1 -1
  628. package/dist/rpc-entry.js +1 -0
  629. package/dist/rpc-entry.js.map +1 -1
  630. package/dist/utils/abort.d.ts +5 -0
  631. package/dist/utils/abort.d.ts.map +1 -0
  632. package/dist/utils/abort.js +48 -0
  633. package/dist/utils/abort.js.map +1 -0
  634. package/dist/utils/ansi.d.ts.map +1 -1
  635. package/dist/utils/ansi.js.map +1 -1
  636. package/dist/utils/changelog.d.ts.map +1 -1
  637. package/dist/utils/changelog.js.map +1 -1
  638. package/dist/utils/child-process.d.ts.map +1 -1
  639. package/dist/utils/child-process.js.map +1 -1
  640. package/dist/utils/clipboard-image.d.ts.map +1 -1
  641. package/dist/utils/clipboard-image.js.map +1 -1
  642. package/dist/utils/clipboard-native.d.ts.map +1 -1
  643. package/dist/utils/clipboard-native.js.map +1 -1
  644. package/dist/utils/clipboard.d.ts +1 -1
  645. package/dist/utils/clipboard.d.ts.map +1 -1
  646. package/dist/utils/clipboard.js +22 -2
  647. package/dist/utils/clipboard.js.map +1 -1
  648. package/dist/utils/coalescing-terminal.d.ts +1 -0
  649. package/dist/utils/coalescing-terminal.d.ts.map +1 -1
  650. package/dist/utils/coalescing-terminal.js +16 -8
  651. package/dist/utils/coalescing-terminal.js.map +1 -1
  652. package/dist/utils/deprecation.d.ts.map +1 -1
  653. package/dist/utils/deprecation.js.map +1 -1
  654. package/dist/utils/exif-orientation.d.ts.map +1 -1
  655. package/dist/utils/exif-orientation.js.map +1 -1
  656. package/dist/utils/frontmatter.d.ts.map +1 -1
  657. package/dist/utils/frontmatter.js.map +1 -1
  658. package/dist/utils/fs-watch.d.ts.map +1 -1
  659. package/dist/utils/fs-watch.js.map +1 -1
  660. package/dist/utils/git.d.ts.map +1 -1
  661. package/dist/utils/git.js.map +1 -1
  662. package/dist/utils/html.d.ts.map +1 -1
  663. package/dist/utils/html.js.map +1 -1
  664. package/dist/utils/image-convert.d.ts.map +1 -1
  665. package/dist/utils/image-convert.js.map +1 -1
  666. package/dist/utils/image-process.d.ts.map +1 -1
  667. package/dist/utils/image-process.js.map +1 -1
  668. package/dist/utils/image-resize-core.d.ts.map +1 -1
  669. package/dist/utils/image-resize-core.js.map +1 -1
  670. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  671. package/dist/utils/image-resize-worker.js.map +1 -1
  672. package/dist/utils/image-resize.d.ts.map +1 -1
  673. package/dist/utils/image-resize.js.map +1 -1
  674. package/dist/utils/json.d.ts.map +1 -1
  675. package/dist/utils/json.js.map +1 -1
  676. package/dist/utils/management-http.d.ts +23 -0
  677. package/dist/utils/management-http.d.ts.map +1 -0
  678. package/dist/utils/management-http.js +50 -0
  679. package/dist/utils/management-http.js.map +1 -0
  680. package/dist/utils/mime.d.ts.map +1 -1
  681. package/dist/utils/mime.js.map +1 -1
  682. package/dist/utils/open-browser.d.ts.map +1 -1
  683. package/dist/utils/open-browser.js.map +1 -1
  684. package/dist/utils/paths.d.ts +5 -3
  685. package/dist/utils/paths.d.ts.map +1 -1
  686. package/dist/utils/paths.js +25 -4
  687. package/dist/utils/paths.js.map +1 -1
  688. package/dist/utils/photon.d.ts.map +1 -1
  689. package/dist/utils/photon.js.map +1 -1
  690. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  691. package/dist/utils/pi-user-agent.js.map +1 -1
  692. package/dist/utils/shell.d.ts.map +1 -1
  693. package/dist/utils/shell.js.map +1 -1
  694. package/dist/utils/sleep.d.ts.map +1 -1
  695. package/dist/utils/sleep.js.map +1 -1
  696. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  697. package/dist/utils/syntax-highlight.js.map +1 -1
  698. package/dist/utils/tool-result-images.d.ts +19 -0
  699. package/dist/utils/tool-result-images.d.ts.map +1 -0
  700. package/dist/utils/tool-result-images.js +45 -0
  701. package/dist/utils/tool-result-images.js.map +1 -0
  702. package/dist/utils/tools-manager.d.ts.map +1 -1
  703. package/dist/utils/tools-manager.js +4 -6
  704. package/dist/utils/tools-manager.js.map +1 -1
  705. package/dist/utils/version-check.d.ts +4 -0
  706. package/dist/utils/version-check.d.ts.map +1 -1
  707. package/dist/utils/version-check.js +20 -2
  708. package/dist/utils/version-check.js.map +1 -1
  709. package/dist/utils/windows-self-update.d.ts.map +1 -1
  710. package/dist/utils/windows-self-update.js.map +1 -1
  711. package/docs/bundled/README.md +14 -16
  712. package/docs/bundled/extensions/biu.md +63 -0
  713. package/docs/bundled/extensions/router.md +20 -14
  714. package/docs/bundled/extensions/statusline.md +7 -5
  715. package/docs/bundled/themes.md +1 -1
  716. package/docs/compaction.md +25 -30
  717. package/docs/custom-provider.md +303 -467
  718. package/docs/development.md +83 -0
  719. package/docs/docs.json +6 -10
  720. package/docs/extensions.md +139 -119
  721. package/docs/index.md +18 -74
  722. package/docs/json.md +40 -15
  723. package/docs/keybindings.md +38 -10
  724. package/docs/models.md +210 -150
  725. package/docs/providers.md +3 -1
  726. package/docs/quickstart.md +5 -3
  727. package/docs/rpc.md +177 -134
  728. package/docs/sdk.md +165 -178
  729. package/docs/security.md +2 -2
  730. package/docs/session-format.md +139 -119
  731. package/docs/settings.md +4 -1
  732. package/docs/skills.md +0 -1
  733. package/docs/themes.md +5 -3
  734. package/docs/tui.md +411 -549
  735. package/docs/usage.md +11 -11
  736. package/examples/README.md +6 -1
  737. package/examples/extensions/README.md +13 -33
  738. package/examples/extensions/custom-compaction.ts +1 -16
  739. package/examples/extensions/custom-provider-anthropic/index.ts +11 -4
  740. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -1
  741. package/examples/extensions/doom-overlay/wad-finder.ts +7 -3
  742. package/examples/extensions/handoff.ts +2 -11
  743. package/examples/extensions/qna.ts +3 -7
  744. package/examples/extensions/summarize.ts +8 -18
  745. package/examples/extensions/truncated-tool.ts +2 -2
  746. package/examples/rpc-extension-ui.ts +11 -2
  747. package/examples/sdk/02-custom-model.ts +1 -2
  748. package/examples/sdk/09-api-keys-and-oauth.ts +1 -1
  749. package/examples/sdk/12-full-control.ts +3 -1
  750. package/examples/sdk/README.md +1 -0
  751. package/npm-shrinkwrap.json +199 -270
  752. package/package.json +16 -9
  753. package/dist/extensions/plan/constants.d.ts +0 -68
  754. package/dist/extensions/plan/constants.d.ts.map +0 -1
  755. package/dist/extensions/plan/constants.js +0 -100
  756. package/dist/extensions/plan/constants.js.map +0 -1
  757. package/dist/extensions/plan/index.d.ts +0 -30
  758. package/dist/extensions/plan/index.d.ts.map +0 -1
  759. package/dist/extensions/plan/index.js +0 -429
  760. package/dist/extensions/plan/index.js.map +0 -1
  761. package/dist/extensions/plan/schema.d.ts +0 -20
  762. package/dist/extensions/plan/schema.d.ts.map +0 -1
  763. package/dist/extensions/plan/schema.js +0 -13
  764. package/dist/extensions/plan/schema.js.map +0 -1
  765. package/dist/extensions/plan/state.d.ts +0 -38
  766. package/dist/extensions/plan/state.d.ts.map +0 -1
  767. package/dist/extensions/plan/state.js +0 -96
  768. package/dist/extensions/plan/state.js.map +0 -1
  769. package/dist/extensions/plan/storage.d.ts +0 -26
  770. package/dist/extensions/plan/storage.d.ts.map +0 -1
  771. package/dist/extensions/plan/storage.js +0 -83
  772. package/dist/extensions/plan/storage.js.map +0 -1
  773. package/dist/extensions/plan/view.d.ts +0 -44
  774. package/dist/extensions/plan/view.d.ts.map +0 -1
  775. package/dist/extensions/plan/view.js +0 -149
  776. package/dist/extensions/plan/view.js.map +0 -1
  777. package/docs/bundled/extensions/README.md +0 -19
  778. package/docs/bundled/extensions/plan.md +0 -34
  779. package/examples/extensions/plan-mode/README.md +0 -66
  780. package/examples/extensions/plan-mode/index.ts +0 -390
  781. package/examples/extensions/plan-mode/utils.ts +0 -168
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/index.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gCAAgC,CAAC;AA8GrF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA6KrD","sourcesContent":["/**\n * rewind — file-backed checkpoint & rewind for Pi.\n *\n * Engine: instead of snapshotting the whole work tree into a per-session shadow\n * git repo (the old design, which froze the UI in large directories like a\n * multi-project parent folder and grew storage without bound), we back up ONLY\n * the files Pi's edit/write tools are about to modify — one copyFile before each\n * edit (see engine.ts, ported from Claude Code's file-history). Cost is\n * proportional to how many files Pi changed, never to project size.\n *\n * Per turn: before_agent_start opens a snapshot frame (re-recording tracked files\n * at their turn-start state); tool_call(edit|write) backs up each newly edited\n * file before it lands; agent_settled persists the frame to the session JSONL\n * (custom \"pi-rewind-snapshot\" entry) when it changed. agent_settled is used\n * instead of agent_end so auto-retry, overflow compaction-retry, and queued\n * follow-ups stay in one logical turn (agent_end can fire mid-continuation).\n * The frame anchors to the FIRST user entry the run appended (tracked via\n * anchorScanStart + anchor.ts) — the entry whose start the frame recorded;\n * steering/follow-up messages append later user entries that must not steal\n * the anchor. On session_start the index is rebuilt from those entries;\n * resume/fork hard-links the prior session's blobs.\n *\n * Time-travel is fused into /tree: navigating to a node whose turn changed files\n * offers to sync the work tree to that point (session_before_tree/session_tree).\n * Snapshot selection mirrors navigateTree's leaf rules (see restore.ts).\n * /rewind itself is a settings + storage menu (menu.ts), not a restore picker.\n *\n * Restore safety: applySnapshot only rewrites files that differ and never throws\n * out — a broken backup degrades to \"leave the file alone\", so it can never abort\n * the user's session.\n *\n * Architecture informed by oh-my-pi (GPL-3.0) and Claude Code's file-history;\n * independent implementation. No ANSI: all UI is native (ctx.ui.* + theme).\n */\nimport path from \"node:path\";\n\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../core/extensions/types.ts\";\n\nimport { firstUserEntryIdAfter } from \"./anchor.ts\";\nimport { loadRewindConfig, type RewindConfig, reloadRewindConfig } from \"./config.ts\";\nimport {\n\tbeginTurn,\n\tcapSnapshots,\n\tdisposeSession,\n\tendTurn,\n\tgetSnapshots,\n\tmigrateBackupsFromSession,\n\tregisterSession,\n\trestoreStateFromSnapshots,\n\ttrackEdit,\n} from \"./engine.ts\";\nimport { runGc, sessionIdFromFile } from \"./gc.ts\";\nimport { runRewindMenu } from \"./menu.ts\";\nimport { rewindBackupsRoot, sessionsDirectory } from \"./paths.ts\";\nimport {\n\ttype EntryTreeView,\n\trestoreToSnapshot,\n\tsnapshotChangeDiffStats,\n\tsnapshotChangedPaths,\n\tsnapshotForEntry,\n} from \"./restore.ts\";\nimport { type FileHistorySnapshot, isSnapshot, SNAPSHOT_ENTRY_TYPE } from \"./snapshot.ts\";\nimport { configureStorage } from \"./storage.ts\";\nimport { truncateText } from \"./text.ts\";\nimport { editWriteTargetPath, resolveToolPath } from \"./tool-path.ts\";\n\n// Bind the engine/gc storage roots to the real on-disk locations (they avoid\n// importing paths.ts directly so they stay node-testable). Safe to call at load.\nconfigureStorage({ backupsRoot: rewindBackupsRoot(), sessionsRoot: sessionsDirectory() });\n\n// Global (one config.json). Reloaded from disk at session_start; /rewind menu\n// saveRewindConfig updates the in-memory cache so the next turn sees changes\n// without re-reading the file every before_agent_start.\nlet config: RewindConfig = loadRewindConfig();\n\n/** /tree confirm → apply payload (changedPaths skips a second originChanged pass). */\ninterface PendingTreeRestore {\n\tsnapshot: FileHistorySnapshot;\n\tchangedPaths: string[];\n}\n\n// Per-session transient state held by the integration layer.\nconst pendingPrompt = new Map<string, string>(); // turn prompt, captured for the snapshot label\nconst pendingTreeRestore = new Map<string, PendingTreeRestore | null>(); // /tree sync intent\n// Leaf id before the current run started (null = branch root). agent_settled\n// scans forward from here for the run's FIRST user entry — the frame's anchor.\nconst anchorScanStart = new Map<string, string | null>();\n\n// ---- helpers --------------------------------------------------------------\n\n/** Record the current leaf as the scan start for the next frame's anchor. */\nfunction markAnchorScanStart(sid: string, ctx: ExtensionContext): void {\n\tanchorScanStart.set(sid, ctx.sessionManager.getLeafId() ?? null);\n}\n\n/** Adapt the session manager to restore.ts's tree view (turn-anchor = navigateTree's \"leaf = parent\" entries). */\nfunction sessionTreeView(ctx: ExtensionContext): EntryTreeView {\n\treturn {\n\t\tgetEntry(id) {\n\t\t\tconst e = ctx.sessionManager.getEntry(id);\n\t\t\tif (!e) return undefined;\n\t\t\tconst isTurnAnchor =\n\t\t\t\t(e.type === \"message\" && (e.message as AgentMessage).role === \"user\") || e.type === \"custom_message\";\n\t\t\treturn { id: e.id, parentId: e.parentId, isTurnAnchor };\n\t\t},\n\t};\n}\n\n/** Rebuild the snapshot list for a session from its persisted custom entries. */\nfunction rebuildSnapshots(ctx: ExtensionContext): FileHistorySnapshot[] {\n\tconst out: FileHistorySnapshot[] = [];\n\tfor (const entry of ctx.sessionManager.getEntries()) {\n\t\tif (entry.type === \"custom\" && entry.customType === SNAPSHOT_ENTRY_TYPE && isSnapshot(entry.data)) {\n\t\t\tout.push(entry.data);\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Max files listed in the /tree restore confirmation before \"+N more\". */\nconst RESTORE_PREVIEW_LIMIT = 8;\n/** Max characters per previewed path (leading-truncated: the filename matters most). */\nconst RESTORE_PREVIEW_PATH_MAX = 64;\n\n/**\n * Bounded cwd-relative file list shown under the /tree restore question, so an\n * irreversible work-tree rewrite is confirmed against WHICH files, not just a\n * count. Multi-line select titles are upstream-sanctioned (ui.confirm joins\n * title + message with \\n through the same selector).\n */\nfunction formatRestorePreview(changedPaths: string[], cwd: string): string {\n\tconst lines = changedPaths.slice(0, RESTORE_PREVIEW_LIMIT).map((p) => {\n\t\tconst rel = path.relative(cwd, p);\n\t\tconst display = rel && !rel.startsWith(\"..\") && !path.isAbsolute(rel) ? rel : p;\n\t\tconst bounded =\n\t\t\tdisplay.length > RESTORE_PREVIEW_PATH_MAX ? `…${display.slice(-(RESTORE_PREVIEW_PATH_MAX - 1))}` : display;\n\t\treturn ` ${bounded}`;\n\t});\n\tif (changedPaths.length > RESTORE_PREVIEW_LIMIT) {\n\t\tlines.push(` … +${changedPaths.length - RESTORE_PREVIEW_LIMIT} more`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---- extension entry ------------------------------------------------------\n\nexport default function rewind(pi: ExtensionAPI): void {\n\t// session_start: rebuild the index, migrate fork/resume blobs, reclaim storage.\n\tpi.on(\"session_start\", async (event, ctx) => {\n\t\tconfig = reloadRewindConfig();\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tregisterSession(sid, ctx.cwd);\n\n\t\tconst snapshots = rebuildSnapshots(ctx);\n\n\t\t// Migrate BEFORE rebuilding in-memory state: restoreStateFromSnapshots\n\t\t// prunes over-cap blobs from THIS session's directory (fire-and-forget),\n\t\t// and linking only the retained frames first keeps the two steps disjoint\n\t\t// — no race, and dropped frames' blobs are never linked at all.\n\t\tif ((event.reason === \"resume\" || event.reason === \"fork\") && event.previousSessionFile) {\n\t\t\tconst prevSid = sessionIdFromFile(event.previousSessionFile);\n\t\t\tif (prevSid) {\n\t\t\t\ttry {\n\t\t\t\t\tawait migrateBackupsFromSession(prevSid, sid, capSnapshots(snapshots, config.maxSnapshots));\n\t\t\t\t} catch {\n\t\t\t\t\t// best-effort; a missing blob just means that version can't restore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trestoreStateFromSnapshots(sid, ctx.cwd, snapshots, config.maxSnapshots);\n\t\tmarkAnchorScanStart(sid, ctx);\n\n\t\ttry {\n\t\t\trunGc(config.retentionDays, sid);\n\t\t} catch {\n\t\t\t// GC is best-effort; never block startup\n\t\t}\n\t});\n\n\t// tool_call: back up the target file BEFORE the edit/write lands. Synchronous\n\t// so the backup is on disk before the hook returns control to the agent loop.\n\tpi.on(\"tool_call\", (event, ctx) => {\n\t\tif (event.toolName !== \"edit\" && event.toolName !== \"write\") return;\n\t\tif (!config.enabled) return;\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tconst rawPath = editWriteTargetPath(event.input);\n\t\tif (!rawPath) return;\n\t\tconst abs = resolveToolPath(rawPath, ctx.cwd);\n\t\ttry {\n\t\t\ttrackEdit(sid, abs);\n\t\t} catch {\n\t\t\t// never block the edit on a backup failure\n\t\t}\n\t});\n\n\t// before_agent_start: open the turn's snapshot frame.\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\t// Memory-cached; session_start reloads disk, /rewind save updates cache.\n\t\tconfig = loadRewindConfig();\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// The run's user entry is appended after this hook; the frame's anchor is\n\t\t// the first user entry that appears past this leaf.\n\t\tmarkAnchorScanStart(sid, ctx);\n\t\tif (!config.enabled) return;\n\t\tpendingPrompt.set(sid, event.prompt ?? \"\");\n\t\ttry {\n\t\t\tawait beginTurn(sid);\n\t\t} catch {\n\t\t\t// non-fatal\n\t\t}\n\t});\n\n\t// agent_settled: finalize + persist after Pi will not auto-continue (retry /\n\t// compact-retry / follow-up). agent_end alone is too early for that.\n\tpi.on(\"agent_settled\", async (_event, ctx) => {\n\t\tif (!config.enabled) return;\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// Anchor = the run's FIRST appended user entry (whose start the frame\n\t\t// recorded). Steering/follow-up messages consumed in the same run append\n\t\t// later user entries and must not steal the anchor. Unresolvable (scan\n\t\t// start missing / off-branch / no user entry appended) -> endTurn discards.\n\t\tconst scanStart = anchorScanStart.get(sid);\n\t\tconst userEntryId =\n\t\t\tscanStart === undefined ? \"\" : (firstUserEntryIdAfter(ctx.sessionManager.getBranch(), scanStart) ?? \"\");\n\t\tconst turnId = ctx.sessionManager.getLeafId() ?? userEntryId;\n\t\tconst prompt = truncateText(pendingPrompt.get(sid) ?? \"\", 120, { collapseWhitespace: true });\n\t\tpendingPrompt.delete(sid);\n\t\tconst frame = endTurn(sid, userEntryId, turnId, prompt, new Date().toISOString(), config.maxSnapshots);\n\t\tif (frame) {\n\t\t\tpi.appendEntry(SNAPSHOT_ENTRY_TYPE, frame);\n\t\t}\n\t\t// Next run may start without before_agent_start (custom-triggered): scan\n\t\t// from the settled leaf (past this run's entries + the snapshot entry).\n\t\tmarkAnchorScanStart(sid, ctx);\n\t});\n\n\t// session_before_tree: offer to sync files when navigating to a changed point.\n\tpi.on(\"session_before_tree\", async (event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tpendingTreeRestore.set(sid, null);\n\t\tif (!config.enabled) return;\n\n\t\tconst target = snapshotForEntry(getSnapshots(sid), sessionTreeView(ctx), event.preparation.targetId);\n\t\tif (!target) return;\n\t\tconst changed = await snapshotChangedPaths(sid, target);\n\t\tif (changed.length === 0) return; // silent nav, like native /tree\n\t\t// Lifecycle handler: silent-return without UI. (hasUI is the guard for\n\t\t// ctx.ui.*; checking ctx.mode here would wrongly proceed when a TUI\n\t\t// session has no usable UI.)\n\t\tif (!ctx.hasUI) return;\n\n\t\tconst n = changed.length;\n\t\tlet lineStats = \"\";\n\t\ttry {\n\t\t\tconst stats = await snapshotChangeDiffStats(sid, target, changed);\n\t\t\tif (stats.insertions > 0 || stats.deletions > 0) {\n\t\t\t\tlineStats = ` (+${stats.insertions} / −${stats.deletions})`;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Coarse stats are best-effort; still offer path preview.\n\t\t}\n\t\tconst choice = await ctx.ui.select(\n\t\t\t`Restore ${n} file${n === 1 ? \"\" : \"s\"} to this point?${lineStats}\\n${formatRestorePreview(changed, ctx.cwd)}\\n (covers edit/write changes only; files changed via bash are not tracked)`,\n\t\t\t[\"Yes, restore files\", \"No, conversation only\"],\n\t\t);\n\t\tif (choice?.startsWith(\"Yes\")) {\n\t\t\t// Cache paths so session_tree can apply without a second full compare.\n\t\t\tpendingTreeRestore.set(sid, { snapshot: target, changedPaths: changed });\n\t\t}\n\t});\n\n\t// session_tree: execute any sync intent recorded above.\n\tpi.on(\"session_tree\", async (_event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// The leaf moved; the old scan start may now be off-branch. Re-mark so a\n\t\t// custom-triggered run after navigation still anchors correctly.\n\t\tmarkAnchorScanStart(sid, ctx);\n\t\tconst pending = pendingTreeRestore.get(sid);\n\t\tpendingTreeRestore.set(sid, null);\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst changed = await restoreToSnapshot(sid, pending.snapshot, {\n\t\t\t\tonlyPaths: new Set(pending.changedPaths),\n\t\t\t});\n\t\t\tif (changed.length > 0) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Restored ${changed.length} file${changed.length === 1 ? \"\" : \"s\"} to this checkpoint.`,\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tctx.ui.notify(`Rewind restore failed: ${String(e)}`, \"warning\");\n\t\t}\n\t});\n\n\t// session_shutdown: drop this session's in-memory state.\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tdisposeSession(sid);\n\t\tpendingTreeRestore.delete(sid);\n\t\tpendingPrompt.delete(sid);\n\t\tanchorScanStart.delete(sid);\n\t});\n\n\t// /rewind: settings + storage menu (time-travel itself is via /tree).\n\tpi.registerCommand(\"rewind\", {\n\t\tdescription: \"Rewind settings and backup storage (restore is via /tree)\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tawait runRewindMenu(ctx);\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/index.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gCAAgC,CAAC;AA8GrF,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA6KrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/rewind/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * rewind — file-backed checkpoint & rewind for Pi.\n *\n * Engine: instead of snapshotting the whole work tree into a per-session shadow\n * git repo (the old design, which froze the UI in large directories like a\n * multi-project parent folder and grew storage without bound), we back up ONLY\n * the files Pi's edit/write tools are about to modify — one copyFile before each\n * edit (see engine.ts, ported from Claude Code's file-history). Cost is\n * proportional to how many files Pi changed, never to project size.\n *\n * Per turn: before_agent_start opens a snapshot frame (re-recording tracked files\n * at their turn-start state); tool_call(edit|write) backs up each newly edited\n * file before it lands; agent_settled persists the frame to the session JSONL\n * (custom \"pi-rewind-snapshot\" entry) when it changed. agent_settled is used\n * instead of agent_end so auto-retry, overflow compaction-retry, and queued\n * follow-ups stay in one logical turn (agent_end can fire mid-continuation).\n * The frame anchors to the FIRST user entry the run appended (tracked via\n * anchorScanStart + anchor.ts) — the entry whose start the frame recorded;\n * steering/follow-up messages append later user entries that must not steal\n * the anchor. On session_start the index is rebuilt from those entries;\n * resume/fork hard-links the prior session's blobs.\n *\n * Time-travel is fused into /tree: navigating to a node whose turn changed files\n * offers to sync the work tree to that point (session_before_tree/session_tree).\n * Snapshot selection mirrors navigateTree's leaf rules (see restore.ts).\n * /rewind itself is a settings + storage menu (menu.ts), not a restore picker.\n *\n * Restore safety: applySnapshot only rewrites files that differ and never throws\n * out — a broken backup degrades to \"leave the file alone\", so it can never abort\n * the user's session.\n *\n * Architecture informed by oh-my-pi (GPL-3.0) and Claude Code's file-history;\n * independent implementation. No ANSI: all UI is native (ctx.ui.* + theme).\n */\nimport path from \"node:path\";\n\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../core/extensions/types.ts\";\n\nimport { firstUserEntryIdAfter } from \"./anchor.ts\";\nimport { loadRewindConfig, type RewindConfig, reloadRewindConfig } from \"./config.ts\";\nimport {\n\tbeginTurn,\n\tcapSnapshots,\n\tdisposeSession,\n\tendTurn,\n\tgetSnapshots,\n\tmigrateBackupsFromSession,\n\tregisterSession,\n\trestoreStateFromSnapshots,\n\ttrackEdit,\n} from \"./engine.ts\";\nimport { runGc, sessionIdFromFile } from \"./gc.ts\";\nimport { runRewindMenu } from \"./menu.ts\";\nimport { rewindBackupsRoot, sessionsDirectory } from \"./paths.ts\";\nimport {\n\ttype EntryTreeView,\n\trestoreToSnapshot,\n\tsnapshotChangeDiffStats,\n\tsnapshotChangedPaths,\n\tsnapshotForEntry,\n} from \"./restore.ts\";\nimport { type FileHistorySnapshot, isSnapshot, SNAPSHOT_ENTRY_TYPE } from \"./snapshot.ts\";\nimport { configureStorage } from \"./storage.ts\";\nimport { truncateText } from \"./text.ts\";\nimport { editWriteTargetPath, resolveToolPath } from \"./tool-path.ts\";\n\n// Bind the engine/gc storage roots to the real on-disk locations (they avoid\n// importing paths.ts directly so they stay node-testable). Safe to call at load.\nconfigureStorage({ backupsRoot: rewindBackupsRoot(), sessionsRoot: sessionsDirectory() });\n\n// Global (one config.json). Reloaded from disk at session_start; /rewind menu\n// saveRewindConfig updates the in-memory cache so the next turn sees changes\n// without re-reading the file every before_agent_start.\nlet config: RewindConfig = loadRewindConfig();\n\n/** /tree confirm → apply payload (changedPaths skips a second originChanged pass). */\ninterface PendingTreeRestore {\n\tsnapshot: FileHistorySnapshot;\n\tchangedPaths: string[];\n}\n\n// Per-session transient state held by the integration layer.\nconst pendingPrompt = new Map<string, string>(); // turn prompt, captured for the snapshot label\nconst pendingTreeRestore = new Map<string, PendingTreeRestore | null>(); // /tree sync intent\n// Leaf id before the current run started (null = branch root). agent_settled\n// scans forward from here for the run's FIRST user entry — the frame's anchor.\nconst anchorScanStart = new Map<string, string | null>();\n\n// ---- helpers --------------------------------------------------------------\n\n/** Record the current leaf as the scan start for the next frame's anchor. */\nfunction markAnchorScanStart(sid: string, ctx: ExtensionContext): void {\n\tanchorScanStart.set(sid, ctx.sessionManager.getLeafId() ?? null);\n}\n\n/** Adapt the session manager to restore.ts's tree view (turn-anchor = navigateTree's \"leaf = parent\" entries). */\nfunction sessionTreeView(ctx: ExtensionContext): EntryTreeView {\n\treturn {\n\t\tgetEntry(id) {\n\t\t\tconst e = ctx.sessionManager.getEntry(id);\n\t\t\tif (!e) return undefined;\n\t\t\tconst isTurnAnchor =\n\t\t\t\t(e.type === \"message\" && (e.message as AgentMessage).role === \"user\") || e.type === \"custom_message\";\n\t\t\treturn { id: e.id, parentId: e.parentId, isTurnAnchor };\n\t\t},\n\t};\n}\n\n/** Rebuild the snapshot list for a session from its persisted custom entries. */\nfunction rebuildSnapshots(ctx: ExtensionContext): FileHistorySnapshot[] {\n\tconst out: FileHistorySnapshot[] = [];\n\tfor (const entry of ctx.sessionManager.getEntries()) {\n\t\tif (entry.type === \"custom\" && entry.customType === SNAPSHOT_ENTRY_TYPE && isSnapshot(entry.data)) {\n\t\t\tout.push(entry.data);\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Max files listed in the /tree restore confirmation before \"+N more\". */\nconst RESTORE_PREVIEW_LIMIT = 8;\n/** Max characters per previewed path (leading-truncated: the filename matters most). */\nconst RESTORE_PREVIEW_PATH_MAX = 64;\n\n/**\n * Bounded cwd-relative file list shown under the /tree restore question, so an\n * irreversible work-tree rewrite is confirmed against WHICH files, not just a\n * count. Multi-line select titles are upstream-sanctioned (ui.confirm joins\n * title + message with \\n through the same selector).\n */\nfunction formatRestorePreview(changedPaths: string[], cwd: string): string {\n\tconst lines = changedPaths.slice(0, RESTORE_PREVIEW_LIMIT).map((p) => {\n\t\tconst rel = path.relative(cwd, p);\n\t\tconst display = rel && !rel.startsWith(\"..\") && !path.isAbsolute(rel) ? rel : p;\n\t\tconst bounded =\n\t\t\tdisplay.length > RESTORE_PREVIEW_PATH_MAX ? `…${display.slice(-(RESTORE_PREVIEW_PATH_MAX - 1))}` : display;\n\t\treturn ` ${bounded}`;\n\t});\n\tif (changedPaths.length > RESTORE_PREVIEW_LIMIT) {\n\t\tlines.push(` … +${changedPaths.length - RESTORE_PREVIEW_LIMIT} more`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---- extension entry ------------------------------------------------------\n\nexport default function rewind(pi: ExtensionAPI): void {\n\t// session_start: rebuild the index, migrate fork/resume blobs, reclaim storage.\n\tpi.on(\"session_start\", async (event, ctx) => {\n\t\tconfig = reloadRewindConfig();\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tregisterSession(sid, ctx.cwd);\n\n\t\tconst snapshots = rebuildSnapshots(ctx);\n\n\t\t// Migrate BEFORE rebuilding in-memory state: restoreStateFromSnapshots\n\t\t// prunes over-cap blobs from THIS session's directory (fire-and-forget),\n\t\t// and linking only the retained frames first keeps the two steps disjoint\n\t\t// — no race, and dropped frames' blobs are never linked at all.\n\t\tif ((event.reason === \"resume\" || event.reason === \"fork\") && event.previousSessionFile) {\n\t\t\tconst prevSid = sessionIdFromFile(event.previousSessionFile);\n\t\t\tif (prevSid) {\n\t\t\t\ttry {\n\t\t\t\t\tawait migrateBackupsFromSession(prevSid, sid, capSnapshots(snapshots, config.maxSnapshots));\n\t\t\t\t} catch {\n\t\t\t\t\t// best-effort; a missing blob just means that version can't restore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trestoreStateFromSnapshots(sid, ctx.cwd, snapshots, config.maxSnapshots);\n\t\tmarkAnchorScanStart(sid, ctx);\n\n\t\ttry {\n\t\t\trunGc(config.retentionDays, sid);\n\t\t} catch {\n\t\t\t// GC is best-effort; never block startup\n\t\t}\n\t});\n\n\t// tool_call: back up the target file BEFORE the edit/write lands. Synchronous\n\t// so the backup is on disk before the hook returns control to the agent loop.\n\tpi.on(\"tool_call\", (event, ctx) => {\n\t\tif (event.toolName !== \"edit\" && event.toolName !== \"write\") return;\n\t\tif (!config.enabled) return;\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tconst rawPath = editWriteTargetPath(event.input);\n\t\tif (!rawPath) return;\n\t\tconst abs = resolveToolPath(rawPath, ctx.cwd);\n\t\ttry {\n\t\t\ttrackEdit(sid, abs);\n\t\t} catch {\n\t\t\t// never block the edit on a backup failure\n\t\t}\n\t});\n\n\t// before_agent_start: open the turn's snapshot frame.\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\t// Memory-cached; session_start reloads disk, /rewind save updates cache.\n\t\tconfig = loadRewindConfig();\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// The run's user entry is appended after this hook; the frame's anchor is\n\t\t// the first user entry that appears past this leaf.\n\t\tmarkAnchorScanStart(sid, ctx);\n\t\tif (!config.enabled) return;\n\t\tpendingPrompt.set(sid, event.prompt ?? \"\");\n\t\ttry {\n\t\t\tawait beginTurn(sid);\n\t\t} catch {\n\t\t\t// non-fatal\n\t\t}\n\t});\n\n\t// agent_settled: finalize + persist after Pi will not auto-continue (retry /\n\t// compact-retry / follow-up). agent_end alone is too early for that.\n\tpi.on(\"agent_settled\", async (_event, ctx) => {\n\t\tif (!config.enabled) return;\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// Anchor = the run's FIRST appended user entry (whose start the frame\n\t\t// recorded). Steering/follow-up messages consumed in the same run append\n\t\t// later user entries and must not steal the anchor. Unresolvable (scan\n\t\t// start missing / off-branch / no user entry appended) -> endTurn discards.\n\t\tconst scanStart = anchorScanStart.get(sid);\n\t\tconst userEntryId =\n\t\t\tscanStart === undefined ? \"\" : (firstUserEntryIdAfter(ctx.sessionManager.getBranch(), scanStart) ?? \"\");\n\t\tconst turnId = ctx.sessionManager.getLeafId() ?? userEntryId;\n\t\tconst prompt = truncateText(pendingPrompt.get(sid) ?? \"\", 120, { collapseWhitespace: true });\n\t\tpendingPrompt.delete(sid);\n\t\tconst frame = endTurn(sid, userEntryId, turnId, prompt, new Date().toISOString(), config.maxSnapshots);\n\t\tif (frame) {\n\t\t\tpi.appendEntry(SNAPSHOT_ENTRY_TYPE, frame);\n\t\t}\n\t\t// Next run may start without before_agent_start (custom-triggered): scan\n\t\t// from the settled leaf (past this run's entries + the snapshot entry).\n\t\tmarkAnchorScanStart(sid, ctx);\n\t});\n\n\t// session_before_tree: offer to sync files when navigating to a changed point.\n\tpi.on(\"session_before_tree\", async (event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tpendingTreeRestore.set(sid, null);\n\t\tif (!config.enabled) return;\n\n\t\tconst target = snapshotForEntry(getSnapshots(sid), sessionTreeView(ctx), event.preparation.targetId);\n\t\tif (!target) return;\n\t\tconst changed = await snapshotChangedPaths(sid, target);\n\t\tif (changed.length === 0) return; // silent nav, like native /tree\n\t\t// Lifecycle handler: silent-return without UI. (hasUI is the guard for\n\t\t// ctx.ui.*; checking ctx.mode here would wrongly proceed when a TUI\n\t\t// session has no usable UI.)\n\t\tif (!ctx.hasUI) return;\n\n\t\tconst n = changed.length;\n\t\tlet lineStats = \"\";\n\t\ttry {\n\t\t\tconst stats = await snapshotChangeDiffStats(sid, target, changed);\n\t\t\tif (stats.insertions > 0 || stats.deletions > 0) {\n\t\t\t\tlineStats = ` (+${stats.insertions} / −${stats.deletions})`;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Coarse stats are best-effort; still offer path preview.\n\t\t}\n\t\tconst choice = await ctx.ui.select(\n\t\t\t`Restore ${n} file${n === 1 ? \"\" : \"s\"} to this point?${lineStats}\\n${formatRestorePreview(changed, ctx.cwd)}\\n (covers edit/write changes only; files changed via bash are not tracked)`,\n\t\t\t[\"Yes, restore files\", \"No, conversation only\"],\n\t\t);\n\t\tif (choice?.startsWith(\"Yes\")) {\n\t\t\t// Cache paths so session_tree can apply without a second full compare.\n\t\t\tpendingTreeRestore.set(sid, { snapshot: target, changedPaths: changed });\n\t\t}\n\t});\n\n\t// session_tree: execute any sync intent recorded above.\n\tpi.on(\"session_tree\", async (_event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// The leaf moved; the old scan start may now be off-branch. Re-mark so a\n\t\t// custom-triggered run after navigation still anchors correctly.\n\t\tmarkAnchorScanStart(sid, ctx);\n\t\tconst pending = pendingTreeRestore.get(sid);\n\t\tpendingTreeRestore.set(sid, null);\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst changed = await restoreToSnapshot(sid, pending.snapshot, {\n\t\t\t\tonlyPaths: new Set(pending.changedPaths),\n\t\t\t});\n\t\t\tif (changed.length > 0) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Restored ${changed.length} file${changed.length === 1 ? \"\" : \"s\"} to this checkpoint.`,\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tctx.ui.notify(`Rewind restore failed: ${String(e)}`, \"warning\");\n\t\t}\n\t});\n\n\t// session_shutdown: drop this session's in-memory state.\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tdisposeSession(sid);\n\t\tpendingTreeRestore.delete(sid);\n\t\tpendingPrompt.delete(sid);\n\t\tanchorScanStart.delete(sid);\n\t});\n\n\t// /rewind: settings + storage menu (time-travel itself is via /tree).\n\tpi.registerCommand(\"rewind\", {\n\t\tdescription: \"Rewind settings and backup storage (restore is via /tree)\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tawait runRewindMenu(ctx);\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/rewind/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * rewind — file-backed checkpoint & rewind for Pi.\n *\n * Engine: instead of snapshotting the whole work tree into a per-session shadow\n * git repo (the old design, which froze the UI in large directories like a\n * multi-project parent folder and grew storage without bound), we back up ONLY\n * the files Pi's edit/write tools are about to modify — one copyFile before each\n * edit (see engine.ts, ported from Claude Code's file-history). Cost is\n * proportional to how many files Pi changed, never to project size.\n *\n * Per turn: before_agent_start opens a snapshot frame (re-recording tracked files\n * at their turn-start state); tool_call(edit|write) backs up each newly edited\n * file before it lands; agent_settled persists the frame to the session JSONL\n * (custom \"pi-rewind-snapshot\" entry) when it changed. agent_settled is used\n * instead of agent_end so auto-retry, overflow compaction-retry, and queued\n * follow-ups stay in one logical turn (agent_end can fire mid-continuation).\n * The frame anchors to the FIRST user entry the run appended (tracked via\n * anchorScanStart + anchor.ts) — the entry whose start the frame recorded;\n * steering/follow-up messages append later user entries that must not steal\n * the anchor. On session_start the index is rebuilt from those entries;\n * resume/fork hard-links the prior session's blobs.\n *\n * Time-travel is fused into /tree: navigating to a node whose turn changed files\n * offers to sync the work tree to that point (session_before_tree/session_tree).\n * Snapshot selection mirrors navigateTree's leaf rules (see restore.ts).\n * /rewind itself is a settings + storage menu (menu.ts), not a restore picker.\n *\n * Restore safety: applySnapshot only rewrites files that differ and never throws\n * out — a broken backup degrades to \"leave the file alone\", so it can never abort\n * the user's session.\n *\n * Architecture informed by oh-my-pi (GPL-3.0) and Claude Code's file-history;\n * independent implementation. No ANSI: all UI is native (ctx.ui.* + theme).\n */\nimport path from \"node:path\";\n\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../core/extensions/types.ts\";\n\nimport { firstUserEntryIdAfter } from \"./anchor.ts\";\nimport { loadRewindConfig, type RewindConfig, reloadRewindConfig } from \"./config.ts\";\nimport {\n\tbeginTurn,\n\tcapSnapshots,\n\tdisposeSession,\n\tendTurn,\n\tgetSnapshots,\n\tmigrateBackupsFromSession,\n\tregisterSession,\n\trestoreStateFromSnapshots,\n\ttrackEdit,\n} from \"./engine.ts\";\nimport { runGc, sessionIdFromFile } from \"./gc.ts\";\nimport { runRewindMenu } from \"./menu.ts\";\nimport { rewindBackupsRoot, sessionsDirectory } from \"./paths.ts\";\nimport {\n\ttype EntryTreeView,\n\trestoreToSnapshot,\n\tsnapshotChangeDiffStats,\n\tsnapshotChangedPaths,\n\tsnapshotForEntry,\n} from \"./restore.ts\";\nimport { type FileHistorySnapshot, isSnapshot, SNAPSHOT_ENTRY_TYPE } from \"./snapshot.ts\";\nimport { configureStorage } from \"./storage.ts\";\nimport { truncateText } from \"./text.ts\";\nimport { editWriteTargetPath, resolveToolPath } from \"./tool-path.ts\";\n\n// Bind the engine/gc storage roots to the real on-disk locations (they avoid\n// importing paths.ts directly so they stay node-testable). Safe to call at load.\nconfigureStorage({ backupsRoot: rewindBackupsRoot(), sessionsRoot: sessionsDirectory() });\n\n// Global (one config.json). Reloaded from disk at session_start; /rewind menu\n// saveRewindConfig updates the in-memory cache so the next turn sees changes\n// without re-reading the file every before_agent_start.\nlet config: RewindConfig = loadRewindConfig();\n\n/** /tree confirm → apply payload (changedPaths skips a second originChanged pass). */\ninterface PendingTreeRestore {\n\tsnapshot: FileHistorySnapshot;\n\tchangedPaths: string[];\n}\n\n// Per-session transient state held by the integration layer.\nconst pendingPrompt = new Map<string, string>(); // turn prompt, captured for the snapshot label\nconst pendingTreeRestore = new Map<string, PendingTreeRestore | null>(); // /tree sync intent\n// Leaf id before the current run started (null = branch root). agent_settled\n// scans forward from here for the run's FIRST user entry — the frame's anchor.\nconst anchorScanStart = new Map<string, string | null>();\n\n// ---- helpers --------------------------------------------------------------\n\n/** Record the current leaf as the scan start for the next frame's anchor. */\nfunction markAnchorScanStart(sid: string, ctx: ExtensionContext): void {\n\tanchorScanStart.set(sid, ctx.sessionManager.getLeafId() ?? null);\n}\n\n/** Adapt the session manager to restore.ts's tree view (turn-anchor = navigateTree's \"leaf = parent\" entries). */\nfunction sessionTreeView(ctx: ExtensionContext): EntryTreeView {\n\treturn {\n\t\tgetEntry(id) {\n\t\t\tconst e = ctx.sessionManager.getEntry(id);\n\t\t\tif (!e) return undefined;\n\t\t\tconst isTurnAnchor =\n\t\t\t\t(e.type === \"message\" && (e.message as AgentMessage).role === \"user\") || e.type === \"custom_message\";\n\t\t\treturn { id: e.id, parentId: e.parentId, isTurnAnchor };\n\t\t},\n\t};\n}\n\n/** Rebuild the snapshot list for a session from its persisted custom entries. */\nfunction rebuildSnapshots(ctx: ExtensionContext): FileHistorySnapshot[] {\n\tconst out: FileHistorySnapshot[] = [];\n\tfor (const entry of ctx.sessionManager.getEntries()) {\n\t\tif (entry.type === \"custom\" && entry.customType === SNAPSHOT_ENTRY_TYPE && isSnapshot(entry.data)) {\n\t\t\tout.push(entry.data);\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Max files listed in the /tree restore confirmation before \"+N more\". */\nconst RESTORE_PREVIEW_LIMIT = 8;\n/** Max characters per previewed path (leading-truncated: the filename matters most). */\nconst RESTORE_PREVIEW_PATH_MAX = 64;\n\n/**\n * Bounded cwd-relative file list shown under the /tree restore question, so an\n * irreversible work-tree rewrite is confirmed against WHICH files, not just a\n * count. Multi-line select titles are upstream-sanctioned (ui.confirm joins\n * title + message with \\n through the same selector).\n */\nfunction formatRestorePreview(changedPaths: string[], cwd: string): string {\n\tconst lines = changedPaths.slice(0, RESTORE_PREVIEW_LIMIT).map((p) => {\n\t\tconst rel = path.relative(cwd, p);\n\t\tconst display = rel && !rel.startsWith(\"..\") && !path.isAbsolute(rel) ? rel : p;\n\t\tconst bounded =\n\t\t\tdisplay.length > RESTORE_PREVIEW_PATH_MAX ? `…${display.slice(-(RESTORE_PREVIEW_PATH_MAX - 1))}` : display;\n\t\treturn ` ${bounded}`;\n\t});\n\tif (changedPaths.length > RESTORE_PREVIEW_LIMIT) {\n\t\tlines.push(` … +${changedPaths.length - RESTORE_PREVIEW_LIMIT} more`);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\n// ---- extension entry ------------------------------------------------------\n\nexport default function rewind(pi: ExtensionAPI): void {\n\t// session_start: rebuild the index, migrate fork/resume blobs, reclaim storage.\n\tpi.on(\"session_start\", async (event, ctx) => {\n\t\tconfig = reloadRewindConfig();\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tregisterSession(sid, ctx.cwd);\n\n\t\tconst snapshots = rebuildSnapshots(ctx);\n\n\t\t// Migrate BEFORE rebuilding in-memory state: restoreStateFromSnapshots\n\t\t// prunes over-cap blobs from THIS session's directory (fire-and-forget),\n\t\t// and linking only the retained frames first keeps the two steps disjoint\n\t\t// — no race, and dropped frames' blobs are never linked at all.\n\t\tif ((event.reason === \"resume\" || event.reason === \"fork\") && event.previousSessionFile) {\n\t\t\tconst prevSid = sessionIdFromFile(event.previousSessionFile);\n\t\t\tif (prevSid) {\n\t\t\t\ttry {\n\t\t\t\t\tawait migrateBackupsFromSession(prevSid, sid, capSnapshots(snapshots, config.maxSnapshots));\n\t\t\t\t} catch {\n\t\t\t\t\t// best-effort; a missing blob just means that version can't restore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trestoreStateFromSnapshots(sid, ctx.cwd, snapshots, config.maxSnapshots);\n\t\tmarkAnchorScanStart(sid, ctx);\n\n\t\ttry {\n\t\t\trunGc(config.retentionDays, sid);\n\t\t} catch {\n\t\t\t// GC is best-effort; never block startup\n\t\t}\n\t});\n\n\t// tool_call: back up the target file BEFORE the edit/write lands. Synchronous\n\t// so the backup is on disk before the hook returns control to the agent loop.\n\tpi.on(\"tool_call\", (event, ctx) => {\n\t\tif (event.toolName !== \"edit\" && event.toolName !== \"write\") return;\n\t\tif (!config.enabled) return;\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tconst rawPath = editWriteTargetPath(event.input);\n\t\tif (!rawPath) return;\n\t\tconst abs = resolveToolPath(rawPath, ctx.cwd);\n\t\ttry {\n\t\t\ttrackEdit(sid, abs);\n\t\t} catch {\n\t\t\t// never block the edit on a backup failure\n\t\t}\n\t});\n\n\t// before_agent_start: open the turn's snapshot frame.\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\t// Memory-cached; session_start reloads disk, /rewind save updates cache.\n\t\tconfig = loadRewindConfig();\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// The run's user entry is appended after this hook; the frame's anchor is\n\t\t// the first user entry that appears past this leaf.\n\t\tmarkAnchorScanStart(sid, ctx);\n\t\tif (!config.enabled) return;\n\t\tpendingPrompt.set(sid, event.prompt ?? \"\");\n\t\ttry {\n\t\t\tawait beginTurn(sid);\n\t\t} catch {\n\t\t\t// non-fatal\n\t\t}\n\t});\n\n\t// agent_settled: finalize + persist after Pi will not auto-continue (retry /\n\t// compact-retry / follow-up). agent_end alone is too early for that.\n\tpi.on(\"agent_settled\", async (_event, ctx) => {\n\t\tif (!config.enabled) return;\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// Anchor = the run's FIRST appended user entry (whose start the frame\n\t\t// recorded). Steering/follow-up messages consumed in the same run append\n\t\t// later user entries and must not steal the anchor. Unresolvable (scan\n\t\t// start missing / off-branch / no user entry appended) -> endTurn discards.\n\t\tconst scanStart = anchorScanStart.get(sid);\n\t\tconst userEntryId =\n\t\t\tscanStart === undefined ? \"\" : (firstUserEntryIdAfter(ctx.sessionManager.getBranch(), scanStart) ?? \"\");\n\t\tconst turnId = ctx.sessionManager.getLeafId() ?? userEntryId;\n\t\tconst prompt = truncateText(pendingPrompt.get(sid) ?? \"\", 120, { collapseWhitespace: true });\n\t\tpendingPrompt.delete(sid);\n\t\tconst frame = endTurn(sid, userEntryId, turnId, prompt, new Date().toISOString(), config.maxSnapshots);\n\t\tif (frame) {\n\t\t\tpi.appendEntry(SNAPSHOT_ENTRY_TYPE, frame);\n\t\t}\n\t\t// Next run may start without before_agent_start (custom-triggered): scan\n\t\t// from the settled leaf (past this run's entries + the snapshot entry).\n\t\tmarkAnchorScanStart(sid, ctx);\n\t});\n\n\t// session_before_tree: offer to sync files when navigating to a changed point.\n\tpi.on(\"session_before_tree\", async (event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tpendingTreeRestore.set(sid, null);\n\t\tif (!config.enabled) return;\n\n\t\tconst target = snapshotForEntry(getSnapshots(sid), sessionTreeView(ctx), event.preparation.targetId);\n\t\tif (!target) return;\n\t\tconst changed = await snapshotChangedPaths(sid, target);\n\t\tif (changed.length === 0) return; // silent nav, like native /tree\n\t\t// Lifecycle handler: silent-return without UI. (hasUI is the guard for\n\t\t// ctx.ui.*; checking ctx.mode here would wrongly proceed when a TUI\n\t\t// session has no usable UI.)\n\t\tif (!ctx.hasUI) return;\n\n\t\tconst n = changed.length;\n\t\tlet lineStats = \"\";\n\t\ttry {\n\t\t\tconst stats = await snapshotChangeDiffStats(sid, target, changed);\n\t\t\tif (stats.insertions > 0 || stats.deletions > 0) {\n\t\t\t\tlineStats = ` (+${stats.insertions} / −${stats.deletions})`;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Coarse stats are best-effort; still offer path preview.\n\t\t}\n\t\tconst choice = await ctx.ui.select(\n\t\t\t`Restore ${n} file${n === 1 ? \"\" : \"s\"} to this point?${lineStats}\\n${formatRestorePreview(changed, ctx.cwd)}\\n (covers edit/write changes only; files changed via bash are not tracked)`,\n\t\t\t[\"Yes, restore files\", \"No, conversation only\"],\n\t\t);\n\t\tif (choice?.startsWith(\"Yes\")) {\n\t\t\t// Cache paths so session_tree can apply without a second full compare.\n\t\t\tpendingTreeRestore.set(sid, { snapshot: target, changedPaths: changed });\n\t\t}\n\t});\n\n\t// session_tree: execute any sync intent recorded above.\n\tpi.on(\"session_tree\", async (_event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\t// The leaf moved; the old scan start may now be off-branch. Re-mark so a\n\t\t// custom-triggered run after navigation still anchors correctly.\n\t\tmarkAnchorScanStart(sid, ctx);\n\t\tconst pending = pendingTreeRestore.get(sid);\n\t\tpendingTreeRestore.set(sid, null);\n\t\tif (!pending) return;\n\t\ttry {\n\t\t\tconst changed = await restoreToSnapshot(sid, pending.snapshot, {\n\t\t\t\tonlyPaths: new Set(pending.changedPaths),\n\t\t\t});\n\t\t\tif (changed.length > 0) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Restored ${changed.length} file${changed.length === 1 ? \"\" : \"s\"} to this checkpoint.`,\n\t\t\t\t\t\"info\",\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tctx.ui.notify(`Rewind restore failed: ${String(e)}`, \"warning\");\n\t\t}\n\t});\n\n\t// session_shutdown: drop this session's in-memory state.\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tconst sid = ctx.sessionManager.getSessionId();\n\t\tif (!sid) return;\n\t\tdisposeSession(sid);\n\t\tpendingTreeRestore.delete(sid);\n\t\tpendingPrompt.delete(sid);\n\t\tanchorScanStart.delete(sid);\n\t});\n\n\t// /rewind: settings + storage menu (time-travel itself is via /tree).\n\tpi.registerCommand(\"rewind\", {\n\t\tdescription: \"Rewind settings and backup storage (restore is via /tree)\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tawait runRewindMenu(ctx);\n\t\t},\n\t});\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/menu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAW9E,wBAAsB,aAAa,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B/E","sourcesContent":["/**\n * menu.ts — the `/rewind` settings menu.\n *\n * Per the design, /rewind no longer performs the time-travel itself — restoring\n * files is fused into /tree navigation (see index.ts session_before_tree/\n * session_tree). /rewind is purely a settings + storage console:\n * - toggle the master switch\n * - set the auto-clean retention window\n * - inspect and prune backup storage (aged / orphaned / all-but-current)\n */\nimport type { ExtensionCommandContext } from \"../../core/extensions/types.ts\";\n\nimport { loadRewindConfig, saveRewindConfig } from \"./config.ts\";\nimport { listSessions, removeSession, runGc } from \"./gc.ts\";\nimport { formatSize } from \"./text.ts\";\n\nconst RETENTION_PRESETS = [7, 14, 30, 60, 90];\n\n/** Backup dirs written to this recently likely belong to a live session in another Pi process. */\nconst RECENT_ACTIVE_MS = 10 * 60 * 1000;\n\nexport async function runRewindMenu(ctx: ExtensionCommandContext): Promise<void> {\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(\"/rewind requires an interactive UI.\", \"warning\");\n\t\treturn;\n\t}\n\tconst sid = ctx.sessionManager.getSessionId() ?? undefined;\n\n\tfor (;;) {\n\t\tconst cfg = loadRewindConfig();\n\t\tconst sessions = listSessions(sid);\n\t\tconst total = sessions.reduce((n, s) => n + s.bytes, 0);\n\n\t\tconst enabledLabel = `Rewind: ${cfg.enabled ? \"on\" : \"off\"}`;\n\t\tconst retentionLabel = `Auto-clean backups: ${cfg.retentionDays > 0 ? `after ${cfg.retentionDays} days` : \"never (keep forever)\"}`;\n\t\tconst storageLabel = `Storage: ${formatSize(total)} across ${sessions.length} session${sessions.length === 1 ? \"\" : \"s\"}`;\n\n\t\tconst pick = await ctx.ui.select(\"Rewind settings (Esc to close)\", [enabledLabel, retentionLabel, storageLabel]);\n\t\tif (!pick) return;\n\n\t\tif (pick === enabledLabel) {\n\t\t\tsaveRewindConfig({ ...cfg, enabled: !cfg.enabled });\n\t\t\tctx.ui.notify(`Rewind ${cfg.enabled ? \"disabled\" : \"enabled\"}.`, \"info\");\n\t\t} else if (pick === retentionLabel) {\n\t\t\tawait pickRetention(ctx);\n\t\t} else if (pick === storageLabel) {\n\t\t\tawait storageMenu(ctx, sid);\n\t\t}\n\t}\n}\n\nasync function pickRetention(ctx: ExtensionCommandContext): Promise<void> {\n\tconst options = [...RETENTION_PRESETS.map((d) => `${d} days`), \"Keep forever\", \"Custom...\"];\n\tconst pick = await ctx.ui.select(\"Auto-clean backups after\", options);\n\tif (!pick) return;\n\n\tlet days: number;\n\tif (pick === \"Keep forever\") {\n\t\tdays = 0;\n\t} else if (pick === \"Custom...\") {\n\t\tconst value = await ctx.ui.input(\"Days to keep backups (0 = forever)\");\n\t\tif (value === undefined) return;\n\t\tconst n = Number.parseInt(value.trim(), 10);\n\t\tif (!Number.isFinite(n) || n < 0) {\n\t\t\tctx.ui.notify(\"Invalid number of days.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tdays = n;\n\t} else {\n\t\tdays = Number.parseInt(pick, 10);\n\t}\n\n\tconst cfg = loadRewindConfig();\n\tsaveRewindConfig({ ...cfg, retentionDays: days });\n\tctx.ui.notify(days > 0 ? `Backups now kept for ${days} days.` : \"Backups kept forever.\", \"info\");\n}\n\nasync function storageMenu(ctx: ExtensionCommandContext, sid: string | undefined): Promise<void> {\n\tfor (;;) {\n\t\tconst sessions = listSessions(sid);\n\t\tconst total = sessions.reduce((n, s) => n + s.bytes, 0);\n\t\tconst orphans = sessions.filter((s) => s.orphan);\n\t\t// Skip recently-written dirs: they may belong to another running Pi\n\t\t// process whose in-memory index still references those blobs.\n\t\tconst now = Date.now();\n\t\tconst others = sessions.filter((s) => s.sessionId !== sid && now - s.mtimeMs > RECENT_ACTIVE_MS);\n\n\t\tconst cleanLabel = \"Clean now (aged + orphaned)\";\n\t\tconst orphanLabel = `Remove orphaned (${orphans.length})`;\n\t\tconst allLabel = `Remove all except current (${others.length})`;\n\n\t\tconst pick = await ctx.ui.select(\n\t\t\t`Rewind storage — ${formatSize(total)}, ${sessions.length} session${sessions.length === 1 ? \"\" : \"s\"} (Esc to close)`,\n\t\t\t[cleanLabel, orphanLabel, allLabel],\n\t\t);\n\t\tif (!pick) return;\n\n\t\tif (pick === cleanLabel) {\n\t\t\tconst r = runGc(loadRewindConfig().retentionDays, sid);\n\t\t\tctx.ui.notify(\n\t\t\t\tr.removed > 0\n\t\t\t\t\t? `Removed ${r.removed} session(s), reclaimed ${formatSize(r.reclaimedBytes)}.`\n\t\t\t\t\t: \"Nothing to clean.\",\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t} else if (pick === orphanLabel) {\n\t\t\tif (orphans.length === 0) {\n\t\t\t\tctx.ui.notify(\"No orphaned backups.\", \"info\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst ok = await ctx.ui.confirm(\n\t\t\t\t\"Remove orphaned backups?\",\n\t\t\t\t`Deletes backups for ${orphans.length} session(s) with no session file.`,\n\t\t\t);\n\t\t\tif (!ok) continue;\n\t\t\tlet bytes = 0;\n\t\t\tlet removed = 0;\n\t\t\tfor (const s of orphans) {\n\t\t\t\tconst b = removeSession(s.sessionId);\n\t\t\t\tif (b !== null) {\n\t\t\t\t\tbytes += b;\n\t\t\t\t\tremoved++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx.ui.notify(`Removed ${removed} orphaned session(s), reclaimed ${formatSize(bytes)}.`, \"info\");\n\t\t} else if (pick === allLabel) {\n\t\t\tif (others.length === 0) {\n\t\t\t\tctx.ui.notify(\"No other sessions' backups.\", \"info\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst ok = await ctx.ui.confirm(\n\t\t\t\t\"Remove all other backups?\",\n\t\t\t\t`Deletes backups for ${others.length} session(s). Keeps the current session and any written to in the last 10 minutes (possibly running elsewhere).`,\n\t\t\t);\n\t\t\tif (!ok) continue;\n\t\t\tlet bytes = 0;\n\t\t\tlet removed = 0;\n\t\t\tfor (const s of others) {\n\t\t\t\tconst b = removeSession(s.sessionId);\n\t\t\t\tif (b !== null) {\n\t\t\t\t\tbytes += b;\n\t\t\t\t\tremoved++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx.ui.notify(`Removed ${removed} session(s), reclaimed ${formatSize(bytes)}.`, \"info\");\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/menu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAW9E,wBAAsB,aAAa,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4B/E"}
@@ -1 +1 @@
1
- {"version":3,"file":"menu.js","sourceRoot":"","sources":["../../../src/extensions/rewind/menu.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * menu.ts — the `/rewind` settings menu.\n *\n * Per the design, /rewind no longer performs the time-travel itself — restoring\n * files is fused into /tree navigation (see index.ts session_before_tree/\n * session_tree). /rewind is purely a settings + storage console:\n * - toggle the master switch\n * - set the auto-clean retention window\n * - inspect and prune backup storage (aged / orphaned / all-but-current)\n */\nimport type { ExtensionCommandContext } from \"../../core/extensions/types.ts\";\n\nimport { loadRewindConfig, saveRewindConfig } from \"./config.ts\";\nimport { listSessions, removeSession, runGc } from \"./gc.ts\";\nimport { formatSize } from \"./text.ts\";\n\nconst RETENTION_PRESETS = [7, 14, 30, 60, 90];\n\n/** Backup dirs written to this recently likely belong to a live session in another Pi process. */\nconst RECENT_ACTIVE_MS = 10 * 60 * 1000;\n\nexport async function runRewindMenu(ctx: ExtensionCommandContext): Promise<void> {\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(\"/rewind requires an interactive UI.\", \"warning\");\n\t\treturn;\n\t}\n\tconst sid = ctx.sessionManager.getSessionId() ?? undefined;\n\n\tfor (;;) {\n\t\tconst cfg = loadRewindConfig();\n\t\tconst sessions = listSessions(sid);\n\t\tconst total = sessions.reduce((n, s) => n + s.bytes, 0);\n\n\t\tconst enabledLabel = `Rewind: ${cfg.enabled ? \"on\" : \"off\"}`;\n\t\tconst retentionLabel = `Auto-clean backups: ${cfg.retentionDays > 0 ? `after ${cfg.retentionDays} days` : \"never (keep forever)\"}`;\n\t\tconst storageLabel = `Storage: ${formatSize(total)} across ${sessions.length} session${sessions.length === 1 ? \"\" : \"s\"}`;\n\n\t\tconst pick = await ctx.ui.select(\"Rewind settings (Esc to close)\", [enabledLabel, retentionLabel, storageLabel]);\n\t\tif (!pick) return;\n\n\t\tif (pick === enabledLabel) {\n\t\t\tsaveRewindConfig({ ...cfg, enabled: !cfg.enabled });\n\t\t\tctx.ui.notify(`Rewind ${cfg.enabled ? \"disabled\" : \"enabled\"}.`, \"info\");\n\t\t} else if (pick === retentionLabel) {\n\t\t\tawait pickRetention(ctx);\n\t\t} else if (pick === storageLabel) {\n\t\t\tawait storageMenu(ctx, sid);\n\t\t}\n\t}\n}\n\nasync function pickRetention(ctx: ExtensionCommandContext): Promise<void> {\n\tconst options = [...RETENTION_PRESETS.map((d) => `${d} days`), \"Keep forever\", \"Custom...\"];\n\tconst pick = await ctx.ui.select(\"Auto-clean backups after\", options);\n\tif (!pick) return;\n\n\tlet days: number;\n\tif (pick === \"Keep forever\") {\n\t\tdays = 0;\n\t} else if (pick === \"Custom...\") {\n\t\tconst value = await ctx.ui.input(\"Days to keep backups (0 = forever)\");\n\t\tif (value === undefined) return;\n\t\tconst n = Number.parseInt(value.trim(), 10);\n\t\tif (!Number.isFinite(n) || n < 0) {\n\t\t\tctx.ui.notify(\"Invalid number of days.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tdays = n;\n\t} else {\n\t\tdays = Number.parseInt(pick, 10);\n\t}\n\n\tconst cfg = loadRewindConfig();\n\tsaveRewindConfig({ ...cfg, retentionDays: days });\n\tctx.ui.notify(days > 0 ? `Backups now kept for ${days} days.` : \"Backups kept forever.\", \"info\");\n}\n\nasync function storageMenu(ctx: ExtensionCommandContext, sid: string | undefined): Promise<void> {\n\tfor (;;) {\n\t\tconst sessions = listSessions(sid);\n\t\tconst total = sessions.reduce((n, s) => n + s.bytes, 0);\n\t\tconst orphans = sessions.filter((s) => s.orphan);\n\t\t// Skip recently-written dirs: they may belong to another running Pi\n\t\t// process whose in-memory index still references those blobs.\n\t\tconst now = Date.now();\n\t\tconst others = sessions.filter((s) => s.sessionId !== sid && now - s.mtimeMs > RECENT_ACTIVE_MS);\n\n\t\tconst cleanLabel = \"Clean now (aged + orphaned)\";\n\t\tconst orphanLabel = `Remove orphaned (${orphans.length})`;\n\t\tconst allLabel = `Remove all except current (${others.length})`;\n\n\t\tconst pick = await ctx.ui.select(\n\t\t\t`Rewind storage — ${formatSize(total)}, ${sessions.length} session${sessions.length === 1 ? \"\" : \"s\"} (Esc to close)`,\n\t\t\t[cleanLabel, orphanLabel, allLabel],\n\t\t);\n\t\tif (!pick) return;\n\n\t\tif (pick === cleanLabel) {\n\t\t\tconst r = runGc(loadRewindConfig().retentionDays, sid);\n\t\t\tctx.ui.notify(\n\t\t\t\tr.removed > 0\n\t\t\t\t\t? `Removed ${r.removed} session(s), reclaimed ${formatSize(r.reclaimedBytes)}.`\n\t\t\t\t\t: \"Nothing to clean.\",\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t} else if (pick === orphanLabel) {\n\t\t\tif (orphans.length === 0) {\n\t\t\t\tctx.ui.notify(\"No orphaned backups.\", \"info\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst ok = await ctx.ui.confirm(\n\t\t\t\t\"Remove orphaned backups?\",\n\t\t\t\t`Deletes backups for ${orphans.length} session(s) with no session file.`,\n\t\t\t);\n\t\t\tif (!ok) continue;\n\t\t\tlet bytes = 0;\n\t\t\tlet removed = 0;\n\t\t\tfor (const s of orphans) {\n\t\t\t\tconst b = removeSession(s.sessionId);\n\t\t\t\tif (b !== null) {\n\t\t\t\t\tbytes += b;\n\t\t\t\t\tremoved++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx.ui.notify(`Removed ${removed} orphaned session(s), reclaimed ${formatSize(bytes)}.`, \"info\");\n\t\t} else if (pick === allLabel) {\n\t\t\tif (others.length === 0) {\n\t\t\t\tctx.ui.notify(\"No other sessions' backups.\", \"info\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst ok = await ctx.ui.confirm(\n\t\t\t\t\"Remove all other backups?\",\n\t\t\t\t`Deletes backups for ${others.length} session(s). Keeps the current session and any written to in the last 10 minutes (possibly running elsewhere).`,\n\t\t\t);\n\t\t\tif (!ok) continue;\n\t\t\tlet bytes = 0;\n\t\t\tlet removed = 0;\n\t\t\tfor (const s of others) {\n\t\t\t\tconst b = removeSession(s.sessionId);\n\t\t\t\tif (b !== null) {\n\t\t\t\t\tbytes += b;\n\t\t\t\t\tremoved++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx.ui.notify(`Removed ${removed} session(s), reclaimed ${formatSize(bytes)}.`, \"info\");\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"menu.js","sourceRoot":"","sources":["../../../src/extensions/rewind/menu.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * menu.ts — the `/rewind` settings menu.\n *\n * Per the design, /rewind no longer performs the time-travel itself — restoring\n * files is fused into /tree navigation (see index.ts session_before_tree/\n * session_tree). /rewind is purely a settings + storage console:\n * - toggle the master switch\n * - set the auto-clean retention window\n * - inspect and prune backup storage (aged / orphaned / all-but-current)\n */\nimport type { ExtensionCommandContext } from \"../../core/extensions/types.ts\";\n\nimport { loadRewindConfig, saveRewindConfig } from \"./config.ts\";\nimport { listSessions, removeSession, runGc } from \"./gc.ts\";\nimport { formatSize } from \"./text.ts\";\n\nconst RETENTION_PRESETS = [7, 14, 30, 60, 90];\n\n/** Backup dirs written to this recently likely belong to a live session in another Pi process. */\nconst RECENT_ACTIVE_MS = 10 * 60 * 1000;\n\nexport async function runRewindMenu(ctx: ExtensionCommandContext): Promise<void> {\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(\"/rewind requires an interactive UI.\", \"warning\");\n\t\treturn;\n\t}\n\tconst sid = ctx.sessionManager.getSessionId() ?? undefined;\n\n\tfor (;;) {\n\t\tconst cfg = loadRewindConfig();\n\t\tconst sessions = listSessions(sid);\n\t\tconst total = sessions.reduce((n, s) => n + s.bytes, 0);\n\n\t\tconst enabledLabel = `Rewind: ${cfg.enabled ? \"on\" : \"off\"}`;\n\t\tconst retentionLabel = `Auto-clean backups: ${cfg.retentionDays > 0 ? `after ${cfg.retentionDays} days` : \"never (keep forever)\"}`;\n\t\tconst storageLabel = `Storage: ${formatSize(total)} across ${sessions.length} session${sessions.length === 1 ? \"\" : \"s\"}`;\n\n\t\tconst pick = await ctx.ui.select(\"Rewind settings (Esc to close)\", [enabledLabel, retentionLabel, storageLabel]);\n\t\tif (!pick) return;\n\n\t\tif (pick === enabledLabel) {\n\t\t\tsaveRewindConfig({ ...cfg, enabled: !cfg.enabled });\n\t\t\tctx.ui.notify(`Rewind ${cfg.enabled ? \"disabled\" : \"enabled\"}.`, \"info\");\n\t\t} else if (pick === retentionLabel) {\n\t\t\tawait pickRetention(ctx);\n\t\t} else if (pick === storageLabel) {\n\t\t\tawait storageMenu(ctx, sid);\n\t\t}\n\t}\n}\n\nasync function pickRetention(ctx: ExtensionCommandContext): Promise<void> {\n\tconst options = [...RETENTION_PRESETS.map((d) => `${d} days`), \"Keep forever\", \"Custom...\"];\n\tconst pick = await ctx.ui.select(\"Auto-clean backups after\", options);\n\tif (!pick) return;\n\n\tlet days: number;\n\tif (pick === \"Keep forever\") {\n\t\tdays = 0;\n\t} else if (pick === \"Custom...\") {\n\t\tconst value = await ctx.ui.input(\"Days to keep backups (0 = forever)\");\n\t\tif (value === undefined) return;\n\t\tconst n = Number.parseInt(value.trim(), 10);\n\t\tif (!Number.isFinite(n) || n < 0) {\n\t\t\tctx.ui.notify(\"Invalid number of days.\", \"warning\");\n\t\t\treturn;\n\t\t}\n\t\tdays = n;\n\t} else {\n\t\tdays = Number.parseInt(pick, 10);\n\t}\n\n\tconst cfg = loadRewindConfig();\n\tsaveRewindConfig({ ...cfg, retentionDays: days });\n\tctx.ui.notify(days > 0 ? `Backups now kept for ${days} days.` : \"Backups kept forever.\", \"info\");\n}\n\nasync function storageMenu(ctx: ExtensionCommandContext, sid: string | undefined): Promise<void> {\n\tfor (;;) {\n\t\tconst sessions = listSessions(sid);\n\t\tconst total = sessions.reduce((n, s) => n + s.bytes, 0);\n\t\tconst orphans = sessions.filter((s) => s.orphan);\n\t\t// Skip recently-written dirs: they may belong to another running Pi\n\t\t// process whose in-memory index still references those blobs.\n\t\tconst now = Date.now();\n\t\tconst others = sessions.filter((s) => s.sessionId !== sid && now - s.mtimeMs > RECENT_ACTIVE_MS);\n\n\t\tconst cleanLabel = \"Clean now (aged + orphaned)\";\n\t\tconst orphanLabel = `Remove orphaned (${orphans.length})`;\n\t\tconst allLabel = `Remove all except current (${others.length})`;\n\n\t\tconst pick = await ctx.ui.select(\n\t\t\t`Rewind storage — ${formatSize(total)}, ${sessions.length} session${sessions.length === 1 ? \"\" : \"s\"} (Esc to close)`,\n\t\t\t[cleanLabel, orphanLabel, allLabel],\n\t\t);\n\t\tif (!pick) return;\n\n\t\tif (pick === cleanLabel) {\n\t\t\tconst r = runGc(loadRewindConfig().retentionDays, sid);\n\t\t\tctx.ui.notify(\n\t\t\t\tr.removed > 0\n\t\t\t\t\t? `Removed ${r.removed} session(s), reclaimed ${formatSize(r.reclaimedBytes)}.`\n\t\t\t\t\t: \"Nothing to clean.\",\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t} else if (pick === orphanLabel) {\n\t\t\tif (orphans.length === 0) {\n\t\t\t\tctx.ui.notify(\"No orphaned backups.\", \"info\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst ok = await ctx.ui.confirm(\n\t\t\t\t\"Remove orphaned backups?\",\n\t\t\t\t`Deletes backups for ${orphans.length} session(s) with no session file.`,\n\t\t\t);\n\t\t\tif (!ok) continue;\n\t\t\tlet bytes = 0;\n\t\t\tlet removed = 0;\n\t\t\tfor (const s of orphans) {\n\t\t\t\tconst b = removeSession(s.sessionId);\n\t\t\t\tif (b !== null) {\n\t\t\t\t\tbytes += b;\n\t\t\t\t\tremoved++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx.ui.notify(`Removed ${removed} orphaned session(s), reclaimed ${formatSize(bytes)}.`, \"info\");\n\t\t} else if (pick === allLabel) {\n\t\t\tif (others.length === 0) {\n\t\t\t\tctx.ui.notify(\"No other sessions' backups.\", \"info\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst ok = await ctx.ui.confirm(\n\t\t\t\t\"Remove all other backups?\",\n\t\t\t\t`Deletes backups for ${others.length} session(s). Keeps the current session and any written to in the last 10 minutes (possibly running elsewhere).`,\n\t\t\t);\n\t\t\tif (!ok) continue;\n\t\t\tlet bytes = 0;\n\t\t\tlet removed = 0;\n\t\t\tfor (const s of others) {\n\t\t\t\tconst b = removeSession(s.sessionId);\n\t\t\t\tif (b !== null) {\n\t\t\t\t\tbytes += b;\n\t\t\t\t\tremoved++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tctx.ui.notify(`Removed ${removed} session(s), reclaimed ${formatSize(bytes)}.`, \"info\");\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/paths.ts"],"names":[],"mappings":"AAQA,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C","sourcesContent":["import { join } from \"node:path\";\n\nimport { getAgentDir } from \"../../config.ts\";\n\nfunction rewindDirectory(): string {\n\treturn join(getAgentDir(), \"rewind\");\n}\n\nexport function rewindConfigPath(): string {\n\treturn join(rewindDirectory(), \"config.json\");\n}\n\nexport function rewindBackupsRoot(): string {\n\treturn join(rewindDirectory(), \"backups\");\n}\n\nexport function sessionsDirectory(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n"]}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/paths.ts"],"names":[],"mappings":"AAQA,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/extensions/rewind/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,SAAS,eAAe,GAAW;IAClC,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,CAAC;AAAA,CACrC;AAED,MAAM,UAAU,gBAAgB,GAAW;IAC1C,OAAO,IAAI,CAAC,eAAe,EAAE,EAAE,aAAa,CAAC,CAAC;AAAA,CAC9C;AAED,MAAM,UAAU,iBAAiB,GAAW;IAC3C,OAAO,IAAI,CAAC,eAAe,EAAE,EAAE,SAAS,CAAC,CAAC;AAAA,CAC1C;AAED,MAAM,UAAU,iBAAiB,GAAW;IAC3C,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,CAAC;AAAA,CACvC","sourcesContent":["import { join } from \"node:path\";\n\nimport { getAgentDir } from \"../../config.ts\";\n\nfunction rewindDirectory(): string {\n\treturn join(getAgentDir(), \"rewind\");\n}\n\nexport function rewindConfigPath(): string {\n\treturn join(rewindDirectory(), \"config.json\");\n}\n\nexport function rewindBackupsRoot(): string {\n\treturn join(rewindDirectory(), \"backups\");\n}\n\nexport function sessionsDirectory(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n"]}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/extensions/rewind/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,SAAS,eAAe;IACvB,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC/B,OAAO,IAAI,CAAC,eAAe,EAAE,EAAE,aAAa,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,iBAAiB;IAChC,OAAO,IAAI,CAAC,eAAe,EAAE,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,iBAAiB;IAChC,OAAO,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC","sourcesContent":["import { join } from \"node:path\";\n\nimport { getAgentDir } from \"../../config.ts\";\n\nfunction rewindDirectory(): string {\n\treturn join(getAgentDir(), \"rewind\");\n}\n\nexport function rewindConfigPath(): string {\n\treturn join(rewindDirectory(), \"config.json\");\n}\n\nexport function rewindBackupsRoot(): string {\n\treturn join(rewindDirectory(), \"backups\");\n}\n\nexport function sessionsDirectory(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACN,KAAK,oBAAoB,EAEzB,KAAK,eAAe,EAGpB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,2EAA2E;AAC3E,MAAM,WAAW,SAAS;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qGAAmG;IACnG,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;CAC5C;AAiCD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC/B,SAAS,EAAE,mBAAmB,EAAE,EAChC,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,MAAM,GACd,mBAAmB,GAAG,SAAS,CAkBjC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAExG;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,mBAAmB,EAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,mBAAmB,EAC7B,IAAI,CAAC,EAAE,oBAAoB,GACzB,OAAO,CAAC,MAAM,EAAE,CAAC,CAEnB","sourcesContent":["/**\n * restore.ts — map a /tree navigation target to the snapshot that should be\n * restored and apply it via the engine.\n *\n * Snapshot semantics: a frame anchored at user entry U records the work tree\n * at the moment U's run STARTED (see engine.ts beginTurn). The selection here\n * must mirror AgentSession.navigateTree's leaf rules:\n *\n * - user / custom_message target: navigateTree removes the target's turn\n * from the branch (leaf = parent, text returns to the editor) → restore\n * to the state BEFORE that turn = the snapshot anchored at the target.\n * - any other target: the target's turn STAYS in the conversation\n * (leaf = target) → restore to the state AFTER that turn = the first\n * chronological snapshot anchored at a descendant turn (it recorded the\n * work tree when the next run began). No such snapshot → nothing was\n * recorded after the target, so offer no restore at all — falling back to\n * an ancestor frame would roll back the target turn's own edits and leave\n * the work tree older than the conversation.\n */\nimport {\n\ttype ApplySnapshotOptions,\n\tapplySnapshot,\n\ttype CoarseDiffStats,\n\tcollectChangeDiffStats,\n\tcollectChanges,\n} from \"./engine.ts\";\nimport type { FileHistorySnapshot } from \"./snapshot.ts\";\n\n/** Minimal per-entry view needed to walk the tree and classify targets. */\nexport interface EntryView {\n\tid: string;\n\tparentId: string | null;\n\t/** True for entries navigateTree treats as turn starts (user / custom_message → leaf = parent). */\n\tisTurnAnchor: boolean;\n}\n\n/** Minimal session view we need to walk the entry tree. */\nexport interface EntryTreeView {\n\tgetEntry(id: string): EntryView | undefined;\n}\n\n/** Cycle guard for parent-chain walks (ids are acyclic in practice). */\nconst ANCESTOR_WALK_GUARD = 100_000;\n\n/** True when `id`'s parent chain passes through `ancestorId` (strict descent). */\nfunction isDescendantOf(view: EntryTreeView, id: string, ancestorId: string): boolean {\n\tlet cur = view.getEntry(id)?.parentId ?? null;\n\tlet guard = 0;\n\twhile (cur !== null && guard++ < ANCESTOR_WALK_GUARD) {\n\t\tif (cur === ancestorId) return true;\n\t\tcur = view.getEntry(cur)?.parentId ?? null;\n\t}\n\treturn false;\n}\n\n/**\n * First (chronological) snapshot anchored at a strict descendant of `entryId`\n * — the run that started right after `entryId` existed, so its frame records\n * the work tree state \"just after\" the entry.\n */\nfunction snapshotAfterEntry(\n\tsnapshots: FileHistorySnapshot[],\n\tview: EntryTreeView,\n\tentryId: string,\n): FileHistorySnapshot | undefined {\n\tfor (const snap of snapshots) {\n\t\tif (!snap.userEntryId) continue;\n\t\tif (isDescendantOf(view, snap.userEntryId, entryId)) return snap;\n\t}\n\treturn undefined;\n}\n\n/**\n * The snapshot that matches navigating to `targetId`, per the rules in the\n * module header. Undefined when no recorded state corresponds to the target\n * (then the work tree should be left alone).\n */\nexport function snapshotForEntry(\n\tsnapshots: FileHistorySnapshot[],\n\tview: EntryTreeView,\n\ttargetId: string,\n): FileHistorySnapshot | undefined {\n\tconst target = view.getEntry(targetId);\n\tif (!target) return undefined;\n\n\tif (target.isTurnAnchor) {\n\t\t// Exact anchor match: the frame recording the state right before this\n\t\t// turn ran. Chronologically last wins if duplicate anchors exist.\n\t\tlet exact: FileHistorySnapshot | undefined;\n\t\tfor (const snap of snapshots) {\n\t\t\tif (snap.userEntryId === targetId) exact = snap;\n\t\t}\n\t\tif (exact) return exact;\n\t\t// No frame for this turn (it recorded no changes): the state before it\n\t\t// equals the state after its parent.\n\t\treturn target.parentId === null ? undefined : snapshotAfterEntry(snapshots, view, target.parentId);\n\t}\n\n\treturn snapshotAfterEntry(snapshots, view, targetId);\n}\n\n/**\n * Absolute file paths restoring to this snapshot would change on disk (empty =\n * none). Callers use the length for the count and the paths for the preview.\n */\nexport function snapshotChangedPaths(sessionId: string, snapshot: FileHistorySnapshot): Promise<string[]> {\n\treturn collectChanges(sessionId, snapshot);\n}\n\n/**\n * Coarse +N/−M line stats for the confirm dialog (paths must already be known\n * changed — typically from snapshotChangedPaths).\n */\nexport function snapshotChangeDiffStats(\n\tsessionId: string,\n\tsnapshot: FileHistorySnapshot,\n\tchangedPaths: readonly string[],\n): Promise<CoarseDiffStats> {\n\treturn collectChangeDiffStats(sessionId, snapshot, changedPaths);\n}\n\n/**\n * Restore the work tree to `snapshot` and return changed absolute paths.\n * Pass `onlyPaths` from a prior snapshotChangedPaths call to skip re-compare.\n */\nexport async function restoreToSnapshot(\n\tsessionId: string,\n\tsnapshot: FileHistorySnapshot,\n\topts?: ApplySnapshotOptions,\n): Promise<string[]> {\n\treturn applySnapshot(sessionId, snapshot, opts);\n}\n"]}
1
+ {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACN,KAAK,oBAAoB,EAEzB,KAAK,eAAe,EAGpB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEzD,2EAA2E;AAC3E,MAAM,WAAW,SAAS;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,mGAAmG;IACnG,YAAY,EAAE,OAAO,CAAC;CACtB;AAED,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;CAC5C;AAiCD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC/B,SAAS,EAAE,mBAAmB,EAAE,EAChC,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,MAAM,GACd,mBAAmB,GAAG,SAAS,CAkBjC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAExG;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACtC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,mBAAmB,EAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACtC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,mBAAmB,EAC7B,IAAI,CAAC,EAAE,oBAAoB,GACzB,OAAO,CAAC,MAAM,EAAE,CAAC,CAEnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../src/extensions/rewind/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAEN,aAAa,EAEb,sBAAsB,EACtB,cAAc,GACd,MAAM,aAAa,CAAC;AAgBrB,wEAAwE;AACxE,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAEpC,kFAAkF;AAClF,SAAS,cAAc,CAAC,IAAmB,EAAE,EAAU,EAAE,UAAkB,EAAW;IACrF,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,KAAK,IAAI,IAAI,KAAK,EAAE,GAAG,mBAAmB,EAAE,CAAC;QACtD,IAAI,GAAG,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC;QACpC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC5C,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAC1B,SAAgC,EAChC,IAAmB,EACnB,OAAe,EACmB;IAClC,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,SAAS;QAChC,IAAI,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC/B,SAAgC,EAChC,IAAmB,EACnB,QAAgB,EACkB;IAClC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,sEAAsE;QACtE,kEAAkE;QAClE,IAAI,KAAsC,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,WAAW,KAAK,QAAQ;gBAAE,KAAK,GAAG,IAAI,CAAC;QACjD,CAAC;QACD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,uEAAuE;QACvE,qCAAqC;QACrC,OAAO,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACpG,CAAC;IAED,OAAO,kBAAkB,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AAAA,CACrD;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,QAA6B,EAAqB;IACzG,OAAO,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAAA,CAC3C;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACtC,SAAiB,EACjB,QAA6B,EAC7B,YAA+B,EACJ;IAC3B,OAAO,sBAAsB,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;AAAA,CACjE;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,SAAiB,EACjB,QAA6B,EAC7B,IAA2B,EACP;IACpB,OAAO,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AAAA,CAChD","sourcesContent":["/**\n * restore.ts — map a /tree navigation target to the snapshot that should be\n * restored and apply it via the engine.\n *\n * Snapshot semantics: a frame anchored at user entry U records the work tree\n * at the moment U's run STARTED (see engine.ts beginTurn). The selection here\n * must mirror AgentSession.navigateTree's leaf rules:\n *\n * - user / custom_message target: navigateTree removes the target's turn\n * from the branch (leaf = parent, text returns to the editor) → restore\n * to the state BEFORE that turn = the snapshot anchored at the target.\n * - any other target: the target's turn STAYS in the conversation\n * (leaf = target) → restore to the state AFTER that turn = the first\n * chronological snapshot anchored at a descendant turn (it recorded the\n * work tree when the next run began). No such snapshot → nothing was\n * recorded after the target, so offer no restore at all — falling back to\n * an ancestor frame would roll back the target turn's own edits and leave\n * the work tree older than the conversation.\n */\nimport {\n\ttype ApplySnapshotOptions,\n\tapplySnapshot,\n\ttype CoarseDiffStats,\n\tcollectChangeDiffStats,\n\tcollectChanges,\n} from \"./engine.ts\";\nimport type { FileHistorySnapshot } from \"./snapshot.ts\";\n\n/** Minimal per-entry view needed to walk the tree and classify targets. */\nexport interface EntryView {\n\tid: string;\n\tparentId: string | null;\n\t/** True for entries navigateTree treats as turn starts (user / custom_message → leaf = parent). */\n\tisTurnAnchor: boolean;\n}\n\n/** Minimal session view we need to walk the entry tree. */\nexport interface EntryTreeView {\n\tgetEntry(id: string): EntryView | undefined;\n}\n\n/** Cycle guard for parent-chain walks (ids are acyclic in practice). */\nconst ANCESTOR_WALK_GUARD = 100_000;\n\n/** True when `id`'s parent chain passes through `ancestorId` (strict descent). */\nfunction isDescendantOf(view: EntryTreeView, id: string, ancestorId: string): boolean {\n\tlet cur = view.getEntry(id)?.parentId ?? null;\n\tlet guard = 0;\n\twhile (cur !== null && guard++ < ANCESTOR_WALK_GUARD) {\n\t\tif (cur === ancestorId) return true;\n\t\tcur = view.getEntry(cur)?.parentId ?? null;\n\t}\n\treturn false;\n}\n\n/**\n * First (chronological) snapshot anchored at a strict descendant of `entryId`\n * — the run that started right after `entryId` existed, so its frame records\n * the work tree state \"just after\" the entry.\n */\nfunction snapshotAfterEntry(\n\tsnapshots: FileHistorySnapshot[],\n\tview: EntryTreeView,\n\tentryId: string,\n): FileHistorySnapshot | undefined {\n\tfor (const snap of snapshots) {\n\t\tif (!snap.userEntryId) continue;\n\t\tif (isDescendantOf(view, snap.userEntryId, entryId)) return snap;\n\t}\n\treturn undefined;\n}\n\n/**\n * The snapshot that matches navigating to `targetId`, per the rules in the\n * module header. Undefined when no recorded state corresponds to the target\n * (then the work tree should be left alone).\n */\nexport function snapshotForEntry(\n\tsnapshots: FileHistorySnapshot[],\n\tview: EntryTreeView,\n\ttargetId: string,\n): FileHistorySnapshot | undefined {\n\tconst target = view.getEntry(targetId);\n\tif (!target) return undefined;\n\n\tif (target.isTurnAnchor) {\n\t\t// Exact anchor match: the frame recording the state right before this\n\t\t// turn ran. Chronologically last wins if duplicate anchors exist.\n\t\tlet exact: FileHistorySnapshot | undefined;\n\t\tfor (const snap of snapshots) {\n\t\t\tif (snap.userEntryId === targetId) exact = snap;\n\t\t}\n\t\tif (exact) return exact;\n\t\t// No frame for this turn (it recorded no changes): the state before it\n\t\t// equals the state after its parent.\n\t\treturn target.parentId === null ? undefined : snapshotAfterEntry(snapshots, view, target.parentId);\n\t}\n\n\treturn snapshotAfterEntry(snapshots, view, targetId);\n}\n\n/**\n * Absolute file paths restoring to this snapshot would change on disk (empty =\n * none). Callers use the length for the count and the paths for the preview.\n */\nexport function snapshotChangedPaths(sessionId: string, snapshot: FileHistorySnapshot): Promise<string[]> {\n\treturn collectChanges(sessionId, snapshot);\n}\n\n/**\n * Coarse +N/−M line stats for the confirm dialog (paths must already be known\n * changed — typically from snapshotChangedPaths).\n */\nexport function snapshotChangeDiffStats(\n\tsessionId: string,\n\tsnapshot: FileHistorySnapshot,\n\tchangedPaths: readonly string[],\n): Promise<CoarseDiffStats> {\n\treturn collectChangeDiffStats(sessionId, snapshot, changedPaths);\n}\n\n/**\n * Restore the work tree to `snapshot` and return changed absolute paths.\n * Pass `onlyPaths` from a prior snapshotChangedPaths call to skip re-compare.\n */\nexport async function restoreToSnapshot(\n\tsessionId: string,\n\tsnapshot: FileHistorySnapshot,\n\topts?: ApplySnapshotOptions,\n): Promise<string[]> {\n\treturn applySnapshot(sessionId, snapshot, opts);\n}\n"]}
1
+ {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../src/extensions/rewind/restore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAEN,aAAa,EAEb,sBAAsB,EACtB,cAAc,GACd,MAAM,aAAa,CAAC;AAgBrB,wEAAwE;AACxE,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAEpC,kFAAkF;AAClF,SAAS,cAAc,CAAC,IAAmB,EAAE,EAAU,EAAE,UAAkB;IAC1E,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,GAAG,KAAK,IAAI,IAAI,KAAK,EAAE,GAAG,mBAAmB,EAAE,CAAC;QACtD,IAAI,GAAG,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC;QACpC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC5C,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAC1B,SAAgC,EAChC,IAAmB,EACnB,OAAe;IAEf,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,SAAS;QAChC,IAAI,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IAClE,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC/B,SAAgC,EAChC,IAAmB,EACnB,QAAgB;IAEhB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,sEAAsE;QACtE,kEAAkE;QAClE,IAAI,KAAsC,CAAC;QAC3C,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,WAAW,KAAK,QAAQ;gBAAE,KAAK,GAAG,IAAI,CAAC;QACjD,CAAC;QACD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,uEAAuE;QACvE,qCAAqC;QACrC,OAAO,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACpG,CAAC;IAED,OAAO,kBAAkB,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,QAA6B;IACpF,OAAO,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACtC,SAAiB,EACjB,QAA6B,EAC7B,YAA+B;IAE/B,OAAO,sBAAsB,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;AAClE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,SAAiB,EACjB,QAA6B,EAC7B,IAA2B;IAE3B,OAAO,aAAa,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC","sourcesContent":["/**\n * restore.ts — map a /tree navigation target to the snapshot that should be\n * restored and apply it via the engine.\n *\n * Snapshot semantics: a frame anchored at user entry U records the work tree\n * at the moment U's run STARTED (see engine.ts beginTurn). The selection here\n * must mirror AgentSession.navigateTree's leaf rules:\n *\n * - user / custom_message target: navigateTree removes the target's turn\n * from the branch (leaf = parent, text returns to the editor) → restore\n * to the state BEFORE that turn = the snapshot anchored at the target.\n * - any other target: the target's turn STAYS in the conversation\n * (leaf = target) → restore to the state AFTER that turn = the first\n * chronological snapshot anchored at a descendant turn (it recorded the\n * work tree when the next run began). No such snapshot → nothing was\n * recorded after the target, so offer no restore at all — falling back to\n * an ancestor frame would roll back the target turn's own edits and leave\n * the work tree older than the conversation.\n */\nimport {\n\ttype ApplySnapshotOptions,\n\tapplySnapshot,\n\ttype CoarseDiffStats,\n\tcollectChangeDiffStats,\n\tcollectChanges,\n} from \"./engine.ts\";\nimport type { FileHistorySnapshot } from \"./snapshot.ts\";\n\n/** Minimal per-entry view needed to walk the tree and classify targets. */\nexport interface EntryView {\n\tid: string;\n\tparentId: string | null;\n\t/** True for entries navigateTree treats as turn starts (user / custom_message → leaf = parent). */\n\tisTurnAnchor: boolean;\n}\n\n/** Minimal session view we need to walk the entry tree. */\nexport interface EntryTreeView {\n\tgetEntry(id: string): EntryView | undefined;\n}\n\n/** Cycle guard for parent-chain walks (ids are acyclic in practice). */\nconst ANCESTOR_WALK_GUARD = 100_000;\n\n/** True when `id`'s parent chain passes through `ancestorId` (strict descent). */\nfunction isDescendantOf(view: EntryTreeView, id: string, ancestorId: string): boolean {\n\tlet cur = view.getEntry(id)?.parentId ?? null;\n\tlet guard = 0;\n\twhile (cur !== null && guard++ < ANCESTOR_WALK_GUARD) {\n\t\tif (cur === ancestorId) return true;\n\t\tcur = view.getEntry(cur)?.parentId ?? null;\n\t}\n\treturn false;\n}\n\n/**\n * First (chronological) snapshot anchored at a strict descendant of `entryId`\n * — the run that started right after `entryId` existed, so its frame records\n * the work tree state \"just after\" the entry.\n */\nfunction snapshotAfterEntry(\n\tsnapshots: FileHistorySnapshot[],\n\tview: EntryTreeView,\n\tentryId: string,\n): FileHistorySnapshot | undefined {\n\tfor (const snap of snapshots) {\n\t\tif (!snap.userEntryId) continue;\n\t\tif (isDescendantOf(view, snap.userEntryId, entryId)) return snap;\n\t}\n\treturn undefined;\n}\n\n/**\n * The snapshot that matches navigating to `targetId`, per the rules in the\n * module header. Undefined when no recorded state corresponds to the target\n * (then the work tree should be left alone).\n */\nexport function snapshotForEntry(\n\tsnapshots: FileHistorySnapshot[],\n\tview: EntryTreeView,\n\ttargetId: string,\n): FileHistorySnapshot | undefined {\n\tconst target = view.getEntry(targetId);\n\tif (!target) return undefined;\n\n\tif (target.isTurnAnchor) {\n\t\t// Exact anchor match: the frame recording the state right before this\n\t\t// turn ran. Chronologically last wins if duplicate anchors exist.\n\t\tlet exact: FileHistorySnapshot | undefined;\n\t\tfor (const snap of snapshots) {\n\t\t\tif (snap.userEntryId === targetId) exact = snap;\n\t\t}\n\t\tif (exact) return exact;\n\t\t// No frame for this turn (it recorded no changes): the state before it\n\t\t// equals the state after its parent.\n\t\treturn target.parentId === null ? undefined : snapshotAfterEntry(snapshots, view, target.parentId);\n\t}\n\n\treturn snapshotAfterEntry(snapshots, view, targetId);\n}\n\n/**\n * Absolute file paths restoring to this snapshot would change on disk (empty =\n * none). Callers use the length for the count and the paths for the preview.\n */\nexport function snapshotChangedPaths(sessionId: string, snapshot: FileHistorySnapshot): Promise<string[]> {\n\treturn collectChanges(sessionId, snapshot);\n}\n\n/**\n * Coarse +N/−M line stats for the confirm dialog (paths must already be known\n * changed — typically from snapshotChangedPaths).\n */\nexport function snapshotChangeDiffStats(\n\tsessionId: string,\n\tsnapshot: FileHistorySnapshot,\n\tchangedPaths: readonly string[],\n): Promise<CoarseDiffStats> {\n\treturn collectChangeDiffStats(sessionId, snapshot, changedPaths);\n}\n\n/**\n * Restore the work tree to `snapshot` and return changed absolute paths.\n * Pass `onlyPaths` from a prior snapshotChangedPaths call to skip re-compare.\n */\nexport async function restoreToSnapshot(\n\tsessionId: string,\n\tsnapshot: FileHistorySnapshot,\n\topts?: ApplySnapshotOptions,\n): Promise<string[]> {\n\treturn applySnapshot(sessionId, snapshot, opts);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,+FAA+F;IAC/F,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC,CAAC,EAAE,CAAC,CAAC;IACL,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,4FAA0F;IAC1F,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,mBAAmB,CAMrE","sourcesContent":["/**\n * snapshot.ts — persisted data shapes for the rewind extension's file-history\n * engine. Pure types + tiny helpers; no Pi imports, no side effects, so the\n * engine and its selftests can import them under plain node.\n *\n * A FileHistorySnapshot is written to the session JSONL (via the integration\n * layer's appendEntry, custom type \"pi-rewind-snapshot\"). It is never sent to\n * the LLM and survives reload/compaction. Each snapshot anchors to one turn and\n * records, for every tracked file, which backup blob holds that file's contents\n * as of the start of the turn.\n */\n\n/** Custom session-entry type used to persist snapshots to the session JSONL. */\nexport const SNAPSHOT_ENTRY_TYPE = \"pi-rewind-snapshot\";\n\n/**\n * One file's backup at a specific version.\n * `backupName === null` means the file did not exist at this version — rewinding\n * to a snapshot carrying a null backup deletes the file.\n */\nexport interface FileBackup {\n\t/** Backup blob filename (`<sha256(relpath)[:16]>@v<n>`), or null if the file did not exist. */\n\tbackupName: string | null;\n\t/** Monotonic version for this file within the session (1-based). */\n\tversion: number;\n}\n\n/**\n * One turn's snapshot. `v` lets future readers migrate old entries.\n * `trackedFileBackups` is keyed by the file's path *relative to cwd* when inside\n * cwd (else the absolute path), matching the engine's path shortening.\n */\nexport interface FileHistorySnapshot {\n\tv: 1;\n\t/** Session entry id this snapshot anchors to (used to match /tree navigation). */\n\tuserEntryId: string;\n\t/** Leaf/turn id when finalized (informational). */\n\tturnId: string;\n\t/** The user prompt that drove the turn (truncated; shown in the /tree sync prompt). */\n\tprompt: string;\n\t/** Map of tracking-path → the backup recorded for that file at the start of this turn. */\n\ttrackedFileBackups: Record<string, FileBackup>;\n\t/** ISO timestamp the snapshot was finalized. */\n\ttimestamp: string;\n}\n\n/** Narrow an arbitrary session-entry payload to a FileHistorySnapshot. */\nexport function isSnapshot(data: unknown): data is FileHistorySnapshot {\n\tif (!data || typeof data !== \"object\") return false;\n\tconst s = data as Partial<FileHistorySnapshot>;\n\treturn (\n\t\ttypeof s.userEntryId === \"string\" && typeof s.trackedFileBackups === \"object\" && s.trackedFileBackups !== null\n\t);\n}\n"]}
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gFAAgF;AAChF,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AAExD;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,+FAA+F;IAC/F,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC,CAAC,EAAE,CAAC,CAAC;IACL,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,0FAA0F;IAC1F,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC/C,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,mBAAmB,CAMrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/extensions/rewind/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AAiCxD,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,IAAa,EAA+B;IACtE,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,CAAC,GAAG,IAAoC,CAAC;IAC/C,OAAO,CACN,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,kBAAkB,KAAK,QAAQ,IAAI,CAAC,CAAC,kBAAkB,KAAK,IAAI,CAC9G,CAAC;AAAA,CACF","sourcesContent":["/**\n * snapshot.ts — persisted data shapes for the rewind extension's file-history\n * engine. Pure types + tiny helpers; no Pi imports, no side effects, so the\n * engine and its selftests can import them under plain node.\n *\n * A FileHistorySnapshot is written to the session JSONL (via the integration\n * layer's appendEntry, custom type \"pi-rewind-snapshot\"). It is never sent to\n * the LLM and survives reload/compaction. Each snapshot anchors to one turn and\n * records, for every tracked file, which backup blob holds that file's contents\n * as of the start of the turn.\n */\n\n/** Custom session-entry type used to persist snapshots to the session JSONL. */\nexport const SNAPSHOT_ENTRY_TYPE = \"pi-rewind-snapshot\";\n\n/**\n * One file's backup at a specific version.\n * `backupName === null` means the file did not exist at this version — rewinding\n * to a snapshot carrying a null backup deletes the file.\n */\nexport interface FileBackup {\n\t/** Backup blob filename (`<sha256(relpath)[:16]>@v<n>`), or null if the file did not exist. */\n\tbackupName: string | null;\n\t/** Monotonic version for this file within the session (1-based). */\n\tversion: number;\n}\n\n/**\n * One turn's snapshot. `v` lets future readers migrate old entries.\n * `trackedFileBackups` is keyed by the file's path *relative to cwd* when inside\n * cwd (else the absolute path), matching the engine's path shortening.\n */\nexport interface FileHistorySnapshot {\n\tv: 1;\n\t/** Session entry id this snapshot anchors to (used to match /tree navigation). */\n\tuserEntryId: string;\n\t/** Leaf/turn id when finalized (informational). */\n\tturnId: string;\n\t/** The user prompt that drove the turn (truncated; shown in the /tree sync prompt). */\n\tprompt: string;\n\t/** Map of tracking-path → the backup recorded for that file at the start of this turn. */\n\ttrackedFileBackups: Record<string, FileBackup>;\n\t/** ISO timestamp the snapshot was finalized. */\n\ttimestamp: string;\n}\n\n/** Narrow an arbitrary session-entry payload to a FileHistorySnapshot. */\nexport function isSnapshot(data: unknown): data is FileHistorySnapshot {\n\tif (!data || typeof data !== \"object\") return false;\n\tconst s = data as Partial<FileHistorySnapshot>;\n\treturn (\n\t\ttypeof s.userEntryId === \"string\" && typeof s.trackedFileBackups === \"object\" && s.trackedFileBackups !== null\n\t);\n}\n"]}
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/extensions/rewind/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AAiCxD,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,IAAa;IACvC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,CAAC,GAAG,IAAoC,CAAC;IAC/C,OAAO,CACN,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,kBAAkB,KAAK,QAAQ,IAAI,CAAC,CAAC,kBAAkB,KAAK,IAAI,CAC9G,CAAC;AACH,CAAC","sourcesContent":["/**\n * snapshot.ts — persisted data shapes for the rewind extension's file-history\n * engine. Pure types + tiny helpers; no Pi imports, no side effects, so the\n * engine and its selftests can import them under plain node.\n *\n * A FileHistorySnapshot is written to the session JSONL (via the integration\n * layer's appendEntry, custom type \"pi-rewind-snapshot\"). It is never sent to\n * the LLM and survives reload/compaction. Each snapshot anchors to one turn and\n * records, for every tracked file, which backup blob holds that file's contents\n * as of the start of the turn.\n */\n\n/** Custom session-entry type used to persist snapshots to the session JSONL. */\nexport const SNAPSHOT_ENTRY_TYPE = \"pi-rewind-snapshot\";\n\n/**\n * One file's backup at a specific version.\n * `backupName === null` means the file did not exist at this version — rewinding\n * to a snapshot carrying a null backup deletes the file.\n */\nexport interface FileBackup {\n\t/** Backup blob filename (`<sha256(relpath)[:16]>@v<n>`), or null if the file did not exist. */\n\tbackupName: string | null;\n\t/** Monotonic version for this file within the session (1-based). */\n\tversion: number;\n}\n\n/**\n * One turn's snapshot. `v` lets future readers migrate old entries.\n * `trackedFileBackups` is keyed by the file's path *relative to cwd* when inside\n * cwd (else the absolute path), matching the engine's path shortening.\n */\nexport interface FileHistorySnapshot {\n\tv: 1;\n\t/** Session entry id this snapshot anchors to (used to match /tree navigation). */\n\tuserEntryId: string;\n\t/** Leaf/turn id when finalized (informational). */\n\tturnId: string;\n\t/** The user prompt that drove the turn (truncated; shown in the /tree sync prompt). */\n\tprompt: string;\n\t/** Map of tracking-path → the backup recorded for that file at the start of this turn. */\n\ttrackedFileBackups: Record<string, FileBackup>;\n\t/** ISO timestamp the snapshot was finalized. */\n\ttimestamp: string;\n}\n\n/** Narrow an arbitrary session-entry payload to a FileHistorySnapshot. */\nexport function isSnapshot(data: unknown): data is FileHistorySnapshot {\n\tif (!data || typeof data !== \"object\") return false;\n\tconst s = data as Partial<FileHistorySnapshot>;\n\treturn (\n\t\ttypeof s.userEntryId === \"string\" && typeof s.trackedFileBackups === \"object\" && s.trackedFileBackups !== null\n\t);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/storage.ts"],"names":[],"mappings":"AAiBA,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG1F;AAED,qDAAqD;AACrD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,oEAAoE;AACpE,wBAAgB,eAAe,IAAI,MAAM,CAExC","sourcesContent":["/**\n * storage.ts — injected storage roots for the rewind engine.\n *\n * engine.ts and gc.ts must stay loadable under plain node (for offline\n * selftests), so they cannot statically import paths.ts (which imports\n * getAgentDir from @astralyn/pi — unresolvable outside Pi).\n * Instead they read the roots from here, and the integration layer (index.ts)\n * calls configureStorage() with the real paths at startup. Selftests call it with\n * temp directories.\n *\n * No Pi imports in this module.\n */\nimport { join } from \"node:path\";\n\nlet backupsRoot = \"\";\nlet sessionsRoot = \"\";\n\n/** Bind the on-disk roots. Called once by index.ts (or by selftests). */\nexport function configureStorage(opts: { backupsRoot: string; sessionsRoot: string }): void {\n\tbackupsRoot = opts.backupsRoot;\n\tsessionsRoot = opts.sessionsRoot;\n}\n\n/** Root holding every session's backup directory. */\nexport function backupsRootDir(): string {\n\treturn backupsRoot;\n}\n\n/** A single session's backup directory. */\nexport function backupsDir(sessionId: string): string {\n\treturn join(backupsRoot, sessionId);\n}\n\n/** Root holding Pi's session JSONL files (for orphan detection). */\nexport function sessionsRootDir(): string {\n\treturn sessionsRoot;\n}\n"]}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/storage.ts"],"names":[],"mappings":"AAiBA,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG1F;AAED,qDAAqD;AACrD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,2CAA2C;AAC3C,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,oEAAoE;AACpE,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/extensions/rewind/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,IAAI,WAAW,GAAG,EAAE,CAAC;AACrB,IAAI,YAAY,GAAG,EAAE,CAAC;AAEtB,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,IAAmD,EAAQ;IAC3F,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAC/B,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;AAAA,CACjC;AAED,qDAAqD;AACrD,MAAM,UAAU,cAAc,GAAW;IACxC,OAAO,WAAW,CAAC;AAAA,CACnB;AAED,2CAA2C;AAC3C,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAU;IACrD,OAAO,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAAA,CACpC;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe,GAAW;IACzC,OAAO,YAAY,CAAC;AAAA,CACpB","sourcesContent":["/**\n * storage.ts — injected storage roots for the rewind engine.\n *\n * engine.ts and gc.ts must stay loadable under plain node (for offline\n * selftests), so they cannot statically import paths.ts (which imports\n * getAgentDir from @astralyn/pi — unresolvable outside Pi).\n * Instead they read the roots from here, and the integration layer (index.ts)\n * calls configureStorage() with the real paths at startup. Selftests call it with\n * temp directories.\n *\n * No Pi imports in this module.\n */\nimport { join } from \"node:path\";\n\nlet backupsRoot = \"\";\nlet sessionsRoot = \"\";\n\n/** Bind the on-disk roots. Called once by index.ts (or by selftests). */\nexport function configureStorage(opts: { backupsRoot: string; sessionsRoot: string }): void {\n\tbackupsRoot = opts.backupsRoot;\n\tsessionsRoot = opts.sessionsRoot;\n}\n\n/** Root holding every session's backup directory. */\nexport function backupsRootDir(): string {\n\treturn backupsRoot;\n}\n\n/** A single session's backup directory. */\nexport function backupsDir(sessionId: string): string {\n\treturn join(backupsRoot, sessionId);\n}\n\n/** Root holding Pi's session JSONL files (for orphan detection). */\nexport function sessionsRootDir(): string {\n\treturn sessionsRoot;\n}\n"]}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../src/extensions/rewind/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,IAAI,WAAW,GAAG,EAAE,CAAC;AACrB,IAAI,YAAY,GAAG,EAAE,CAAC;AAEtB,yEAAyE;AACzE,MAAM,UAAU,gBAAgB,CAAC,IAAmD;IACnF,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAC/B,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;AAClC,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,cAAc;IAC7B,OAAO,WAAW,CAAC;AACpB,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC3C,OAAO,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AACrC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe;IAC9B,OAAO,YAAY,CAAC;AACrB,CAAC","sourcesContent":["/**\n * storage.ts — injected storage roots for the rewind engine.\n *\n * engine.ts and gc.ts must stay loadable under plain node (for offline\n * selftests), so they cannot statically import paths.ts (which imports\n * getAgentDir from @astralyn/pi — unresolvable outside Pi).\n * Instead they read the roots from here, and the integration layer (index.ts)\n * calls configureStorage() with the real paths at startup. Selftests call it with\n * temp directories.\n *\n * No Pi imports in this module.\n */\nimport { join } from \"node:path\";\n\nlet backupsRoot = \"\";\nlet sessionsRoot = \"\";\n\n/** Bind the on-disk roots. Called once by index.ts (or by selftests). */\nexport function configureStorage(opts: { backupsRoot: string; sessionsRoot: string }): void {\n\tbackupsRoot = opts.backupsRoot;\n\tsessionsRoot = opts.sessionsRoot;\n}\n\n/** Root holding every session's backup directory. */\nexport function backupsRootDir(): string {\n\treturn backupsRoot;\n}\n\n/** A single session's backup directory. */\nexport function backupsDir(sessionId: string): string {\n\treturn join(backupsRoot, sessionId);\n}\n\n/** Root holding Pi's session JSONL files (for orphan detection). */\nexport function sessionsRootDir(): string {\n\treturn sessionsRoot;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/text.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAMvG;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhD","sourcesContent":["export interface TruncateTextOptions {\n\tellipsis?: string;\n\tcollapseWhitespace?: boolean;\n}\n\nexport function truncateText(text: string, maxLength: number, options: TruncateTextOptions = {}): string {\n\tconst ellipsis = options.ellipsis ?? \"...\";\n\tconst source = options.collapseWhitespace ? text.replace(/\\s+/g, \" \").trim() : text;\n\tif (source.length <= maxLength) return source;\n\tif (ellipsis.length >= maxLength) return ellipsis.slice(0, maxLength);\n\treturn `${source.slice(0, maxLength - ellipsis.length)}${ellipsis}`;\n}\n\nexport function formatSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes}B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n\tif (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n\treturn `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;\n}\n"]}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/text.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAMvG;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/extensions/rewind/text.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,SAAiB,EAAE,OAAO,GAAwB,EAAE,EAAU;IACxG,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,IAAI,MAAM,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,QAAQ,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtE,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;AAAA,CACpE;AAED,MAAM,UAAU,UAAU,CAAC,KAAa,EAAU;IACjD,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,GAAG,CAAC;IACrC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAAA,CACxD","sourcesContent":["export interface TruncateTextOptions {\n\tellipsis?: string;\n\tcollapseWhitespace?: boolean;\n}\n\nexport function truncateText(text: string, maxLength: number, options: TruncateTextOptions = {}): string {\n\tconst ellipsis = options.ellipsis ?? \"...\";\n\tconst source = options.collapseWhitespace ? text.replace(/\\s+/g, \" \").trim() : text;\n\tif (source.length <= maxLength) return source;\n\tif (ellipsis.length >= maxLength) return ellipsis.slice(0, maxLength);\n\treturn `${source.slice(0, maxLength - ellipsis.length)}${ellipsis}`;\n}\n\nexport function formatSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes}B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n\tif (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n\treturn `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;\n}\n"]}
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/extensions/rewind/text.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,SAAiB,EAAE,UAA+B,EAAE;IAC9F,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,IAAI,MAAM,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,QAAQ,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtE,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,QAAQ,EAAE,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,GAAG,CAAC;IACrC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACzD,CAAC","sourcesContent":["export interface TruncateTextOptions {\n\tellipsis?: string;\n\tcollapseWhitespace?: boolean;\n}\n\nexport function truncateText(text: string, maxLength: number, options: TruncateTextOptions = {}): string {\n\tconst ellipsis = options.ellipsis ?? \"...\";\n\tconst source = options.collapseWhitespace ? text.replace(/\\s+/g, \" \").trim() : text;\n\tif (source.length <= maxLength) return source;\n\tif (ellipsis.length >= maxLength) return ellipsis.slice(0, maxLength);\n\treturn `${source.slice(0, maxLength - ellipsis.length)}${ellipsis}`;\n}\n\nexport function formatSize(bytes: number): string {\n\tif (bytes < 1024) return `${bytes}B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n\tif (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n\treturn `${(bytes / (1024 * 1024 * 1024)).toFixed(2)}GB`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-path.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/tool-path.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpE","sourcesContent":["import path from \"node:path\";\n\nexport function editWriteTargetPath(input: unknown): string | undefined {\n\tconst rawPath = (input as { path?: unknown } | undefined)?.path;\n\treturn typeof rawPath === \"string\" && rawPath.length > 0 ? rawPath : undefined;\n}\n\nexport function resolveToolPath(rawPath: string, cwd: string): string {\n\treturn path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);\n}\n"]}
1
+ {"version":3,"file":"tool-path.d.ts","sourceRoot":"","sources":["../../../src/extensions/rewind/tool-path.ts"],"names":[],"mappings":"AAEA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGtE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpE"}
@@ -1 +1 @@
1
- {"version":3,"file":"tool-path.js","sourceRoot":"","sources":["../../../src/extensions/rewind/tool-path.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,mBAAmB,CAAC,KAAc,EAAsB;IACvE,MAAM,OAAO,GAAI,KAAwC,EAAE,IAAI,CAAC;IAChE,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC/E;AAED,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,GAAW,EAAU;IACrE,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,CACvE","sourcesContent":["import path from \"node:path\";\n\nexport function editWriteTargetPath(input: unknown): string | undefined {\n\tconst rawPath = (input as { path?: unknown } | undefined)?.path;\n\treturn typeof rawPath === \"string\" && rawPath.length > 0 ? rawPath : undefined;\n}\n\nexport function resolveToolPath(rawPath: string, cwd: string): string {\n\treturn path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);\n}\n"]}
1
+ {"version":3,"file":"tool-path.js","sourceRoot":"","sources":["../../../src/extensions/rewind/tool-path.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,mBAAmB,CAAC,KAAc;IACjD,MAAM,OAAO,GAAI,KAAwC,EAAE,IAAI,CAAC;IAChE,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,GAAW;IAC3D,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AACxE,CAAC","sourcesContent":["import path from \"node:path\";\n\nexport function editWriteTargetPath(input: unknown): string | undefined {\n\tconst rawPath = (input as { path?: unknown } | undefined)?.path;\n\treturn typeof rawPath === \"string\" && rawPath.length > 0 ? rawPath : undefined;\n}\n\nexport function resolveToolPath(rawPath: string, cwd: string): string {\n\treturn path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);\n}\n"]}
@@ -11,8 +11,11 @@ export declare const DEFAULTS: {
11
11
  readonly probeMaxModels: 2000;
12
12
  readonly originator: "codex";
13
13
  };
14
+ /** Thinking levels understood by Pi and the relay config format. */
14
15
  export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
15
16
  export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
17
+ /** Thinking levels exposed by every router model (GPT Gateway policy). */
18
+ export declare const ROUTER_THINKING_LEVELS: readonly ["low", "medium", "high", "xhigh", "max"];
16
19
  export declare const NO_UI_WARNING = "/router requires an interactive UI.";
17
20
  export declare function isValidRelayId(id: string): boolean;
18
21
  export declare function truncate(value: string, maxLength: number): string;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,WAAW,CAAC;AACrC,eAAO,MAAM,mBAAmB,kCAAkC,CAAC;AAEnE,4EAA4E;AAC5E,eAAO,MAAM,UAAU,gBAA0B,CAAC;AAElD,eAAO,MAAM,cAAc,GAAa,CAAC;AAEzC,eAAO,MAAM,QAAQ;;;;;;;CAOX,CAAC;AAEX,eAAO,MAAM,eAAe,sEAAuE,CAAC;AACpG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,aAAa,wCAAwC,CAAC;AAEnE,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD","sourcesContent":["export const COMMAND_NAME = \"router\";\nexport const COMMAND_DESCRIPTION = \"Manage Codex-style API relays\";\n\n/** Custom api tag; routes to our streamSimple, not Pi built-in handlers. */\nexport const ROUTER_API = \"router-codex\" as const;\n\nexport const CONFIG_VERSION = 1 as const;\n\nexport const DEFAULTS = {\n\tcontextWindow: 272_000,\n\tmaxTokens: 128_000,\n\tprobeTimeoutMs: 10_000,\n\tprobeBodyBytes: 4 * 1024 * 1024,\n\tprobeMaxModels: 2_000,\n\toriginator: \"codex\",\n} as const;\n\nexport const THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport type ThinkingLevel = (typeof THINKING_LEVELS)[number];\n\nexport const NO_UI_WARNING = \"/router requires an interactive UI.\";\n\nexport function isValidRelayId(id: string): boolean {\n\treturn Boolean(id) && !id.includes(\"/\") && !/[\\u0000-\\u001f\\u007f]/.test(id);\n}\n\nexport function truncate(value: string, maxLength: number): string {\n\tif (value.length <= maxLength) return value;\n\treturn `${value.slice(0, Math.max(0, maxLength - 1))}…`;\n}\n\nexport function formatError(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n"]}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,WAAW,CAAC;AACrC,eAAO,MAAM,mBAAmB,kCAAkC,CAAC;AAEnE,4EAA4E;AAC5E,eAAO,MAAM,UAAU,EAAG,cAAuB,CAAC;AAElD,eAAO,MAAM,cAAc,EAAG,CAAU,CAAC;AAEzC,eAAO,MAAM,QAAQ;;;;;;;CAOX,CAAC;AAEX,oEAAoE;AACpE,eAAO,MAAM,eAAe,sEAAuE,CAAC;AACpG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,oDAAqD,CAAC;AAEzF,eAAO,MAAM,aAAa,wCAAwC,CAAC;AAEnE,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD"}
@@ -11,7 +11,10 @@ export const DEFAULTS = {
11
11
  probeMaxModels: 2_000,
12
12
  originator: "codex",
13
13
  };
14
+ /** Thinking levels understood by Pi and the relay config format. */
14
15
  export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
16
+ /** Thinking levels exposed by every router model (GPT Gateway policy). */
17
+ export const ROUTER_THINKING_LEVELS = ["low", "medium", "high", "xhigh", "max"];
15
18
  export const NO_UI_WARNING = "/router requires an interactive UI.";
16
19
  export function isValidRelayId(id) {
17
20
  return Boolean(id) && !id.includes("/") && !/[\u0000-\u001f\u007f]/.test(id);
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/router/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AACrC,MAAM,CAAC,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAEnE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,UAAU,GAAG,cAAuB,CAAC;AAElD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,QAAQ,GAAG;IACvB,aAAa,EAAE,OAAO;IACtB,SAAS,EAAE,OAAO;IAClB,cAAc,EAAE,MAAM;IACtB,cAAc,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC/B,cAAc,EAAE,KAAK;IACrB,UAAU,EAAE,OAAO;CACV,CAAC;AAEX,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAU,CAAC;AAGpG,MAAM,CAAC,MAAM,aAAa,GAAG,qCAAqC,CAAC;AAEnE,MAAM,UAAU,cAAc,CAAC,EAAU,EAAW;IACnD,OAAO,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,CAC7E;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,SAAiB,EAAU;IAClE,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,KAAG,CAAC;AAAA,CACxD;AAED,MAAM,UAAU,WAAW,CAAC,KAAc,EAAU;IACnD,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAAA,CAC9D","sourcesContent":["export const COMMAND_NAME = \"router\";\nexport const COMMAND_DESCRIPTION = \"Manage Codex-style API relays\";\n\n/** Custom api tag; routes to our streamSimple, not Pi built-in handlers. */\nexport const ROUTER_API = \"router-codex\" as const;\n\nexport const CONFIG_VERSION = 1 as const;\n\nexport const DEFAULTS = {\n\tcontextWindow: 272_000,\n\tmaxTokens: 128_000,\n\tprobeTimeoutMs: 10_000,\n\tprobeBodyBytes: 4 * 1024 * 1024,\n\tprobeMaxModels: 2_000,\n\toriginator: \"codex\",\n} as const;\n\nexport const THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport type ThinkingLevel = (typeof THINKING_LEVELS)[number];\n\nexport const NO_UI_WARNING = \"/router requires an interactive UI.\";\n\nexport function isValidRelayId(id: string): boolean {\n\treturn Boolean(id) && !id.includes(\"/\") && !/[\\u0000-\\u001f\\u007f]/.test(id);\n}\n\nexport function truncate(value: string, maxLength: number): string {\n\tif (value.length <= maxLength) return value;\n\treturn `${value.slice(0, Math.max(0, maxLength - 1))}…`;\n}\n\nexport function formatError(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/router/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AACrC,MAAM,CAAC,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAEnE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,UAAU,GAAG,cAAuB,CAAC;AAElD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,QAAQ,GAAG;IACvB,aAAa,EAAE,OAAO;IACtB,SAAS,EAAE,OAAO;IAClB,cAAc,EAAE,MAAM;IACtB,cAAc,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IAC/B,cAAc,EAAE,KAAK;IACrB,UAAU,EAAE,OAAO;CACV,CAAC;AAEX,oEAAoE;AACpE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAU,CAAC;AAGpG,0EAA0E;AAC1E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAU,CAAC;AAEzF,MAAM,CAAC,MAAM,aAAa,GAAG,qCAAqC,CAAC;AAEnE,MAAM,UAAU,cAAc,CAAC,EAAU;IACxC,OAAO,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,SAAiB;IACxD,IAAI,KAAK,CAAC,MAAM,IAAI,SAAS;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["export const COMMAND_NAME = \"router\";\nexport const COMMAND_DESCRIPTION = \"Manage Codex-style API relays\";\n\n/** Custom api tag; routes to our streamSimple, not Pi built-in handlers. */\nexport const ROUTER_API = \"router-codex\" as const;\n\nexport const CONFIG_VERSION = 1 as const;\n\nexport const DEFAULTS = {\n\tcontextWindow: 272_000,\n\tmaxTokens: 128_000,\n\tprobeTimeoutMs: 10_000,\n\tprobeBodyBytes: 4 * 1024 * 1024,\n\tprobeMaxModels: 2_000,\n\toriginator: \"codex\",\n} as const;\n\n/** Thinking levels understood by Pi and the relay config format. */\nexport const THINKING_LEVELS = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nexport type ThinkingLevel = (typeof THINKING_LEVELS)[number];\n\n/** Thinking levels exposed by every router model (GPT Gateway policy). */\nexport const ROUTER_THINKING_LEVELS = [\"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\n\nexport const NO_UI_WARNING = \"/router requires an interactive UI.\";\n\nexport function isValidRelayId(id: string): boolean {\n\treturn Boolean(id) && !id.includes(\"/\") && !/[\\u0000-\\u001f\\u007f]/.test(id);\n}\n\nexport function truncate(value: string, maxLength: number): string {\n\tif (value.length <= maxLength) return value;\n\treturn `${value.slice(0, Math.max(0, maxLength - 1))}…`;\n}\n\nexport function formatError(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  /** Private TUI primitives for /router. */
2
2
  import { Container, type KeybindingsManager, type TUI } from "@earendil-works/pi-tui";
3
3
  import type { Theme } from "../../modes/interactive/theme/theme.ts";
4
+ import { type ThinkingLevel } from "./constants.ts";
4
5
  import type { ThinkingLevelMap } from "./types.ts";
5
6
  export interface SelectItem<T extends string = string> {
6
7
  value: T;
@@ -17,22 +18,28 @@ export declare function createSearchableSelector<T extends string>(opts: {
17
18
  maxVisible?: number;
18
19
  emptyMessage?: string;
19
20
  }): (tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: T | undefined) => void) => Container;
21
+ export interface ModelChecklistItem {
22
+ id: string;
23
+ name?: string;
24
+ unavailable?: boolean;
25
+ }
20
26
  export declare function createModelChecklist(opts: {
21
27
  title: string;
22
28
  subtitle?: string;
23
- models: ReadonlyArray<{
24
- id: string;
25
- name?: string;
26
- }>;
29
+ models: ReadonlyArray<ModelChecklistItem>;
27
30
  initiallySelected?: ReadonlySet<string>;
31
+ /** Models that must remain selected while the checklist is open. */
32
+ protectedIds?: ReadonlySet<string>;
33
+ onChange?: (selectedIds: string[]) => void;
34
+ onProtectedToggle?: (id: string) => void;
28
35
  }): (tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: {
29
- kind: "save";
36
+ kind: "close";
30
37
  selectedIds: string[];
31
- } | {
32
- kind: "cancel";
33
38
  }) => void) => Container;
34
39
  export declare function createThinkingMapEditor(opts: {
35
40
  title: string;
36
41
  map: ThinkingLevelMap;
42
+ levels?: ReadonlyArray<ThinkingLevel>;
43
+ onChange?: (map: ThinkingLevelMap) => void;
37
44
  }): (tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: ThinkingLevelMap | undefined) => void) => Container;
38
45
  //# sourceMappingURL=dialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/dialog.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAE1C,OAAO,EACN,SAAS,EAGT,KAAK,kBAAkB,EAIvB,KAAK,GAAG,EACR,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAEpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACpD,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,KAAK,SAAS,CA8HhH;AAQD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,iBAAiB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACxC,GAAG,CACH,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,kBAAkB,EAC/B,IAAI,EAAE,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,KAAK,IAAI,KAChF,SAAS,CA2Kb;AAWD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,gBAAgB,CAAC;CACtB,GAAG,CACH,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,kBAAkB,EAC/B,IAAI,EAAE,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,KAAK,IAAI,KAChD,SAAS,CAiHb","sourcesContent":["/** Private TUI primitives for /router. */\n\nimport {\n\tContainer,\n\tfuzzyFilter,\n\tInput,\n\ttype KeybindingsManager,\n\tSpacer,\n\tText,\n\tTruncatedText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport { DynamicBorder } from \"../../modes/interactive/components/dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { THINKING_LEVELS, type ThinkingLevel, truncate } from \"./constants.ts\";\nimport type { ThinkingLevelMap } from \"./types.ts\";\n\nexport interface SelectItem<T extends string = string> {\n\tvalue: T;\n\tlabel: string;\n\tdescription?: string;\n\tsearchText?: string;\n}\n\nexport function createSearchableSelector<T extends string>(opts: {\n\ttitle: string;\n\tsubtitle?: string;\n\titems: ReadonlyArray<SelectItem<T>>;\n\tinitialValue?: T;\n\tinitialQuery?: string;\n\tmaxVisible?: number;\n\temptyMessage?: string;\n}): (tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: T | undefined) => void) => Container {\n\treturn (tui, theme, keybindings, done) => {\n\t\tconst allItems = [...opts.items];\n\t\tlet filteredItems = allItems;\n\t\tlet selectedIndex = Math.max(\n\t\t\t0,\n\t\t\tallItems.findIndex((item) => item.value === opts.initialValue),\n\t\t);\n\t\tconst maxVisible = Math.max(1, opts.maxVisible ?? 10);\n\t\tconst input = new Input();\n\t\tif (opts.initialQuery) input.setValue(opts.initialQuery);\n\t\tconst search = new Container();\n\t\tconst list = new Container();\n\t\tconst footer = new Text(\"\", 1, 0);\n\t\tconst container = new Container() as Container & {\n\t\t\thandleInput: (data: string) => void;\n\t\t\tfocused: boolean;\n\t\t};\n\n\t\tconst refresh = () => {\n\t\t\tsearch.clear();\n\t\t\tif (input.getValue()) {\n\t\t\t\tsearch.addChild(input);\n\t\t\t\tsearch.addChild(new Spacer(1));\n\t\t\t}\n\t\t\tlist.clear();\n\t\t\tconst start = Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, filteredItems.length - maxVisible)),\n\t\t\t);\n\t\t\tconst end = Math.min(start + maxVisible, filteredItems.length);\n\t\t\tfor (let index = start; index < end; index++) {\n\t\t\t\tconst item = filteredItems[index];\n\t\t\t\tif (!item) continue;\n\t\t\t\tconst active = index === selectedIndex;\n\t\t\t\tconst prefix = active ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\t\tconst label = theme.fg(active ? \"accent\" : \"text\", item.label);\n\t\t\t\tconst description = item.description ? theme.fg(\"muted\", ` ${truncate(item.description, 48)}`) : \"\";\n\t\t\t\tlist.addChild(new TruncatedText(prefix + label + description, 1, 0));\n\t\t\t}\n\t\t\tif (filteredItems.length === 0) {\n\t\t\t\tlist.addChild(new Text(theme.fg(\"muted\", ` ${opts.emptyMessage ?? \"No matching items\"}`), 1, 0));\n\t\t\t} else if (start > 0 || end < filteredItems.length) {\n\t\t\t\tlist.addChild(new Text(theme.fg(\"muted\", ` (${selectedIndex + 1}/${filteredItems.length})`), 1, 0));\n\t\t\t}\n\t\t\tconst hints = [\n\t\t\t\trawKeyHint(\"type\", \"filter\"),\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\"),\n\t\t\t\tkeyHint(\"tui.select.confirm\", \"select\"),\n\t\t\t\tkeyHint(\"tui.select.cancel\", input.getValue() ? \"clear filter\" : \"back\"),\n\t\t\t];\n\t\t\tfooter.setText(hints.join(\" \"));\n\t\t\tcontainer.invalidate();\n\t\t\ttui.requestRender();\n\t\t};\n\n\t\tconst applyFilter = (query: string, preferred?: T) => {\n\t\t\tfilteredItems = query\n\t\t\t\t? fuzzyFilter(\n\t\t\t\t\t\tallItems,\n\t\t\t\t\t\tquery,\n\t\t\t\t\t\t(item) => item.searchText ?? `${item.label} ${item.description ?? \"\"} ${item.value}`,\n\t\t\t\t\t)\n\t\t\t\t: allItems;\n\t\t\tconst preferredIndex = preferred ? filteredItems.findIndex((item) => item.value === preferred) : -1;\n\t\t\tselectedIndex = preferredIndex >= 0 ? preferredIndex : 0;\n\t\t\trefresh();\n\t\t};\n\n\t\tcontainer.addChild(new DynamicBorder((text) => theme.fg(\"border\", text)));\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new TruncatedText(theme.fg(\"accent\", theme.bold(opts.title)), 1, 0));\n\t\tif (opts.subtitle) container.addChild(new TruncatedText(theme.fg(\"muted\", opts.subtitle), 1, 0));\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(search);\n\t\tcontainer.addChild(list);\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(footer);\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new DynamicBorder((text) => theme.fg(\"border\", text)));\n\n\t\tObject.defineProperty(container, \"focused\", {\n\t\t\tget: () => input.focused,\n\t\t\tset: (focused: boolean) => {\n\t\t\t\tinput.focused = focused;\n\t\t\t},\n\t\t});\n\n\t\tcontainer.handleInput = (data: string) => {\n\t\t\tif (keybindings.matches(data, \"tui.select.up\")) {\n\t\t\t\tif (filteredItems.length > 0) {\n\t\t\t\t\tselectedIndex = selectedIndex === 0 ? filteredItems.length - 1 : selectedIndex - 1;\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"tui.select.down\")) {\n\t\t\t\tif (filteredItems.length > 0) {\n\t\t\t\t\tselectedIndex = selectedIndex === filteredItems.length - 1 ? 0 : selectedIndex + 1;\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"tui.select.confirm\")) {\n\t\t\t\tconst item = filteredItems[selectedIndex];\n\t\t\t\tif (item) done(item.value);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\t\tif (input.getValue()) {\n\t\t\t\t\tinput.setValue(\"\");\n\t\t\t\t\tapplyFilter(\"\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdone(undefined);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst before = input.getValue();\n\t\t\tinput.handleInput(data);\n\t\t\tconst after = input.getValue();\n\t\t\tif (after !== before) applyFilter(after);\n\t\t};\n\n\t\tapplyFilter(opts.initialQuery ?? \"\", opts.initialValue);\n\t\treturn container;\n\t};\n}\n\nfunction sameIdSet(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {\n\tif (a.size !== b.size) return false;\n\tfor (const id of a) if (!b.has(id)) return false;\n\treturn true;\n}\n\nexport function createModelChecklist(opts: {\n\ttitle: string;\n\tsubtitle?: string;\n\tmodels: ReadonlyArray<{ id: string; name?: string }>;\n\tinitiallySelected?: ReadonlySet<string>;\n}): (\n\ttui: TUI,\n\ttheme: Theme,\n\tkeybindings: KeybindingsManager,\n\tdone: (result: { kind: \"save\"; selectedIds: string[] } | { kind: \"cancel\" }) => void,\n) => Container {\n\treturn (tui, theme, keybindings, done) => {\n\t\tconst items = opts.models.map((model) => ({\n\t\t\tid: model.id,\n\t\t\tlabel: model.name && model.name !== model.id ? `${model.id} · ${model.name}` : model.id,\n\t\t}));\n\t\tconst initialSelected = new Set(opts.initiallySelected ?? []);\n\t\tconst selected = new Set(initialSelected);\n\t\tlet filter = \"\";\n\t\tlet selectedIndex = 0;\n\t\tlet discardArmed = false;\n\t\tconst maxVisible = 12;\n\t\tconst input = new Input();\n\t\tconst list = new Container();\n\t\tconst footer = new Text(\"\", 1, 0);\n\t\tconst container = new Container() as Container & {\n\t\t\thandleInput: (data: string) => void;\n\t\t\tfocused: boolean;\n\t\t};\n\n\t\tconst isDirty = () => !sameIdSet(selected, initialSelected);\n\n\t\tconst filtered = () => {\n\t\t\tconst q = filter.trim().toLowerCase();\n\t\t\tif (!q) return items;\n\t\t\treturn items.filter((item) => item.id.toLowerCase().includes(q) || item.label.toLowerCase().includes(q));\n\t\t};\n\n\t\tconst refresh = () => {\n\t\t\tconst rows = filtered();\n\t\t\tif (selectedIndex >= rows.length) selectedIndex = Math.max(0, rows.length - 1);\n\t\t\tlist.clear();\n\t\t\tconst start = Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.min(selectedIndex - Math.floor(maxVisible / 2), Math.max(0, rows.length - maxVisible)),\n\t\t\t);\n\t\t\tconst end = Math.min(start + maxVisible, rows.length);\n\t\t\tfor (let index = start; index < end; index++) {\n\t\t\t\tconst item = rows[index]!;\n\t\t\t\tconst active = index === selectedIndex;\n\t\t\t\tconst mark = selected.has(item.id) ? theme.fg(\"success\", \"[x]\") : theme.fg(\"muted\", \"[ ]\");\n\t\t\t\tconst prefix = active ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\t\tconst label = theme.fg(active ? \"accent\" : \"text\", item.label);\n\t\t\t\tlist.addChild(new TruncatedText(`${prefix}${mark} ${label}`, 1, 0));\n\t\t\t}\n\t\t\tif (rows.length === 0) {\n\t\t\t\tlist.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 1, 0));\n\t\t\t}\n\n\t\t\tconst statusParts = [`${selected.size} selected`, `${rows.length} shown`];\n\t\t\tif (isDirty()) statusParts.push(theme.fg(\"warning\", \"unsaved\"));\n\t\t\tconst statusLine = theme.fg(\"muted\", statusParts.join(\" · \"));\n\n\t\t\tlet hints: string[];\n\t\t\tif (discardArmed && isDirty()) {\n\t\t\t\thints = [\n\t\t\t\t\ttheme.fg(\"warning\", \"Unsaved selection — Esc again discards\"),\n\t\t\t\t\tkeyHint(\"app.models.save\", \"apply\"),\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\thints = [\n\t\t\t\t\tkeyHint(\"app.list.toggle\", \"toggle\"),\n\t\t\t\t\tkeyHint(\"app.models.enableAll\", \"all\"),\n\t\t\t\t\tkeyHint(\"app.models.clearAll\", \"none\"),\n\t\t\t\t\tkeyHint(\"app.models.save\", \"apply\"),\n\t\t\t\t\trawKeyHint(\"type\", \"filter\"),\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", filter ? \"clear\" : isDirty() ? \"warn discard\" : \"cancel\"),\n\t\t\t\t];\n\t\t\t}\n\t\t\tfooter.setText(`${statusLine}\\n${hints.join(\" \")}`);\n\t\t\tcontainer.invalidate();\n\t\t\ttui.requestRender();\n\t\t};\n\n\t\tconst markChanged = () => {\n\t\t\tdiscardArmed = false;\n\t\t\trefresh();\n\t\t};\n\n\t\tcontainer.addChild(new DynamicBorder((text) => theme.fg(\"border\", text)));\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new TruncatedText(theme.fg(\"accent\", theme.bold(opts.title)), 1, 0));\n\t\tif (opts.subtitle) container.addChild(new TruncatedText(theme.fg(\"muted\", opts.subtitle), 1, 0));\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(input);\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(list);\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(footer);\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new DynamicBorder((text) => theme.fg(\"border\", text)));\n\n\t\tObject.defineProperty(container, \"focused\", {\n\t\t\tget: () => input.focused,\n\t\t\tset: (focused: boolean) => {\n\t\t\t\tinput.focused = focused;\n\t\t\t},\n\t\t});\n\n\t\tcontainer.handleInput = (data: string) => {\n\t\t\tconst rows = filtered();\n\t\t\tif (keybindings.matches(data, \"app.list.toggle\")) {\n\t\t\t\tconst item = rows[selectedIndex];\n\t\t\t\tif (item) {\n\t\t\t\t\tif (selected.has(item.id)) selected.delete(item.id);\n\t\t\t\t\telse selected.add(item.id);\n\t\t\t\t\tmarkChanged();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"app.models.enableAll\")) {\n\t\t\t\tfor (const item of rows) selected.add(item.id);\n\t\t\t\tmarkChanged();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"app.models.clearAll\")) {\n\t\t\t\tfor (const item of rows) selected.delete(item.id);\n\t\t\t\tmarkChanged();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"app.models.save\")) {\n\t\t\t\tdone({ kind: \"save\", selectedIds: [...selected] });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"tui.select.up\")) {\n\t\t\t\tif (rows.length > 0) {\n\t\t\t\t\tselectedIndex = selectedIndex === 0 ? rows.length - 1 : selectedIndex - 1;\n\t\t\t\t\tdiscardArmed = false;\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"tui.select.down\")) {\n\t\t\t\tif (rows.length > 0) {\n\t\t\t\t\tselectedIndex = selectedIndex === rows.length - 1 ? 0 : selectedIndex + 1;\n\t\t\t\t\tdiscardArmed = false;\n\t\t\t\t\trefresh();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\t\tif (filter) {\n\t\t\t\t\tfilter = \"\";\n\t\t\t\t\tinput.setValue(\"\");\n\t\t\t\t\tselectedIndex = 0;\n\t\t\t\t\tdiscardArmed = false;\n\t\t\t\t\trefresh();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (isDirty() && !discardArmed) {\n\t\t\t\t\tdiscardArmed = true;\n\t\t\t\t\trefresh();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdone({ kind: \"cancel\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst before = input.getValue();\n\t\t\tinput.handleInput(data);\n\t\t\tconst after = input.getValue();\n\t\t\tif (after !== before) {\n\t\t\t\tfilter = after;\n\t\t\t\tselectedIndex = 0;\n\t\t\t\tdiscardArmed = false;\n\t\t\t\trefresh();\n\t\t\t}\n\t\t};\n\n\t\trefresh();\n\t\treturn container;\n\t};\n}\n\nfunction thinkingMapsEqual(a: ThinkingLevelMap, b: ThinkingLevelMap): boolean {\n\tfor (const level of THINKING_LEVELS) {\n\t\tconst av = Object.hasOwn(a, level) ? a[level] : undefined;\n\t\tconst bv = Object.hasOwn(b, level) ? b[level] : undefined;\n\t\tif (av !== bv) return false;\n\t}\n\treturn true;\n}\n\nexport function createThinkingMapEditor(opts: {\n\ttitle: string;\n\tmap: ThinkingLevelMap;\n}): (\n\ttui: TUI,\n\ttheme: Theme,\n\tkeybindings: KeybindingsManager,\n\tdone: (result: ThinkingLevelMap | undefined) => void,\n) => Container {\n\treturn (tui, theme, keybindings, done) => {\n\t\tconst initial: ThinkingLevelMap = { ...opts.map };\n\t\tconst working: ThinkingLevelMap = { ...opts.map };\n\t\tlet index = 0;\n\t\tlet discardArmed = false;\n\t\tconst list = new Container();\n\t\tconst footer = new Text(\"\", 1, 0);\n\t\tconst container = new Container() as Container & {\n\t\t\thandleInput: (data: string) => void;\n\t\t\tfocused: boolean;\n\t\t};\n\n\t\tconst isDirty = () => !thinkingMapsEqual(working, initial);\n\n\t\tconst status = (level: ThinkingLevel): string => {\n\t\t\tconst value = working[level];\n\t\t\tif (value === null) return \"hidden\";\n\t\t\tif (value === undefined) return \"default\";\n\t\t\treturn value === level ? \"on\" : `→ ${value}`;\n\t\t};\n\n\t\tconst refresh = () => {\n\t\t\tlist.clear();\n\t\t\tfor (let i = 0; i < THINKING_LEVELS.length; i++) {\n\t\t\t\tconst level = THINKING_LEVELS[i]!;\n\t\t\t\tconst active = i === index;\n\t\t\t\tconst prefix = active ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\t\tconst label = theme.fg(active ? \"accent\" : \"text\", level.padEnd(8));\n\t\t\t\tconst st = status(level);\n\t\t\t\tconst color = st === \"hidden\" ? \"muted\" : st === \"default\" ? \"dim\" : \"success\";\n\t\t\t\tlist.addChild(new Text(`${prefix}${label} ${theme.fg(color, st)}`, 1, 0));\n\t\t\t}\n\n\t\t\tconst statusLine = isDirty() ? theme.fg(\"warning\", \"Unsaved changes\") : theme.fg(\"muted\", \"No changes yet\");\n\n\t\t\tlet hints: string[];\n\t\t\tif (discardArmed && isDirty()) {\n\t\t\t\thints = [theme.fg(\"warning\", \"Esc again discards\"), keyHint(\"app.models.save\", \"apply\")];\n\t\t\t} else {\n\t\t\t\thints = [\n\t\t\t\t\tkeyHint(\"app.list.toggle\", \"toggle on/hidden\"),\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"toggle on/hidden\"),\n\t\t\t\t\tkeyHint(\"app.models.save\", \"apply\"),\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", isDirty() ? \"warn discard\" : \"back\"),\n\t\t\t\t];\n\t\t\t}\n\t\t\tfooter.setText(`${statusLine}\\n${hints.join(\" \")}`);\n\t\t\tcontainer.invalidate();\n\t\t\ttui.requestRender();\n\t\t};\n\n\t\tconst toggle = () => {\n\t\t\tconst level = THINKING_LEVELS[index]!;\n\t\t\tif (working[level] === null) {\n\t\t\t\tworking[level] = level === \"off\" ? \"none\" : level;\n\t\t\t} else {\n\t\t\t\tworking[level] = null;\n\t\t\t}\n\t\t\tdiscardArmed = false;\n\t\t\trefresh();\n\t\t};\n\n\t\tcontainer.addChild(new DynamicBorder((text) => theme.fg(\"border\", text)));\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new TruncatedText(theme.fg(\"accent\", theme.bold(opts.title)), 1, 0));\n\t\tcontainer.addChild(new Text(theme.fg(\"muted\", \" Toggle which Pi thinking levels this model exposes.\"), 1, 0));\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(list);\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(footer);\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new DynamicBorder((text) => theme.fg(\"border\", text)));\n\n\t\tObject.defineProperty(container, \"focused\", {\n\t\t\tget: () => true,\n\t\t\tset: () => {},\n\t\t});\n\n\t\tcontainer.handleInput = (data: string) => {\n\t\t\tif (keybindings.matches(data, \"tui.select.up\")) {\n\t\t\t\tindex = index === 0 ? THINKING_LEVELS.length - 1 : index - 1;\n\t\t\t\tdiscardArmed = false;\n\t\t\t\trefresh();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"tui.select.down\")) {\n\t\t\t\tindex = index === THINKING_LEVELS.length - 1 ? 0 : index + 1;\n\t\t\t\tdiscardArmed = false;\n\t\t\t\trefresh();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"app.list.toggle\") || keybindings.matches(data, \"tui.select.confirm\")) {\n\t\t\t\ttoggle();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"app.models.save\")) {\n\t\t\t\tdone({ ...working });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\t\tif (isDirty() && !discardArmed) {\n\t\t\t\t\tdiscardArmed = true;\n\t\t\t\t\trefresh();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdone(undefined);\n\t\t\t}\n\t\t};\n\n\t\trefresh();\n\t\treturn container;\n\t};\n}\n"]}
1
+ {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/extensions/router/dialog.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAE1C,OAAO,EACN,SAAS,EAGT,KAAK,kBAAkB,EAIvB,KAAK,GAAG,EACR,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EAA0B,KAAK,aAAa,EAAY,MAAM,gBAAgB,CAAC;AAEtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM;IACpD,KAAK,EAAE,CAAC,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,KAAK,SAAS,CAsHhH;AAED,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAC1C,iBAAiB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC,oEAAoE;IACpE,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C,iBAAiB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC,GAAG,CACH,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,kBAAkB,EAC/B,IAAI,EAAE,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,KAAK,IAAI,KAC5D,SAAS,CA8Jb;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,gBAAgB,CAAC;IACtB,MAAM,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IACtC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC3C,GAAG,CACH,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,kBAAkB,EAC/B,IAAI,EAAE,CAAC,MAAM,EAAE,gBAAgB,GAAG,SAAS,KAAK,IAAI,KAChD,SAAS,CAuFb"}