@baique/pi-web-sky 0.1.35 → 0.1.37

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 (483) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +11 -11
  3. package/.next/build-manifest.json +4 -4
  4. package/.next/next-server.js.nft.json +1 -1
  5. package/.next/prerender-manifest.json +3 -3
  6. package/.next/react-loadable-manifest.json +2 -2
  7. package/.next/required-server-files.js +6 -6
  8. package/.next/required-server-files.json +6 -6
  9. package/.next/server/app/_global-error/page.js +2 -2
  10. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/.next/server/app/_global-error.html +1 -1
  12. package/.next/server/app/_global-error.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/server/app/_not-found/page.js +1 -1
  17. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/.next/server/app/_not-found.html +1 -1
  19. package/.next/server/app/_not-found.rsc +3 -3
  20. package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
  22. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  23. package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
  24. package/.next/server/app/api/agent/[id]/bash-output/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/agent/[id]/events/route.js +3 -3
  26. package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/agent/[id]/route.js +2 -2
  28. package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/agent/new/route.js +1 -1
  30. package/.next/server/app/api/agent/new/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/agent/running/route.js +1 -1
  32. package/.next/server/app/api/agent/running/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/app-update/route.js +1 -1
  34. package/.next/server/app/api/app-update/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/auth/all-providers/route.js +1 -1
  36. package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
  38. package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/auth/login/[provider]/route.js +3 -3
  40. package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
  42. package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/auth/providers/route.js +1 -1
  44. package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/boards/[id]/canvas/route.js +1 -1
  46. package/.next/server/app/api/boards/[id]/canvas/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/boards/[id]/edges/[eid]/route.js +1 -1
  48. package/.next/server/app/api/boards/[id]/edges/[eid]/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/boards/[id]/edges/route.js +1 -1
  50. package/.next/server/app/api/boards/[id]/edges/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/boards/[id]/nodes/[nid]/route.js +1 -1
  52. package/.next/server/app/api/boards/[id]/nodes/[nid]/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/boards/[id]/nodes/route.js +1 -1
  54. package/.next/server/app/api/boards/[id]/nodes/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/boards/[id]/reconcile-task-edges/route.js +1 -1
  56. package/.next/server/app/api/boards/[id]/reconcile-task-edges/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/boards/[id]/route.js +1 -1
  58. package/.next/server/app/api/boards/[id]/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/boards/purge-orphans/route.js +1 -1
  60. package/.next/server/app/api/boards/purge-orphans/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/boards/reorder/route.js +1 -1
  62. package/.next/server/app/api/boards/reorder/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/boards/route.js +1 -1
  64. package/.next/server/app/api/boards/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/cwd/browse/route.js +1 -1
  66. package/.next/server/app/api/cwd/browse/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/cwd/validate/route.js +2 -2
  68. package/.next/server/app/api/cwd/validate/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/default-cwd/route.js +2 -2
  70. package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/file-index/route.js +2 -2
  72. package/.next/server/app/api/file-index/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/files/[...path]/route.js +2 -2
  74. package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/git/diff/route.js +1 -1
  76. package/.next/server/app/api/git/diff/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/git/status/route.js +1 -1
  78. package/.next/server/app/api/git/status/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/home/route.js +1 -1
  80. package/.next/server/app/api/home/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/mcp/route.js +2 -2
  82. package/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/models/route.js +2 -2
  84. package/.next/server/app/api/models/route_client-reference-manifest.js +1 -1
  85. package/.next/server/app/api/models-config/catalog/route.js +1 -1
  86. package/.next/server/app/api/models-config/catalog/route_client-reference-manifest.js +1 -1
  87. package/.next/server/app/api/models-config/discover/route.js +1 -1
  88. package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/models-config/route.js +1 -1
  90. package/.next/server/app/api/models-config/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/models-config/test/route.js +1 -1
  92. package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/api/plugins/route.js +2 -2
  94. package/.next/server/app/api/plugins/route_client-reference-manifest.js +1 -1
  95. package/.next/server/app/api/project-trust/route.js +2 -2
  96. package/.next/server/app/api/project-trust/route_client-reference-manifest.js +1 -1
  97. package/.next/server/app/api/quota/route.js +1 -1
  98. package/.next/server/app/api/quota/route_client-reference-manifest.js +1 -1
  99. package/.next/server/app/api/search/route.js +2 -2
  100. package/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  101. package/.next/server/app/api/sessions/[id]/auto-name/route.js +3 -3
  102. package/.next/server/app/api/sessions/[id]/auto-name/route_client-reference-manifest.js +1 -1
  103. package/.next/server/app/api/sessions/[id]/context/route.js +2 -2
  104. package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
  106. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route_client-reference-manifest.js +1 -1
  107. package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
  108. package/.next/server/app/api/sessions/[id]/export/route_client-reference-manifest.js +1 -1
  109. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  110. package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -1
  111. package/.next/server/app/api/sessions/[id]/state/route.js +2 -2
  112. package/.next/server/app/api/sessions/[id]/state/route_client-reference-manifest.js +1 -1
  113. package/.next/server/app/api/sessions/[id]/todos/route.js +2 -2
  114. package/.next/server/app/api/sessions/[id]/todos/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/sessions/route.js +2 -2
  116. package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/api/skills/check/route.js +1 -1
  118. package/.next/server/app/api/skills/check/route.js.nft.json +1 -1
  119. package/.next/server/app/api/skills/check/route_client-reference-manifest.js +1 -1
  120. package/.next/server/app/api/skills/install/route.js +2 -2
  121. package/.next/server/app/api/skills/install/route_client-reference-manifest.js +1 -1
  122. package/.next/server/app/api/skills/route.js +2 -31
  123. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  124. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  125. package/.next/server/app/api/skills/search/route.js +1 -1
  126. package/.next/server/app/api/skills/search/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/skills/update/route.js +1 -1
  128. package/.next/server/app/api/skills/update/route.js.nft.json +1 -1
  129. package/.next/server/app/api/skills/update/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/api/task-card-questions/[id]/answer/route.js +1 -1
  131. package/.next/server/app/api/task-card-questions/[id]/answer/route_client-reference-manifest.js +1 -1
  132. package/.next/server/app/api/task-card-questions/route.js +1 -1
  133. package/.next/server/app/api/task-card-questions/route_client-reference-manifest.js +1 -1
  134. package/.next/server/app/api/task-cards/[id]/route.js +1 -1
  135. package/.next/server/app/api/task-cards/[id]/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/api/task-cards/route.js +1 -1
  137. package/.next/server/app/api/task-cards/route_client-reference-manifest.js +1 -1
  138. package/.next/server/app/api/task-scheduler/status/route.js +1 -1
  139. package/.next/server/app/api/task-scheduler/status/route_client-reference-manifest.js +1 -1
  140. package/.next/server/app/api/tasks/[id]/board/route.js +1 -1
  141. package/.next/server/app/api/tasks/[id]/board/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/api/tasks/[id]/route.js +1 -1
  143. package/.next/server/app/api/tasks/[id]/route_client-reference-manifest.js +1 -1
  144. package/.next/server/app/api/tasks/reorder/route.js +1 -1
  145. package/.next/server/app/api/tasks/reorder/route_client-reference-manifest.js +1 -1
  146. package/.next/server/app/api/tasks/route.js +1 -1
  147. package/.next/server/app/api/tasks/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/api/terminal/[id]/events/route.js +3 -3
  149. package/.next/server/app/api/terminal/[id]/events/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/terminal/[id]/route.js +2 -2
  151. package/.next/server/app/api/terminal/[id]/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/terminal/route.js +2 -2
  153. package/.next/server/app/api/terminal/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/worktrees/route.js +2 -2
  155. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/favicon.ico/route.js +1 -1
  157. package/.next/server/app/index.html +1 -1
  158. package/.next/server/app/index.rsc +5 -5
  159. package/.next/server/app/index.segments/__PAGE__.segment.rsc +5 -5
  160. package/.next/server/app/index.segments/_full.segment.rsc +5 -5
  161. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  162. package/.next/server/app/manifest.webmanifest/route.js +2 -2
  163. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  164. package/.next/server/app/page.js +3 -3
  165. package/.next/server/app/page_client-reference-manifest.js +1 -1
  166. package/.next/server/app-paths-manifest.json +11 -11
  167. package/.next/server/chunks/2404.js +2 -2
  168. package/.next/server/chunks/2430.js +4 -4
  169. package/.next/server/chunks/3500.js +30 -0
  170. package/.next/server/chunks/4226.js +2 -2
  171. package/.next/server/chunks/542.js +11 -6
  172. package/.next/server/chunks/5794.js +2 -2
  173. package/.next/server/chunks/6429.js +1 -1
  174. package/.next/server/chunks/898.js +2 -2
  175. package/.next/server/instrumentation.js +1 -1
  176. package/.next/server/middleware-build-manifest.js +1 -1
  177. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  178. package/.next/server/middleware.js +3 -3
  179. package/.next/server/next-font-manifest.js +1 -1
  180. package/.next/server/next-font-manifest.json +1 -1
  181. package/.next/server/pages/404.html +1 -1
  182. package/.next/server/pages/500.html +1 -1
  183. package/.next/server/server-reference-manifest.json +1 -1
  184. package/.next/static/chunks/5315.dba7515ae6e139ae.js +4 -0
  185. package/.next/static/chunks/{7999.f4ab221861378788.js → 7999.879d5a20e8641752.js} +1 -1
  186. package/.next/static/chunks/app/_not-found/{page-af4fe23994cb3d66.js → page-8cea8d8de35f4da4.js} +1 -1
  187. package/.next/static/chunks/app/{layout-1d9a7a44fbc92d2e.js → layout-c4ca28ccc08bc975.js} +1 -1
  188. package/.next/static/chunks/app/{page-6d6779a81a0a7639.js → page-c9ead38525a85686.js} +3 -3
  189. package/.next/static/chunks/{main-app-b3e8a8d15d83e1f0.js → main-app-dd2385a1a5a588df.js} +1 -1
  190. package/.next/static/chunks/next/dist/client/components/builtin/global-error-be943dcb89867ab9.js +1 -0
  191. package/.next/static/chunks/{webpack-fa4f40085b1ec1ac.js → webpack-4bbf04891e910713.js} +1 -1
  192. package/.next/static/css/422c7dc0faa04d89.css +1 -0
  193. package/.next/trace +8 -9
  194. package/.next/trace-build +1 -1
  195. package/.next/types/app/api/agent/[id]/bash-output/route.ts +1 -1
  196. package/.next/types/app/api/agent/[id]/events/route.ts +1 -1
  197. package/.next/types/app/api/agent/[id]/route.ts +1 -1
  198. package/.next/types/app/api/agent/new/route.ts +1 -1
  199. package/.next/types/app/api/agent/running/route.ts +1 -1
  200. package/.next/types/app/api/app-update/route.ts +1 -1
  201. package/.next/types/app/api/auth/all-providers/route.ts +1 -1
  202. package/.next/types/app/api/auth/api-key/[provider]/route.ts +1 -1
  203. package/.next/types/app/api/auth/login/[provider]/route.ts +1 -1
  204. package/.next/types/app/api/auth/logout/[provider]/route.ts +1 -1
  205. package/.next/types/app/api/auth/providers/route.ts +1 -1
  206. package/.next/types/app/api/boards/[id]/canvas/route.ts +1 -1
  207. package/.next/types/app/api/boards/[id]/edges/[eid]/route.ts +1 -1
  208. package/.next/types/app/api/boards/[id]/edges/route.ts +1 -1
  209. package/.next/types/app/api/boards/[id]/nodes/[nid]/route.ts +1 -1
  210. package/.next/types/app/api/boards/[id]/nodes/route.ts +1 -1
  211. package/.next/types/app/api/boards/[id]/reconcile-task-edges/route.ts +1 -1
  212. package/.next/types/app/api/boards/[id]/route.ts +1 -1
  213. package/.next/types/app/api/boards/purge-orphans/route.ts +1 -1
  214. package/.next/types/app/api/boards/reorder/route.ts +1 -1
  215. package/.next/types/app/api/boards/route.ts +1 -1
  216. package/.next/types/app/api/cwd/browse/route.ts +1 -1
  217. package/.next/types/app/api/cwd/validate/route.ts +1 -1
  218. package/.next/types/app/api/default-cwd/route.ts +1 -1
  219. package/.next/types/app/api/file-index/route.ts +1 -1
  220. package/.next/types/app/api/files/[...path]/route.ts +1 -1
  221. package/.next/types/app/api/git/diff/route.ts +1 -1
  222. package/.next/types/app/api/git/status/route.ts +1 -1
  223. package/.next/types/app/api/home/route.ts +1 -1
  224. package/.next/types/app/api/mcp/route.ts +1 -1
  225. package/.next/types/app/api/models/route.ts +1 -1
  226. package/.next/types/app/api/models-config/catalog/route.ts +1 -1
  227. package/.next/types/app/api/models-config/discover/route.ts +1 -1
  228. package/.next/types/app/api/models-config/route.ts +1 -1
  229. package/.next/types/app/api/models-config/test/route.ts +1 -1
  230. package/.next/types/app/api/plugins/route.ts +1 -1
  231. package/.next/types/app/api/project-trust/route.ts +1 -1
  232. package/.next/types/app/api/quota/route.ts +1 -1
  233. package/.next/types/app/api/search/route.ts +1 -1
  234. package/.next/types/app/api/sessions/[id]/auto-name/route.ts +1 -1
  235. package/.next/types/app/api/sessions/[id]/context/route.ts +1 -1
  236. package/.next/types/app/api/sessions/[id]/entries/[entryId]/thinking/route.ts +1 -1
  237. package/.next/types/app/api/sessions/[id]/export/route.ts +1 -1
  238. package/.next/types/app/api/sessions/[id]/route.ts +1 -1
  239. package/.next/types/app/api/sessions/[id]/state/route.ts +1 -1
  240. package/.next/types/app/api/sessions/[id]/todos/route.ts +1 -1
  241. package/.next/types/app/api/sessions/route.ts +1 -1
  242. package/.next/types/app/api/skills/check/route.ts +1 -1
  243. package/.next/types/app/api/skills/install/route.ts +1 -1
  244. package/.next/types/app/api/skills/route.ts +1 -1
  245. package/.next/types/app/api/skills/search/route.ts +1 -1
  246. package/.next/types/app/api/skills/update/route.ts +1 -1
  247. package/.next/types/app/api/task-card-questions/[id]/answer/route.ts +1 -1
  248. package/.next/types/app/api/task-card-questions/route.ts +1 -1
  249. package/.next/types/app/api/task-cards/[id]/route.ts +1 -1
  250. package/.next/types/app/api/task-cards/route.ts +1 -1
  251. package/.next/types/app/api/task-scheduler/status/route.ts +1 -1
  252. package/.next/types/app/api/tasks/[id]/board/route.ts +1 -1
  253. package/.next/types/app/api/tasks/[id]/route.ts +1 -1
  254. package/.next/types/app/api/tasks/reorder/route.ts +1 -1
  255. package/.next/types/app/api/tasks/route.ts +1 -1
  256. package/.next/types/app/api/terminal/[id]/events/route.ts +1 -1
  257. package/.next/types/app/api/terminal/[id]/route.ts +1 -1
  258. package/.next/types/app/api/terminal/route.ts +1 -1
  259. package/.next/types/app/api/worktrees/route.ts +1 -1
  260. package/.next/types/app/layout.ts +1 -1
  261. package/.next/types/app/page.ts +1 -1
  262. package/bin/pi-web-sky.js +15 -20
  263. package/lib/agent-client.test.mjs +64 -0
  264. package/lib/agent-client.ts +53 -0
  265. package/lib/agent-event-connection.test.mjs +193 -0
  266. package/lib/agent-event-connection.ts +202 -0
  267. package/lib/agent-event-stream.test.mjs +155 -0
  268. package/lib/agent-event-stream.ts +132 -0
  269. package/lib/agent-event-wire.test.mjs +278 -0
  270. package/lib/agent-event-wire.ts +107 -0
  271. package/lib/agent-phase.ts +24 -0
  272. package/lib/allowed-roots.ts +31 -0
  273. package/lib/ansi.test.mjs +52 -0
  274. package/lib/ansi.ts +211 -0
  275. package/lib/api-types.ts +139 -0
  276. package/lib/app-update.test.mjs +27 -0
  277. package/lib/app-update.ts +28 -0
  278. package/lib/atomic-file.test.mjs +37 -0
  279. package/lib/atomic-file.ts +34 -0
  280. package/lib/audit-session.ts +235 -0
  281. package/lib/bash-output.test.mjs +63 -0
  282. package/lib/bash-output.ts +53 -0
  283. package/lib/bg-image.ts +129 -0
  284. package/lib/board-events.ts +83 -0
  285. package/lib/board-purge.test.mjs +66 -0
  286. package/lib/board-purge.ts +65 -0
  287. package/lib/board-search.test.mjs +110 -0
  288. package/lib/board-search.ts +63 -0
  289. package/lib/board-store.test.mjs +542 -0
  290. package/lib/board-store.ts +794 -0
  291. package/lib/board-types.ts +96 -0
  292. package/lib/board-utils.ts +17 -0
  293. package/lib/bounded-form-data.test.mjs +54 -0
  294. package/lib/bounded-form-data.ts +49 -0
  295. package/lib/browser-notifications.test.mjs +167 -0
  296. package/lib/browser-notifications.ts +104 -0
  297. package/lib/chat-lazy-load.test.mjs +140 -0
  298. package/lib/chat-lazy-load.ts +64 -0
  299. package/lib/clipboard-paths.test.mjs +140 -0
  300. package/lib/clipboard-paths.ts +192 -0
  301. package/lib/clipboard.ts +18 -0
  302. package/lib/compaction-summary.test.mjs +44 -0
  303. package/lib/compaction-summary.ts +34 -0
  304. package/lib/custom-ui-terminal.test.mjs +32 -0
  305. package/lib/custom-ui-terminal.ts +27 -0
  306. package/lib/deepseek-pricing.test.mjs +120 -0
  307. package/lib/deepseek-pricing.ts +71 -0
  308. package/lib/directory-browser.test.mjs +60 -0
  309. package/lib/directory-browser.ts +83 -0
  310. package/lib/draft-stash.test.mjs +91 -0
  311. package/lib/draft-stash.ts +102 -0
  312. package/lib/draft-store.ts +105 -0
  313. package/lib/dropdown-direction.ts +27 -0
  314. package/lib/extension-command.test.mjs +63 -0
  315. package/lib/extension-command.ts +81 -0
  316. package/lib/file-access.test.mjs +31 -0
  317. package/lib/file-access.ts +59 -0
  318. package/lib/file-dirent.test.mjs +56 -0
  319. package/lib/file-dirent.ts +17 -0
  320. package/lib/file-explorer-state.test.mjs +63 -0
  321. package/lib/file-explorer-state.ts +36 -0
  322. package/lib/file-fuzzy.test.mjs +29 -0
  323. package/lib/file-fuzzy.ts +189 -0
  324. package/lib/file-links.test.mjs +92 -0
  325. package/lib/file-links.ts +148 -0
  326. package/lib/file-paths.ts +43 -0
  327. package/lib/file-types.test.mjs +34 -0
  328. package/lib/file-types.ts +73 -0
  329. package/lib/file-upload.test.mjs +70 -0
  330. package/lib/file-upload.ts +59 -0
  331. package/lib/file-viewer-state.test.mjs +20 -0
  332. package/lib/file-viewer-state.ts +15 -0
  333. package/lib/frontmatter.test.mjs +55 -0
  334. package/lib/frontmatter.ts +87 -0
  335. package/lib/git-changes.test.mjs +52 -0
  336. package/lib/git-changes.ts +232 -0
  337. package/lib/git-status.ts +47 -0
  338. package/lib/git-types.ts +29 -0
  339. package/lib/http-dispatcher.test.mjs +83 -0
  340. package/lib/http-dispatcher.ts +86 -0
  341. package/lib/i18n/format.test.mjs +19 -0
  342. package/lib/i18n/format.ts +60 -0
  343. package/lib/i18n/messages/en.ts +662 -0
  344. package/lib/i18n/messages/zh-CN.ts +661 -0
  345. package/lib/i18n/registry.test.mjs +32 -0
  346. package/lib/i18n/registry.ts +43 -0
  347. package/lib/i18n/types.ts +15 -0
  348. package/lib/image-attachments.test.mjs +27 -0
  349. package/lib/image-attachments.ts +56 -0
  350. package/lib/initial-navigation.test.mjs +65 -0
  351. package/lib/initial-navigation.ts +15 -0
  352. package/lib/markdown.test.mjs +104 -0
  353. package/lib/markdown.ts +347 -0
  354. package/lib/message-display.test.mjs +130 -0
  355. package/lib/message-display.ts +47 -0
  356. package/lib/model-catalog.test.mjs +327 -0
  357. package/lib/model-catalog.ts +414 -0
  358. package/lib/model-discovery-auth.ts +58 -0
  359. package/lib/model-discovery.test.mjs +50 -0
  360. package/lib/model-discovery.ts +75 -0
  361. package/lib/model-id-preference.ts +46 -0
  362. package/lib/model-scope.test.mjs +193 -0
  363. package/lib/model-scope.ts +198 -0
  364. package/lib/models-cache.test.mjs +122 -0
  365. package/lib/models-cache.ts +90 -0
  366. package/lib/models-config-store.test.mjs +200 -0
  367. package/lib/models-config-store.ts +84 -0
  368. package/lib/next-config-esm.test.mjs +14 -0
  369. package/lib/next-config.test.mjs +13 -0
  370. package/lib/node-version.test.mjs +33 -0
  371. package/lib/normalize.test.mjs +54 -0
  372. package/lib/normalize.ts +63 -0
  373. package/lib/npx.ts +62 -0
  374. package/lib/panel-layout.test.mjs +56 -0
  375. package/lib/panel-layout.ts +52 -0
  376. package/lib/patch.test.mjs +110 -0
  377. package/lib/patch.ts +134 -0
  378. package/lib/path-security.ts +42 -0
  379. package/lib/paths.test.mjs +58 -0
  380. package/lib/paths.ts +71 -0
  381. package/lib/pi-types.ts +183 -0
  382. package/lib/pi-web-options.test.mjs +63 -0
  383. package/lib/process-lifecycle.test.mjs +83 -0
  384. package/lib/project-command-env.test.mjs +298 -0
  385. package/lib/project-command-env.ts +129 -0
  386. package/lib/project-groups.test.mjs +60 -0
  387. package/lib/project-groups.ts +53 -0
  388. package/lib/project-identity.test.mjs +37 -0
  389. package/lib/project-identity.ts +26 -0
  390. package/lib/project-tree.test.mjs +115 -0
  391. package/lib/project-tree.ts +176 -0
  392. package/lib/project-trust.test.mjs +117 -0
  393. package/lib/project-trust.ts +48 -0
  394. package/lib/prompt-recovery.test.mjs +33 -0
  395. package/lib/prompt-recovery.ts +47 -0
  396. package/lib/provider-api-key-route.test.mjs +12 -0
  397. package/lib/provider-credential-store.test.mjs +126 -0
  398. package/lib/provider-credential-store.ts +114 -0
  399. package/lib/provider-listing-runtime.ts +37 -0
  400. package/lib/provider-listing.test.mjs +114 -0
  401. package/lib/provider-listing.ts +118 -0
  402. package/lib/provider-services.ts +64 -0
  403. package/lib/request-security.test.mjs +256 -0
  404. package/lib/request-security.ts +136 -0
  405. package/lib/rpc-manager-shutdown.test.mjs +372 -0
  406. package/lib/rpc-manager-widgets.test.mjs +342 -0
  407. package/lib/rpc-manager.test.mjs +139 -0
  408. package/lib/rpc-manager.ts +1703 -0
  409. package/lib/rpc-session-info.test.mjs +93 -0
  410. package/lib/session-delete.test.mjs +53 -0
  411. package/lib/session-delete.ts +86 -0
  412. package/lib/session-file-references-core.ts +82 -0
  413. package/lib/session-file-references.test.mjs +91 -0
  414. package/lib/session-file-references.ts +29 -0
  415. package/lib/session-path.test.mjs +24 -0
  416. package/lib/session-path.ts +11 -0
  417. package/lib/session-reader.pagination.test.mjs +115 -0
  418. package/lib/session-reader.test.mjs +446 -0
  419. package/lib/session-reader.ts +548 -0
  420. package/lib/session-resolve.test.mjs +65 -0
  421. package/lib/session-row-context-menu.test.mjs +46 -0
  422. package/lib/session-row-context-menu.ts +29 -0
  423. package/lib/session-scanner.test.mjs +151 -0
  424. package/lib/session-scanner.ts +238 -0
  425. package/lib/session-search.test.mjs +173 -0
  426. package/lib/session-search.ts +284 -0
  427. package/lib/session-stats.ts +26 -0
  428. package/lib/session-timing.test.mjs +107 -0
  429. package/lib/session-timing.ts +45 -0
  430. package/lib/session-title.test.mjs +284 -0
  431. package/lib/session-title.ts +255 -0
  432. package/lib/skill-frontmatter.test.mjs +75 -0
  433. package/lib/skill-frontmatter.ts +40 -0
  434. package/lib/skill-lock.test.mjs +182 -0
  435. package/lib/skill-lock.ts +146 -0
  436. package/lib/skill-updates.test.mjs +182 -0
  437. package/lib/skill-updates.ts +263 -0
  438. package/lib/skills-service.ts +16 -0
  439. package/lib/slash-display.test.mjs +122 -0
  440. package/lib/slash-display.ts +30 -0
  441. package/lib/sqlite-db.test.mjs +140 -0
  442. package/lib/sqlite-db.ts +205 -0
  443. package/lib/startup-preferences.test.mjs +130 -0
  444. package/lib/startup-preferences.ts +65 -0
  445. package/lib/streaming-message.test.mjs +212 -0
  446. package/lib/streaming-message.ts +151 -0
  447. package/lib/subagent-widget.test.mjs +87 -0
  448. package/lib/subagent-widget.ts +109 -0
  449. package/lib/sync-room-server.mjs +137 -0
  450. package/lib/task-card-store.test.mjs +309 -0
  451. package/lib/task-card-store.ts +559 -0
  452. package/lib/task-scheduler.ts +498 -0
  453. package/lib/task-store.test.mjs +216 -0
  454. package/lib/task-store.ts +271 -0
  455. package/lib/terminal-input.test.mjs +46 -0
  456. package/lib/terminal-input.ts +63 -0
  457. package/lib/terminal-manager.test.mjs +71 -0
  458. package/lib/terminal-manager.ts +245 -0
  459. package/lib/tool-execution-progress.test.mjs +32 -0
  460. package/lib/tool-execution-progress.ts +29 -0
  461. package/lib/tool-names.ts +25 -0
  462. package/lib/tool-preset-preference.test.mjs +72 -0
  463. package/lib/tool-preset-preference.ts +41 -0
  464. package/lib/tool-presets.test.mjs +56 -0
  465. package/lib/tool-presets.ts +42 -0
  466. package/lib/turn-written-files.test.mjs +137 -0
  467. package/lib/turn-written-files.ts +60 -0
  468. package/lib/types.ts +349 -0
  469. package/lib/wallpaper-settings.ts +191 -0
  470. package/lib/web-auth.test.mjs +50 -0
  471. package/lib/web-auth.ts +45 -0
  472. package/lib/workspace-memory.test.mjs +129 -0
  473. package/lib/workspace-memory.ts +98 -0
  474. package/lib/worktree.test.mjs +50 -0
  475. package/lib/worktree.ts +250 -0
  476. package/package.json +3 -1
  477. package/server.mjs +34 -0
  478. package/.next/server/chunks/7513.js +0 -30
  479. package/.next/static/chunks/5315.03de3c6abd8b283e.js +0 -4
  480. package/.next/static/chunks/next/dist/client/components/builtin/global-error-00a4badaccb388e6.js +0 -1
  481. package/.next/static/css/cf25164cb7c04d95.css +0 -1
  482. /package/.next/static/{Ud6Y2ONkGuPVFbo1xZQvk → 7sDSz-wW-RWYnxh1l6-fD}/_buildManifest.js +0 -0
  483. /package/.next/static/{Ud6Y2ONkGuPVFbo1xZQvk → 7sDSz-wW-RWYnxh1l6-fD}/_ssgManifest.js +0 -0
