@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,140 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { createJiti } from "jiti";
4
+
5
+ const jiti = createJiti(import.meta.url);
6
+ const {
7
+ fileUriToPath,
8
+ extractPathsFromClipboardData,
9
+ formatPathsForInput,
10
+ } = await jiti.import("./clipboard-paths.ts");
11
+
12
+ test("fileUriToPath parses Unix file URIs", () => {
13
+ assert.equal(fileUriToPath("file:///home/user/project/file.txt"), "/home/user/project/file.txt");
14
+ assert.equal(fileUriToPath("file:///Users/john/Documents"), "/Users/john/Documents");
15
+ assert.equal(fileUriToPath("file://localhost/Users/john/Documents"), "/Users/john/Documents");
16
+ });
17
+
18
+ test("fileUriToPath parses Windows file URIs", () => {
19
+ assert.equal(fileUriToPath("file:///C:/Users/user/project/file.txt"), "C:/Users/user/project/file.txt");
20
+ assert.equal(fileUriToPath("file:///d:/repo/subfolder"), "d:/repo/subfolder");
21
+ assert.equal(fileUriToPath("file://server/share/file.txt"), "//server/share/file.txt");
22
+ });
23
+
24
+ test("fileUriToPath decodes percent-encoded characters and spaces", () => {
25
+ assert.equal(
26
+ fileUriToPath("file:///home/user/my%20folder/%E6%96%87%E4%BB%B6.txt"),
27
+ "/home/user/my folder/文件.txt"
28
+ );
29
+ assert.equal(
30
+ fileUriToPath("file:///C:/Program%20Files/App/test%20file.json"),
31
+ "C:/Program Files/App/test file.json"
32
+ );
33
+ });
34
+
35
+ test("fileUriToPath returns non-file URIs as trimmed strings", () => {
36
+ assert.equal(fileUriToPath(" /home/user/file.txt "), "/home/user/file.txt");
37
+ assert.equal(fileUriToPath("C:\\Users\\user\\file.txt"), "C:\\Users\\user\\file.txt");
38
+ });
39
+
40
+ test("extractPathsFromClipboardData extracts paths from File.path (Electron / Desktop)", () => {
41
+ const fakeClipboard = {
42
+ files: [
43
+ { path: "/home/user/project/a.txt", name: "a.txt" },
44
+ { path: "/home/user/project/b.txt", name: "b.txt" },
45
+ ],
46
+ items: [],
47
+ getData: () => "",
48
+ };
49
+
50
+ const paths = extractPathsFromClipboardData(fakeClipboard);
51
+ assert.deepEqual(paths, ["/home/user/project/a.txt", "/home/user/project/b.txt"]);
52
+ });
53
+
54
+ test("extractPathsFromClipboardData extracts paths from text/uri-list", () => {
55
+ const uriList = [
56
+ "# Comment line",
57
+ "file:///home/user/code/index.ts",
58
+ "file:///home/user/code/package.json",
59
+ ].join("\r\n");
60
+
61
+ const fakeClipboard = {
62
+ files: [],
63
+ items: [],
64
+ getData: (type) => (type === "text/uri-list" ? uriList : ""),
65
+ };
66
+
67
+ const paths = extractPathsFromClipboardData(fakeClipboard);
68
+ assert.deepEqual(paths, ["/home/user/code/index.ts", "/home/user/code/package.json"]);
69
+ });
70
+
71
+ test("extractPathsFromClipboardData extracts paths from Linux Nautilus/GNOME clipboard", () => {
72
+ const nautilusData = "copy\nfile:///home/user/photos/image.png\nfile:///home/user/photos/doc.pdf";
73
+
74
+ const fakeClipboard = {
75
+ files: [],
76
+ items: [],
77
+ getData: (type) => (type === "x-special/nautilus-clipboard" ? nautilusData : ""),
78
+ };
79
+
80
+ const paths = extractPathsFromClipboardData(fakeClipboard);
81
+ assert.deepEqual(paths, ["/home/user/photos/image.png", "/home/user/photos/doc.pdf"]);
82
+ });
83
+
84
+ test("extractPathsFromClipboardData extracts paths from macOS public.file-url", () => {
85
+ const fakeClipboard = {
86
+ files: [],
87
+ items: [],
88
+ getData: (type) => (type === "public.file-url" ? "file:///Users/name/Desktop/test.ts" : ""),
89
+ };
90
+
91
+ const paths = extractPathsFromClipboardData(fakeClipboard);
92
+ assert.deepEqual(paths, ["/Users/name/Desktop/test.ts"]);
93
+ });
94
+
95
+ test("extractPathsFromClipboardData extracts paths from text/plain with file:// URIs", () => {
96
+ const fakeClipboard = {
97
+ files: [],
98
+ items: [],
99
+ getData: (type) => (type === "text/plain" ? "file:///home/user/test.py" : ""),
100
+ };
101
+
102
+ const paths = extractPathsFromClipboardData(fakeClipboard);
103
+ assert.deepEqual(paths, ["/home/user/test.py"]);
104
+ });
105
+
106
+ test("extractPathsFromClipboardData handles null or empty clipboard data", () => {
107
+ assert.deepEqual(extractPathsFromClipboardData(null), []);
108
+ assert.deepEqual(
109
+ extractPathsFromClipboardData({
110
+ files: [],
111
+ items: [],
112
+ getData: () => "",
113
+ }),
114
+ []
115
+ );
116
+ });
117
+
118
+ test("extractPathsFromClipboardData supports fallbackToFileName", () => {
119
+ const fakeClipboard = {
120
+ files: [{ name: "myfile.zip" }],
121
+ items: [],
122
+ getData: () => "",
123
+ };
124
+
125
+ assert.deepEqual(extractPathsFromClipboardData(fakeClipboard, { fallbackToFileName: true }), ["myfile.zip"]);
126
+ assert.deepEqual(extractPathsFromClipboardData(fakeClipboard), []);
127
+ });
128
+
129
+ test("formatPathsForInput formats paths with quotes when containing spaces", () => {
130
+ assert.equal(formatPathsForInput([]), "");
131
+ assert.equal(formatPathsForInput(["/home/user/file.txt"]), "/home/user/file.txt");
132
+ assert.equal(
133
+ formatPathsForInput(["/home/user/my file.txt", "/home/user/other.txt"]),
134
+ '"/home/user/my file.txt" /home/user/other.txt'
135
+ );
136
+ assert.equal(
137
+ formatPathsForInput(['"already quoted"', "simple"]),
138
+ '"already quoted" simple'
139
+ );
140
+ });
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Helpers for extracting and formatting file paths from clipboard / drag-drop data.
3
+ * Supports Windows Explorer (CF_HDROP / File.path), macOS Finder (file-url / uri-list),
4
+ * and Linux file managers (Nautilus, Dolphin, Thunar, etc.).
5
+ */
6
+
7
+ /**
8
+ * Converts a file:// URI to a local file system path.
9
+ * Handles Windows drive letters, Unix paths, percent-encoding, and UNC network paths.
10
+ */
11
+ export function fileUriToPath(uri: string): string {
12
+ const trimmed = uri.trim();
13
+ if (!trimmed.startsWith("file://")) {
14
+ return trimmed;
15
+ }
16
+
17
+ try {
18
+ const parsed = new URL(trimmed);
19
+ let pathname = decodeURIComponent(parsed.pathname);
20
+
21
+ // On Windows, URL pathname for file:///C:/path is "/C:/path" -> "C:/path"
22
+ if (/^\/[a-zA-Z]:[\\/]/.test(pathname)) {
23
+ pathname = pathname.slice(1);
24
+ } else if (parsed.host && parsed.host !== "localhost") {
25
+ // UNC path: file://server/share/file -> //server/share/file
26
+ pathname = `//${parsed.host}${pathname}`;
27
+ }
28
+
29
+ return pathname;
30
+ } catch {
31
+ let raw = trimmed.replace(/^file:\/\/(localhost)?/, "");
32
+ try {
33
+ raw = decodeURIComponent(raw);
34
+ } catch {
35
+ // Ignore URI decode errors and use raw string
36
+ }
37
+ if (/^\/[a-zA-Z]:[\\/]/.test(raw)) {
38
+ raw = raw.slice(1);
39
+ }
40
+ return raw;
41
+ }
42
+ }
43
+
44
+ export interface ExtractPathsOptions {
45
+ /**
46
+ * If true and no full paths or URIs could be found,
47
+ * fall back to returning the `name` property of clipboard File objects.
48
+ */
49
+ fallbackToFileName?: boolean;
50
+ }
51
+
52
+ const LINUX_CLIPBOARD_TYPES = [
53
+ "text/uri-list",
54
+ "x-special/nautilus-clipboard",
55
+ "x-special/gnome-copied-files",
56
+ "x-special/mate-copied-files",
57
+ "x-special/xfce4-copied-files",
58
+ "application/x-kde-cutselection",
59
+ "public.file-url",
60
+ "text/x-moz-url",
61
+ "application/x-moz-file",
62
+ ];
63
+
64
+ function extractPathsFromText(text: string): string[] {
65
+ if (!text) return [];
66
+ const lines = text.split(/\r?\n/);
67
+ const paths: string[] = [];
68
+
69
+ for (const line of lines) {
70
+ const trimmed = line.trim();
71
+ if (!trimmed || trimmed.startsWith("#") || trimmed === "copy" || trimmed === "cut") {
72
+ continue;
73
+ }
74
+ if (trimmed.startsWith("file://")) {
75
+ paths.push(fileUriToPath(trimmed));
76
+ } else if (trimmed.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(trimmed)) {
77
+ paths.push(trimmed);
78
+ }
79
+ }
80
+
81
+ return paths;
82
+ }
83
+
84
+ /**
85
+ * Extracts file or directory paths from clipboard data or drag-and-drop DataTransfer.
86
+ */
87
+ export function extractPathsFromClipboardData(
88
+ clipboardData: DataTransfer | null,
89
+ options?: ExtractPathsOptions
90
+ ): string[] {
91
+ if (!clipboardData) return [];
92
+
93
+ const foundPaths: string[] = [];
94
+
95
+ // 1. Check for Electron / desktop runtime `File.path` property on files
96
+ const files = Array.from(clipboardData.files ?? []);
97
+ for (const file of files) {
98
+ const filePath = (file as unknown as { path?: unknown }).path;
99
+ if (typeof filePath === "string" && filePath.trim()) {
100
+ foundPaths.push(filePath.trim());
101
+ }
102
+ }
103
+
104
+ if (foundPaths.length > 0) {
105
+ return Array.from(new Set(foundPaths));
106
+ }
107
+
108
+ // 2. Check DataTransferItem.getAsFile() if files didn't have path
109
+ const items = Array.from(clipboardData.items ?? []);
110
+ for (const item of items) {
111
+ if (item.kind === "file") {
112
+ const file = item.getAsFile();
113
+ const filePath = (file as unknown as { path?: unknown })?.path;
114
+ if (typeof filePath === "string" && filePath.trim()) {
115
+ foundPaths.push(filePath.trim());
116
+ }
117
+ }
118
+ }
119
+
120
+ if (foundPaths.length > 0) {
121
+ return Array.from(new Set(foundPaths));
122
+ }
123
+
124
+ // 3. Check specialized MIME types (text/uri-list, Linux file managers, macOS file-url)
125
+ for (const mimeType of LINUX_CLIPBOARD_TYPES) {
126
+ try {
127
+ const data = clipboardData.getData(mimeType);
128
+ if (data) {
129
+ const paths = extractPathsFromText(data);
130
+ if (paths.length > 0) {
131
+ foundPaths.push(...paths);
132
+ }
133
+ }
134
+ } catch {
135
+ // Some browsers throw when accessing unsupported custom MIME types
136
+ }
137
+ }
138
+
139
+ if (foundPaths.length > 0) {
140
+ return Array.from(new Set(foundPaths));
141
+ }
142
+
143
+ // 4. Check text/plain if it contains file:// URIs
144
+ try {
145
+ const textPlain = clipboardData.getData("text/plain") || clipboardData.getData("text");
146
+ if (textPlain) {
147
+ const lines = textPlain.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
148
+ const fileUriLines = lines.filter((l) => l.startsWith("file://"));
149
+ if (fileUriLines.length > 0 && fileUriLines.length === lines.length) {
150
+ for (const uri of fileUriLines) {
151
+ foundPaths.push(fileUriToPath(uri));
152
+ }
153
+ }
154
+ }
155
+ } catch {
156
+ // Ignore data access error
157
+ }
158
+
159
+ if (foundPaths.length > 0) {
160
+ return Array.from(new Set(foundPaths));
161
+ }
162
+
163
+ // 5. Fallback to file names if enabled
164
+ if (options?.fallbackToFileName && files.length > 0) {
165
+ for (const file of files) {
166
+ if (file.name) {
167
+ foundPaths.push(file.name);
168
+ }
169
+ }
170
+ }
171
+
172
+ return Array.from(new Set(foundPaths));
173
+ }
174
+
175
+ /**
176
+ * Formats a list of paths into a string suitable for pasting into a text input or textarea.
177
+ * Quotes individual paths if they contain whitespace.
178
+ */
179
+ export function formatPathsForInput(paths: string[], delimiter = " "): string {
180
+ if (!paths || paths.length === 0) return "";
181
+ if (paths.length === 1) return paths[0];
182
+
183
+ return paths
184
+ .map((p) => {
185
+ const trimmed = p.trim();
186
+ if (/\s/.test(trimmed) && !trimmed.startsWith('"') && !trimmed.startsWith("'")) {
187
+ return `"${trimmed}"`;
188
+ }
189
+ return trimmed;
190
+ })
191
+ .join(delimiter);
192
+ }
@@ -0,0 +1,18 @@
1
+ export function copyText(text: string): Promise<void> {
2
+ if (navigator.clipboard?.writeText) {
3
+ return navigator.clipboard.writeText(text);
4
+ }
5
+ try {
6
+ const ta = document.createElement("textarea");
7
+ ta.value = text;
8
+ ta.style.position = "fixed";
9
+ ta.style.opacity = "0";
10
+ document.body.appendChild(ta);
11
+ ta.select();
12
+ document.execCommand("copy");
13
+ document.body.removeChild(ta);
14
+ return Promise.resolve();
15
+ } catch {
16
+ return Promise.reject();
17
+ }
18
+ }
@@ -0,0 +1,44 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { parseCompactionSummary } from "./compaction-summary.ts";
4
+
5
+ test("separates pi file metadata tags from the visible compaction summary", () => {
6
+ const parsed = parseCompactionSummary(`## Goal
7
+ Keep the important user intent.
8
+
9
+ <read-files>
10
+ /tmp/a.ts
11
+ /tmp/b.ts
12
+ </read-files>
13
+
14
+ <modified-files>
15
+ /tmp/changed.ts
16
+ </modified-files>`);
17
+
18
+ assert.equal(parsed.body, "## Goal\nKeep the important user intent.");
19
+ assert.deepEqual(parsed.readFiles, ["/tmp/a.ts", "/tmp/b.ts"]);
20
+ assert.deepEqual(parsed.modifiedFiles, ["/tmp/changed.ts"]);
21
+ });
22
+
23
+ test("leaves normal summaries unchanged", () => {
24
+ const summary = "## Goal\nNo file metadata here.";
25
+
26
+ assert.deepEqual(parseCompactionSummary(summary), {
27
+ body: summary,
28
+ readFiles: [],
29
+ modifiedFiles: [],
30
+ });
31
+ });
32
+
33
+ test("keeps file-like tags that are part of the summary body", () => {
34
+ const summary = `## Critical Context
35
+ The user asked what this compact metadata means: <read-files>example</read-files>.
36
+
37
+ More summary text after the mention.`;
38
+
39
+ assert.deepEqual(parseCompactionSummary(summary), {
40
+ body: summary,
41
+ readFiles: [],
42
+ modifiedFiles: [],
43
+ });
44
+ });
@@ -0,0 +1,34 @@
1
+ export interface ParsedCompactionSummary {
2
+ body: string;
3
+ readFiles: string[];
4
+ modifiedFiles: string[];
5
+ }
6
+
7
+ const FILE_SECTION_RE = /<(read-files|modified-files)>\s*([\s\S]*?)\s*<\/\1>/g;
8
+ const TRAILING_FILE_SECTIONS_RE = /(?:\r?\n){2,}((?:[ \t]*<(?:read-files|modified-files)>[ \t]*\r?\n[\s\S]*?\r?\n[ \t]*<\/(?:read-files|modified-files)>[ \t]*(?:\r?\n)?)+)\s*$/;
9
+
10
+ export function parseCompactionSummary(summary: string): ParsedCompactionSummary {
11
+ const readFiles: string[] = [];
12
+ const modifiedFiles: string[] = [];
13
+ const metadataMatch = summary.match(TRAILING_FILE_SECTIONS_RE);
14
+ const metadataBlock = metadataMatch?.[1] ?? "";
15
+ const body = metadataMatch?.index === undefined ? summary : summary.slice(0, metadataMatch.index);
16
+
17
+ metadataBlock.replace(FILE_SECTION_RE, (_match, section: string, content: string) => {
18
+ const files = content
19
+ .split(/\r?\n/)
20
+ .map((line) => line.trim())
21
+ .filter(Boolean);
22
+
23
+ if (section === "read-files") readFiles.push(...files);
24
+ else modifiedFiles.push(...files);
25
+
26
+ return "";
27
+ });
28
+
29
+ return {
30
+ body: body.trim(),
31
+ readFiles,
32
+ modifiedFiles,
33
+ };
34
+ }
@@ -0,0 +1,32 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ async function loadSubject() {
5
+ return import("./custom-ui-terminal.ts");
6
+ }
7
+
8
+ test("headless custom UI exposes stable terminal dimensions", async () => {
9
+ const { createHeadlessCustomUiTui, DEFAULT_CUSTOM_UI_COLUMNS, DEFAULT_CUSTOM_UI_ROWS } = await loadSubject();
10
+ const tui = createHeadlessCustomUiTui(() => {});
11
+
12
+ assert.deepEqual(tui.terminal, {
13
+ columns: DEFAULT_CUSTOM_UI_COLUMNS,
14
+ rows: DEFAULT_CUSTOM_UI_ROWS,
15
+ kittyProtocolActive: false,
16
+ });
17
+ assert.equal(Object.isFrozen(tui), true);
18
+ assert.equal(Object.isFrozen(tui.terminal), true);
19
+ });
20
+
21
+ test("headless custom UI supports plugin rendering and render requests", async () => {
22
+ const { createHeadlessCustomUiTui } = await loadSubject();
23
+ let renders = 0;
24
+ const tui = createHeadlessCustomUiTui(() => { renders += 1; }, 80, 24);
25
+ const pluginComponent = {
26
+ render: (width) => [`${width}:${tui.terminal.columns}x${tui.terminal.rows}`],
27
+ };
28
+
29
+ assert.deepEqual(pluginComponent.render(80), ["80:80x24"]);
30
+ tui.requestRender();
31
+ assert.equal(renders, 1);
32
+ });
@@ -0,0 +1,27 @@
1
+ export const DEFAULT_CUSTOM_UI_COLUMNS = 92;
2
+ export const DEFAULT_CUSTOM_UI_ROWS = 40;
3
+
4
+ export interface HeadlessCustomUiTerminal {
5
+ readonly columns: number;
6
+ readonly rows: number;
7
+ readonly kittyProtocolActive: false;
8
+ }
9
+
10
+ export interface HeadlessCustomUiTui {
11
+ readonly terminal: HeadlessCustomUiTerminal;
12
+ requestRender(force?: boolean): void;
13
+ }
14
+
15
+ export function createHeadlessCustomUiTui(
16
+ requestRender: (force?: boolean) => void,
17
+ columns = DEFAULT_CUSTOM_UI_COLUMNS,
18
+ rows = DEFAULT_CUSTOM_UI_ROWS,
19
+ ): HeadlessCustomUiTui {
20
+ const terminal = Object.freeze({
21
+ columns,
22
+ rows,
23
+ kittyProtocolActive: false as const,
24
+ });
25
+
26
+ return Object.freeze({ terminal, requestRender });
27
+ }
@@ -0,0 +1,120 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { createJiti } from "jiti";
4
+
5
+ const jiti = createJiti(import.meta.url, {
6
+ interopDefault: true,
7
+ moduleCache: false,
8
+ });
9
+ const { isDeepSeekPeak, nextDeepSeekSwitch, beijingTime } = await jiti.import("./deepseek-pricing.ts");
10
+
11
+ /** 用北京时间构造 UTC 时刻:北京 2026-08-26 09:30 → 传对应 UTC 的 Date */
12
+ function beijingDate(y, mo, d, h, mi = 0) {
13
+ const utc = Date.UTC(y, mo - 1, d, h, mi) - 8 * 3600_000;
14
+ return new Date(utc);
15
+ }
16
+
17
+ // 2026-08-24 是周一,08-30 是周日
18
+ test("工作日高峰时段判峰", () => {
19
+ // 周一 09:00 边界:峰窗开始
20
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 9, 0)), true);
21
+ // 周一 10:00 峰
22
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 10, 0)), true);
23
+ // 周一 11:59 峰
24
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 11, 59)), true);
25
+ // 周一 12:00 边界:峰窗结束,转谷
26
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 12, 0)), false);
27
+ // 周一 14:00 第二峰窗开始
28
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 14, 0)), true);
29
+ // 周一 17:59 峰
30
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 17, 59)), true);
31
+ // 周一 18:00 转谷
32
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 18, 0)), false);
33
+ // 周一 00:00 谷
34
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 0, 0)), false);
35
+ // 周一 08:00 谷
36
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 8, 0)), false);
37
+ // 周一 23:00 谷
38
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 23, 0)), false);
39
+ });
40
+
41
+ test("周末全天谷价(含原本 UTC 峰窗时刻)", () => {
42
+ // 周六 10:00 —— 旧逻辑(UTC 02:00 峰窗内)会误判峰,现在必须为谷
43
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 29, 10, 0)), false);
44
+ // 周六 16:00 —— 旧逻辑(UTC 08:00 峰窗内)误判峰
45
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 29, 16, 0)), false);
46
+ // 周日 09:30 —— 旧逻辑(UTC 01:30 峰窗内)误判峰
47
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 30, 9, 30)), false);
48
+ // 周日 23:00 谷
49
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 30, 23, 0)), false);
50
+ // 周六 00:00 谷
51
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 29, 0, 0)), false);
52
+ });
53
+
54
+ test("UTC 周末边界不错位(北京时刻与 UTC 星期可能不一致)", () => {
55
+ // 北京周六 08:00,对应 UTC 周六 00:00(day 6):必须判谷
56
+ const saturdayEarly = beijingDate(2026, 8, 29, 8, 0);
57
+ assert.equal(saturdayEarly.getUTCDay(), 6); // 确认 UTC 星期六
58
+ assert.equal(isDeepSeekPeak(saturdayEarly), false);
59
+ // 北京周六 00:00,对应 UTC 周五 16:00(day 5):UTC 是周五傍晚,但北京已是周六,必须判谷
60
+ const utcFridayEvening = beijingDate(2026, 8, 29, 0, 0);
61
+ assert.equal(utcFridayEvening.getUTCDay(), 5); // 确认 UTC 星期五
62
+ assert.equal(isDeepSeekPeak(utcFridayEvening), false);
63
+ // 北京周一 01:00,对应 UTC 周日 17:00:必须判谷(UTC 周日,非工作日)
64
+ const mondayEarly = beijingDate(2026, 8, 24, 1, 0);
65
+ assert.equal(mondayEarly.getUTCDay(), 0); // UTC 周日
66
+ assert.equal(isDeepSeekPeak(mondayEarly), false);
67
+ // 北京周一 09:00 对应 UTC 周一 01:00:峰
68
+ assert.equal(isDeepSeekPeak(beijingDate(2026, 8, 24, 9, 0)), true);
69
+ });
70
+
71
+ test("工作日 nextSwitch:峰时 → 峰窗结束转谷", () => {
72
+ // 周一 10:00 峰 → 12:00 转谷
73
+ const s1 = nextDeepSeekSwitch(beijingDate(2026, 8, 24, 10, 0));
74
+ assert.equal(beijingTime(s1).getUTCDate(), 24);
75
+ assert.equal(beijingTime(s1).getUTCHours(), 12);
76
+ // 周一 13:00 谷 → 14:00 转峰
77
+ const s2 = nextDeepSeekSwitch(beijingDate(2026, 8, 24, 13, 0));
78
+ assert.equal(beijingTime(s2).getUTCHours(), 14);
79
+ // 周一 17:59 峰 → 18:00 转谷
80
+ const s3 = nextDeepSeekSwitch(beijingDate(2026, 8, 24, 17, 59));
81
+ assert.equal(beijingTime(s3).getUTCHours(), 18);
82
+ // 周一 23:00 谷 → 周二 09:00 转峰
83
+ const s4 = nextDeepSeekSwitch(beijingDate(2026, 8, 24, 23, 0));
84
+ assert.equal(beijingTime(s4).getUTCDate(), 25);
85
+ assert.equal(beijingTime(s4).getUTCHours(), 9);
86
+ });
87
+
88
+ test("跨周末 nextSwitch:周五晚起全程谷,跳到周一 09:00 转峰", () => {
89
+ // 周五 17:00 峰 → 当天 18:00 转谷
90
+ const fridayPeak = nextDeepSeekSwitch(beijingDate(2026, 8, 28, 17, 0));
91
+ assert.equal(beijingTime(fridayPeak).getUTCDate(), 28);
92
+ assert.equal(beijingTime(fridayPeak).getUTCHours(), 18);
93
+ // 周五 20:00 谷 → 跳过周六周日 → 周一(8/31) 09:00 转峰
94
+ const fridayNight = nextDeepSeekSwitch(beijingDate(2026, 8, 28, 20, 0));
95
+ assert.equal(beijingTime(fridayNight).getUTCDate(), 31);
96
+ assert.equal(beijingTime(fridayNight).getUTCDay(), 1); // 周一
97
+ assert.equal(beijingTime(fridayNight).getUTCHours(), 9);
98
+ // 周六 10:00 谷 → 周一 09:00 转峰(旧实现会错误返回周六 09:00 或 14:00 转峰)
99
+ const saturday = nextDeepSeekSwitch(beijingDate(2026, 8, 29, 10, 0));
100
+ assert.equal(beijingTime(saturday).getUTCDate(), 31);
101
+ assert.equal(beijingTime(saturday).getUTCHours(), 9);
102
+ // 周日 15:00 谷 → 周一 09:00 转峰
103
+ const sunday = nextDeepSeekSwitch(beijingDate(2026, 8, 30, 15, 0));
104
+ assert.equal(beijingTime(sunday).getUTCDate(), 31);
105
+ assert.equal(beijingTime(sunday).getUTCHours(), 9);
106
+ // 周日 23:00 谷 → 周一 09:00 转峰
107
+ const sundayLate = nextDeepSeekSwitch(beijingDate(2026, 8, 30, 23, 0));
108
+ assert.equal(beijingTime(sundayLate).getUTCDate(), 31);
109
+ assert.equal(beijingTime(sundayLate).getUTCHours(), 9);
110
+ });
111
+
112
+ test("周五晚上正是峰时与谷时边界(北京 18:00 整点翻转)", () => {
113
+ // 周五 17:59:59 → 18:00 转谷
114
+ const before = nextDeepSeekSwitch(beijingDate(2026, 8, 28, 17, 59));
115
+ assert.equal(beijingTime(before).getUTCHours(), 18);
116
+ // 周五 18:00:00 整 → 已转谷,下一个切换是周一 09:00
117
+ const onBoundary = nextDeepSeekSwitch(beijingDate(2026, 8, 28, 18, 0));
118
+ assert.equal(beijingTime(onBoundary).getUTCDate(), 31);
119
+ assert.equal(beijingTime(onBoundary).getUTCHours(), 9);
120
+ });
@@ -0,0 +1,71 @@
1
+ /**
2
+ * DeepSeek 峰谷计价规则(服务端额度展示用)。
3
+ *
4
+ * 官方依据:
5
+ * - 定价页:https://api-docs.deepseek.com/zh-cn/quick_start/pricing/
6
+ * - 峰谷计费自 2026-08-17(北京时间 00:00)起生效;
7
+ * 2026-08-23 起周末(周六、周日)全天按低谷价计费(官方更新日志)。
8
+ *
9
+ * 规则(北京时间 UTC+8):
10
+ * - 工作日(周一~周五):高峰 09:00–12:00、14:00–18:00(左闭右开),其余时段谷(半价)
11
+ * - 周末(周六~周日):全天谷价,无峰谷切换
12
+ *
13
+ * 注意:判定必须统一换算到北京时间再取星期与时刻,不能用 UTC 直接判——
14
+ * 例如北京周六 08:00 对应 UTC 周五 24:00,按 UTC 星期会被误判为工作日峰时。
15
+ * 规则如有变动,改这里一处即可(并同步设计文档 .agent/spec/2026-08-26-quota-display-design.md)。
16
+ */
17
+
18
+ /** 北京时区相对 UTC 的毫秒偏移(无夏令时) */
19
+ export const DS_BEIJING_OFFSET_MS = 8 * 60 * 60 * 1000;
20
+
21
+ /** 工作日高峰时段(北京时间小时,左闭右开) */
22
+ export const DS_PEAK_HOURS: readonly [number, number][] = [
23
+ [9, 12],
24
+ [14, 18],
25
+ ];
26
+
27
+ /** 将 UTC 时刻平移为"北京时刻":用它的 UTC getter 读取即得北京时间 */
28
+ export function beijingTime(now: Date): Date {
29
+ return new Date(now.getTime() + DS_BEIJING_OFFSET_MS);
30
+ }
31
+
32
+ /** 北京时刻是否工作日(周一~周五);周末返回 false(全天谷价) */
33
+ function isBeijingWeekday(bj: Date): boolean {
34
+ const day = bj.getUTCDay(); // 0=周日 … 6=周六
35
+ return day >= 1 && day <= 5;
36
+ }
37
+
38
+ /** 当前是否为 DeepSeek 高峰时段(周末恒为谷,返回 false) */
39
+ export function isDeepSeekPeak(now: Date): boolean {
40
+ const bj = beijingTime(now);
41
+ if (!isBeijingWeekday(bj)) return false;
42
+ const h = bj.getUTCHours();
43
+ return DS_PEAK_HOURS.some(([start, end]) => h >= start && h < end);
44
+ }
45
+
46
+ /**
47
+ * 下一次峰谷切换时刻(切换恒发生在北京时间整点)。
48
+ * 周末全天谷无边界:跨周末时直接跳到下一个工作日首个峰窗开始(周一 09:00)。
49
+ */
50
+ export function nextDeepSeekSwitch(now: Date): Date {
51
+ const candidates: number[] = [];
52
+ // 北京视角向后看 8 天:连续两个周末最多 5 天无边界,8 天保证至少命中一个工作日
53
+ for (let dayOffset = 0; dayOffset < 8; dayOffset++) {
54
+ const bj = beijingTime(now);
55
+ bj.setUTCDate(bj.getUTCDate() + dayOffset);
56
+ if (!isBeijingWeekday(bj)) continue;
57
+ for (const [start, end] of DS_PEAK_HOURS) {
58
+ for (const hour of [start, end]) {
59
+ const bjBoundary = new Date(bj);
60
+ bjBoundary.setUTCHours(hour, 0, 0, 0);
61
+ const utcMs = bjBoundary.getTime() - DS_BEIJING_OFFSET_MS;
62
+ if (utcMs > now.getTime()) candidates.push(utcMs);
63
+ }
64
+ }
65
+ }
66
+ if (candidates.length === 0) {
67
+ // 兜底:8 天内必有工作日,正常走不到这里
68
+ return new Date(now.getTime() + 24 * 60 * 60 * 1000);
69
+ }
70
+ return new Date(Math.min(...candidates));
71
+ }