@@ -0,0 +1,446 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import test from "node:test";
6
+ import { createJiti } from "jiti";
7
+
8
+ const jiti = createJiti(import.meta.url);
9
+ const { sessionPathKey } = await jiti.import("./session-path.ts");
10
+ const {
11
+ listAllSessions,
12
+ mergeSessionLists,
13
+ buildSessionContext,
14
+ cacheSessionPath,
15
+ invalidateSessionListCache,
16
+ invalidateSessionPathCache,
17
+ readSessionHeader,
18
+ resolveSessionIdByPath,
19
+ } = await jiti.import("./session-reader.ts");
20
+ const { SessionManager } = await jiti.import("@earendil-works/pi-coding-agent");
21
+ const scannerMod = await jiti.import("./session-scanner.ts");
22
+
23
+ function resetSessionListState() {
24
+ globalThis.__piSessionListCache = undefined;
25
+ globalThis.__piSessionListPromise = undefined;
26
+ globalThis.__piSessionListPromiseGeneration = undefined;
27
+ globalThis.__piSessionListGeneration = 0;
28
+ }
29
+
30
+ function userEntry(id, parentId, content, timestamp = "2026-01-01T00:00:00.000Z") {
31
+ return {
32
+ type: "message",
33
+ id,
34
+ parentId,
35
+ timestamp,
36
+ message: {
37
+ role: "user",
38
+ content,
39
+ },
40
+ };
41
+ }
42
+
43
+ function assistantEntry(id, parentId, text, timestamp = "2026-01-01T00:00:00.000Z") {
44
+ return {
45
+ type: "message",
46
+ id,
47
+ parentId,
48
+ timestamp,
49
+ message: {
50
+ role: "assistant",
51
+ provider: "test",
52
+ model: "test-model",
53
+ content: [{ type: "text", text }],
54
+ },
55
+ };
56
+ }
57
+
58
+ test("renders the SDK compaction-aware context with aligned entry IDs", () => {
59
+ const entries = [
60
+ userEntry("u1", null, "old user request"),
61
+ assistantEntry("a1", "u1", "old assistant answer"),
62
+ userEntry("u2", "a1", "kept user request"),
63
+ {
64
+ type: "compaction",
65
+ id: "cmp",
66
+ parentId: "u2",
67
+ timestamp: "2026-01-01T00:00:03.000Z",
68
+ summary: "old exchange summary",
69
+ firstKeptEntryId: "u2",
70
+ tokensBefore: 123,
71
+ },
72
+ userEntry("u3", "cmp", "after compaction"),
73
+ ];
74
+
75
+ const context = buildSessionContext(entries);
76
+
77
+ assert.deepEqual(context.entryIds, ["cmp", "u2", "u3"]);
78
+ // parentIds are file-authoritative tree parents (unaffected by the
79
+ // compaction-aware display projection). u3's rollback point is the
80
+ // compaction entry; u2's is the assistant before the compaction — the same
81
+ // leaves pi's navigateTree would resolve for those user messages.
82
+ assert.deepEqual(context.parentIds, ["u2", "a1", "cmp"]);
83
+ assert.deepEqual(context.parentIds.length, context.entryIds.length);
84
+ assert.deepEqual(
85
+ context.messages.map((message) => [message.role, message.customType, message.content]),
86
+ [
87
+ ["custom", "compaction", "old exchange summary"],
88
+ ["user", undefined, "kept user request"],
89
+ ["user", undefined, "after compaction"],
90
+ ],
91
+ );
92
+ });
93
+
94
+ test("uses only the latest compaction on the active path", () => {
95
+ const entries = [
96
+ userEntry("u1", null, "old request"),
97
+ assistantEntry("a1", "u1", "old answer"),
98
+ userEntry("u2", "a1", "first kept request"),
99
+ {
100
+ type: "compaction",
101
+ id: "cmp1",
102
+ parentId: "u2",
103
+ timestamp: "2026-01-01T00:00:03.000Z",
104
+ summary: "first summary",
105
+ firstKeptEntryId: "u2",
106
+ tokensBefore: 100,
107
+ },
108
+ assistantEntry("a2", "cmp1", "second kept answer"),
109
+ userEntry("u3", "a2", "second kept request"),
110
+ {
111
+ type: "compaction",
112
+ id: "cmp2",
113
+ parentId: "u3",
114
+ timestamp: "2026-01-01T00:00:06.000Z",
115
+ summary: "latest summary",
116
+ firstKeptEntryId: "a2",
117
+ tokensBefore: 200,
118
+ },
119
+ assistantEntry("a3", "cmp2", "latest answer"),
120
+ ];
121
+
122
+ const context = buildSessionContext(entries);
123
+
124
+ assert.deepEqual(context.entryIds, ["cmp2", "a2", "u3", "a3"]);
125
+ assert.equal(context.messages[0].role, "custom");
126
+ assert.equal(context.messages[0].content, "latest summary");
127
+ assert.equal(context.messages.length, context.entryIds.length);
128
+ });
129
+
130
+ test("uses the selected leaf's path before a later compaction", () => {
131
+ const entries = [
132
+ userEntry("u1", null, "root request"),
133
+ assistantEntry("a1", "u1", "root answer"),
134
+ userEntry("u2", "a1", "main branch"),
135
+ {
136
+ type: "compaction",
137
+ id: "cmp",
138
+ parentId: "u2",
139
+ timestamp: "2026-01-01T00:00:03.000Z",
140
+ summary: "main branch summary",
141
+ firstKeptEntryId: "u2",
142
+ tokensBefore: 100,
143
+ },
144
+ userEntry("alt", "a1", "alternate branch"),
145
+ ];
146
+
147
+ const context = buildSessionContext(entries, "alt");
148
+
149
+ assert.deepEqual(context.entryIds, ["u1", "a1", "alt"]);
150
+ assert.equal(context.messages.some((message) => message.role === "custom"), false);
151
+ });
152
+
153
+ test("returns an empty context for a null leaf", () => {
154
+ const context = buildSessionContext([
155
+ userEntry("u1", null, "not active"),
156
+ ], null);
157
+
158
+ assert.deepEqual(context.messages, []);
159
+ assert.deepEqual(context.entryIds, []);
160
+ assert.deepEqual(context.parentIds, []);
161
+ });
162
+
163
+ test("defers historical thinking without changing live-session content", () => {
164
+ const entries = [
165
+ userEntry("u1", null, "start"),
166
+ {
167
+ ...assistantEntry("a1", "u1", "answer"),
168
+ message: {
169
+ role: "assistant",
170
+ provider: "test",
171
+ model: "test-model",
172
+ content: [
173
+ { type: "thinking", thinking: "large reasoning" },
174
+ { type: "text", text: "answer" },
175
+ ],
176
+ },
177
+ },
178
+ ];
179
+
180
+ const deferred = buildSessionContext(entries, undefined, { deferThinking: true });
181
+ assert.deepEqual(deferred.messages[1].content[0], {
182
+ type: "thinking",
183
+ thinking: "",
184
+ deferred: true,
185
+ });
186
+
187
+ const full = buildSessionContext(entries);
188
+ assert.equal(full.messages[1].content[0].thinking, "large reasoning");
189
+ });
190
+
191
+ test("does not defer empty historical thinking blocks", () => {
192
+ const entries = [
193
+ userEntry("u1", null, "start"),
194
+ {
195
+ ...assistantEntry("a1", "u1", "answer"),
196
+ message: {
197
+ role: "assistant",
198
+ provider: "test",
199
+ model: "test-model",
200
+ content: [
201
+ { type: "thinking", thinking: "" },
202
+ { type: "text", text: "answer" },
203
+ ],
204
+ },
205
+ },
206
+ ];
207
+
208
+ const context = buildSessionContext(entries, undefined, { deferThinking: true });
209
+ assert.deepEqual(context.messages[1].content[0], { type: "thinking", thinking: "" });
210
+ });
211
+
212
+ test("defers only base64 images from historical tool results", () => {
213
+ const userImage = {
214
+ type: "image",
215
+ source: { type: "base64", media_type: "image/png", data: "QUJDRA==" },
216
+ };
217
+ const toolImage = {
218
+ type: "image",
219
+ source: { type: "base64", media_type: "image/jpeg", data: "QUJDRA==" },
220
+ };
221
+ const toolUrlImage = {
222
+ type: "image",
223
+ source: { type: "url", url: "https://example.com/result.png" },
224
+ };
225
+ const flatToolImage = {
226
+ type: "image",
227
+ data: "QUJDRA==",
228
+ mimeType: "image/png",
229
+ };
230
+ const entries = [
231
+ userEntry("u1", null, [{ type: "text", text: "inspect this" }, userImage]),
232
+ assistantEntry("a1", "u1", "reading"),
233
+ {
234
+ type: "message",
235
+ id: "tr1",
236
+ parentId: "a1",
237
+ timestamp: "2026-01-01T00:00:01.000Z",
238
+ message: {
239
+ role: "toolResult",
240
+ toolCallId: "call1",
241
+ content: [
242
+ { type: "text", text: "Read image file" },
243
+ toolImage,
244
+ flatToolImage,
245
+ toolUrlImage,
246
+ ],
247
+ },
248
+ },
249
+ ];
250
+
251
+ const deferred = buildSessionContext(entries, undefined, { deferToolResultImages: true });
252
+ assert.deepEqual(deferred.messages[0].content[1], userImage);
253
+ assert.deepEqual(deferred.messages[2].content[0], { type: "text", text: "Read image file" });
254
+ assert.deepEqual(deferred.messages[2].content[1], toolUrlImage);
255
+ assert.match(deferred.messages[2].content[2].text, /2 tool result images omitted.*image\/jpeg, image\/png.*~8 bytes/);
256
+
257
+ const full = buildSessionContext(entries);
258
+ assert.deepEqual(full.messages[2].content[1], toolImage);
259
+ assert.deepEqual(full.messages[2].content[2], flatToolImage);
260
+ assert.deepEqual(full.messages[2].content[3], toolUrlImage);
261
+ });
262
+
263
+ test("preserves hidden custom messages so the UI can render them collapsed", () => {
264
+ const entries = [
265
+ userEntry("u1", null, "start"),
266
+ {
267
+ type: "custom_message",
268
+ id: "c1",
269
+ parentId: "u1",
270
+ timestamp: "2026-01-01T00:00:01.000Z",
271
+ customType: "extension_debug",
272
+ content: "hidden extension payload",
273
+ display: false,
274
+ details: { source: "test" },
275
+ },
276
+ assistantEntry("a1", "c1", "done"),
277
+ ];
278
+
279
+ const context = buildSessionContext(entries);
280
+
281
+ assert.deepEqual(context.entryIds, ["u1", "c1", "a1"]);
282
+ assert.equal(context.messages[1].role, "custom");
283
+ assert.equal(context.messages[1].customType, "extension_debug");
284
+ assert.equal(context.messages[1].display, false);
285
+ assert.equal(context.messages[1].content, "hidden extension payload");
286
+ });
287
+
288
+ test("preserves valid epoch timestamps on synthetic UI messages", () => {
289
+ const entries = [
290
+ userEntry("u1", null, "start"),
291
+ {
292
+ type: "compaction",
293
+ id: "cmp",
294
+ parentId: "u1",
295
+ timestamp: "1970-01-01T00:00:00.000Z",
296
+ summary: "epoch summary",
297
+ firstKeptEntryId: "u1",
298
+ tokensBefore: 10,
299
+ },
300
+ ];
301
+
302
+ const context = buildSessionContext(entries);
303
+
304
+ assert.equal(context.messages[0].role, "custom");
305
+ assert.equal(context.messages[0].customType, "compaction");
306
+ assert.equal(context.messages[0].timestamp, 0);
307
+ });
308
+
309
+ test("reads only a bounded session header, including headers larger than 4 KiB", () => {
310
+ const dir = mkdtempSync(join(tmpdir(), "pi-web-header-"));
311
+ const filePath = join(dir, "session.jsonl");
312
+ const parentSession = `/tmp/${"p".repeat(5_000)}.jsonl`;
313
+ writeFileSync(filePath, `${JSON.stringify({
314
+ type: "session",
315
+ version: 3,
316
+ id: "session",
317
+ timestamp: "2026-01-01T00:00:00.000Z",
318
+ cwd: dir,
319
+ parentSession,
320
+ })}\n${JSON.stringify(userEntry("u1", null, "message"))}\n`);
321
+
322
+ try {
323
+ assert.equal(readSessionHeader(filePath)?.parentSession, parentSession);
324
+ } finally {
325
+ rmSync(dir, { recursive: true, force: true });
326
+ }
327
+ });
328
+
329
+ test("returns null for malformed or unbounded session headers", () => {
330
+ const dir = mkdtempSync(join(tmpdir(), "pi-web-header-invalid-"));
331
+ const malformedPath = join(dir, "malformed.jsonl");
332
+ const oversizedPath = join(dir, "oversized.jsonl");
333
+ writeFileSync(malformedPath, "{not-json}\n");
334
+ writeFileSync(oversizedPath, "x".repeat(64 * 1024));
335
+
336
+ try {
337
+ assert.equal(readSessionHeader(malformedPath), null);
338
+ assert.equal(readSessionHeader(oversizedPath), null);
339
+ } finally {
340
+ rmSync(dir, { recursive: true, force: true });
341
+ }
342
+ });
343
+
344
+ test("keeps forward and reverse session path caches in sync", async () => {
345
+ const sessionId = "cache-test-session";
346
+ const filePath = join(tmpdir(), "pi-web-cache-test", "..", "cache-test", "session.jsonl");
347
+
348
+ cacheSessionPath(sessionId, filePath);
349
+ try {
350
+ assert.equal(
351
+ await resolveSessionIdByPath(filePath),
352
+ sessionId,
353
+ );
354
+ } finally {
355
+ invalidateSessionPathCache(sessionId);
356
+ }
357
+
358
+ assert.equal(globalThis.__piSessionPathCache?.has(sessionId), false);
359
+ assert.equal(globalThis.__piPathToSessionIdCache?.has(sessionPathKey(filePath)), false);
360
+ });
361
+
362
+ test("forced session listing bypasses the fresh server cache", async (t) => {
363
+ const originalScan = scannerMod.sessionScanner.scan;
364
+ let scans = 0;
365
+ scannerMod.sessionScanner.scan = async () => {
366
+ scans += 1;
367
+ return [];
368
+ };
369
+ resetSessionListState();
370
+ t.after(() => {
371
+ scannerMod.sessionScanner.scan = originalScan;
372
+ resetSessionListState();
373
+ });
374
+
375
+ await listAllSessions({ force: true });
376
+ await listAllSessions();
377
+ assert.equal(scans, 1);
378
+
379
+ await listAllSessions({ force: true });
380
+ assert.equal(scans, 2);
381
+ });
382
+
383
+ test("a scan invalidated in flight retries before returning to its caller", async (t) => {
384
+ const originalScan = scannerMod.sessionScanner.scan;
385
+ let scans = 0;
386
+ let releaseFirstScan;
387
+ let markFirstScanStarted;
388
+ const firstScanStarted = new Promise((resolve) => {
389
+ markFirstScanStarted = resolve;
390
+ });
391
+ const firstScanGate = new Promise((resolve) => {
392
+ releaseFirstScan = resolve;
393
+ });
394
+ scannerMod.sessionScanner.scan = async () => {
395
+ scans += 1;
396
+ if (scans === 1) {
397
+ markFirstScanStarted();
398
+ await firstScanGate;
399
+ }
400
+ return [];
401
+ };
402
+ resetSessionListState();
403
+ t.after(() => {
404
+ scannerMod.sessionScanner.scan = originalScan;
405
+ resetSessionListState();
406
+ });
407
+
408
+ const listing = listAllSessions({ force: true });
409
+ await firstScanStarted;
410
+ invalidateSessionListCache();
411
+ releaseFirstScan();
412
+ await listing;
413
+
414
+ assert.equal(scans, 2);
415
+ });
416
+
417
+ test("disk sessions replace runtime snapshots with the same id", () => {
418
+ const base = {
419
+ path: "/tmp/session.jsonl",
420
+ id: "same-id",
421
+ cwd: "/tmp",
422
+ created: "2026-01-01T00:00:00.000Z",
423
+ modified: "2026-01-01T00:00:01.000Z",
424
+ messageCount: 2,
425
+ firstMessage: "persisted",
426
+ };
427
+ const persisted = { ...base };
428
+ const runtime = {
429
+ ...base,
430
+ path: "/tmp/not-written-yet.jsonl",
431
+ modified: "2026-01-01T00:00:02.000Z",
432
+ firstMessage: "runtime",
433
+ transient: true,
434
+ };
435
+ const runtimeOnly = {
436
+ ...runtime,
437
+ id: "runtime-only",
438
+ modified: "2026-01-01T00:00:03.000Z",
439
+ };
440
+
441
+ const merged = mergeSessionLists([persisted], [runtime, runtimeOnly]);
442
+
443
+ assert.deepEqual(merged.map((session) => session.id), ["runtime-only", "same-id"]);
444
+ assert.equal(merged[1], persisted);
445
+ assert.equal(merged[1].transient, undefined);
446
+ });