@baique/pi-web-sky 0.1.54 → 0.1.56

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 (509) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +14 -10
  3. package/.next/build-manifest.json +4 -4
  4. package/.next/next-server.js.nft.json +1 -1
  5. package/.next/prerender-manifest.json +6 -6
  6. package/.next/react-loadable-manifest.json +3 -3
  7. package/.next/required-server-files.js +6 -6
  8. package/.next/required-server-files.json +6 -6
  9. package/.next/routes-manifest.json +28 -0
  10. package/.next/server/app/_global-error/page.js +2 -2
  11. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/.next/server/app/_global-error.html +1 -1
  13. package/.next/server/app/_global-error.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  17. package/.next/server/app/_not-found/page.js +1 -1
  18. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/.next/server/app/_not-found.html +1 -1
  20. package/.next/server/app/_not-found.rsc +4 -3
  21. package/.next/server/app/_not-found.segments/_full.segment.rsc +4 -3
  22. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +4 -3
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +3 -2
  24. package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
  25. package/.next/server/app/api/agent/[id]/bash-output/route_client-reference-manifest.js +1 -1
  26. package/.next/server/app/api/agent/[id]/events/route.js +2 -2
  27. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
  28. package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/agent/[id]/route.js +1 -1
  30. package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
  31. package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/agent/new/route.js +1 -1
  33. package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
  34. package/.next/server/app/api/agent/new/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/agent/running/route.js +1 -1
  36. package/.next/server/app/api/agent/running/route.js.nft.json +1 -1
  37. package/.next/server/app/api/agent/running/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/app-update/route.js +1 -1
  39. package/.next/server/app/api/app-update/route_client-reference-manifest.js +1 -1
  40. package/.next/server/app/api/auth/all-providers/route.js +1 -1
  41. package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +1 -1
  42. package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
  43. package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +1 -1
  44. package/.next/server/app/api/auth/login/[provider]/route.js +1 -1
  45. package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +1 -1
  46. package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
  47. package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +1 -1
  48. package/.next/server/app/api/auth/providers/route.js +1 -1
  49. package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +1 -1
  50. package/.next/server/app/api/board-assets/[...path]/route.js +2 -2
  51. package/.next/server/app/api/board-assets/[...path]/route_client-reference-manifest.js +1 -1
  52. package/.next/server/app/api/board-assets/route.js +2 -2
  53. package/.next/server/app/api/board-assets/route_client-reference-manifest.js +1 -1
  54. package/.next/server/app/api/boards/[id]/add-session/route.js +1 -0
  55. package/.next/server/app/api/boards/[id]/add-session/route.js.nft.json +1 -0
  56. package/.next/server/app/api/boards/[id]/add-session/route_client-reference-manifest.js +1 -0
  57. package/.next/server/app/api/boards/[id]/reconcile/route.js +1 -1
  58. package/.next/server/app/api/boards/[id]/reconcile/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/boards/[id]/route.js +1 -1
  60. package/.next/server/app/api/boards/[id]/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/boards/reorder/route.js +2 -2
  62. package/.next/server/app/api/boards/reorder/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/boards/route.js +2 -2
  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 +3 -3
  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 +1 -1
  96. package/.next/server/app/api/project-trust/route.js.nft.json +1 -1
  97. package/.next/server/app/api/project-trust/route_client-reference-manifest.js +1 -1
  98. package/.next/server/app/api/quota/route.js +1 -1
  99. package/.next/server/app/api/quota/route_client-reference-manifest.js +1 -1
  100. package/.next/server/app/api/search/route.js +2 -2
  101. package/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  102. package/.next/server/app/api/sessions/[id]/auto-name/route.js +2 -2
  103. package/.next/server/app/api/sessions/[id]/auto-name/route.js.nft.json +1 -1
  104. package/.next/server/app/api/sessions/[id]/auto-name/route_client-reference-manifest.js +1 -1
  105. package/.next/server/app/api/sessions/[id]/context/route.js +1 -1
  106. package/.next/server/app/api/sessions/[id]/context/route.js.nft.json +1 -1
  107. package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +1 -1
  108. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
  109. package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route_client-reference-manifest.js +1 -1
  110. package/.next/server/app/api/sessions/[id]/export/route.js +3 -3
  111. package/.next/server/app/api/sessions/[id]/export/route_client-reference-manifest.js +1 -1
  112. package/.next/server/app/api/sessions/[id]/minimap/route.js +1 -1
  113. package/.next/server/app/api/sessions/[id]/minimap/route.js.nft.json +1 -1
  114. package/.next/server/app/api/sessions/[id]/minimap/route_client-reference-manifest.js +1 -1
  115. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  116. package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  117. package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -1
  118. package/.next/server/app/api/sessions/[id]/state/route.js +1 -1
  119. package/.next/server/app/api/sessions/[id]/state/route.js.nft.json +1 -1
  120. package/.next/server/app/api/sessions/[id]/state/route_client-reference-manifest.js +1 -1
  121. package/.next/server/app/api/sessions/[id]/todos/route.js +1 -1
  122. package/.next/server/app/api/sessions/[id]/todos/route.js.nft.json +1 -1
  123. package/.next/server/app/api/sessions/[id]/todos/route_client-reference-manifest.js +1 -1
  124. package/.next/server/app/api/sessions/route.js +1 -1
  125. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  126. package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
  127. package/.next/server/app/api/sessions/summary/route.js +2 -2
  128. package/.next/server/app/api/sessions/summary/route_client-reference-manifest.js +1 -1
  129. package/.next/server/app/api/skills/check/route.js +1 -1
  130. package/.next/server/app/api/skills/check/route_client-reference-manifest.js +1 -1
  131. package/.next/server/app/api/skills/install/route.js +2 -2
  132. package/.next/server/app/api/skills/install/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/api/skills/route.js +1 -1
  134. package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  135. package/.next/server/app/api/skills/search/route.js +1 -1
  136. package/.next/server/app/api/skills/search/route_client-reference-manifest.js +1 -1
  137. package/.next/server/app/api/skills/update/route.js +1 -1
  138. package/.next/server/app/api/skills/update/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/api/subagents/[id]/route.js +1 -0
  140. package/.next/server/app/api/subagents/[id]/route.js.nft.json +1 -0
  141. package/.next/server/app/api/subagents/[id]/route_client-reference-manifest.js +1 -0
  142. package/.next/server/app/api/subagents/profiles/route.js +30 -0
  143. package/.next/server/app/api/subagents/profiles/route.js.nft.json +1 -0
  144. package/.next/server/app/api/subagents/profiles/route_client-reference-manifest.js +1 -0
  145. package/.next/server/app/api/subagents/settings/route.js +1 -0
  146. package/.next/server/app/api/subagents/settings/route.js.nft.json +1 -0
  147. package/.next/server/app/api/subagents/settings/route_client-reference-manifest.js +1 -0
  148. package/.next/server/app/api/task-card-questions/[id]/answer/route.js +2 -2
  149. package/.next/server/app/api/task-card-questions/[id]/answer/route_client-reference-manifest.js +1 -1
  150. package/.next/server/app/api/task-card-questions/route.js +2 -2
  151. package/.next/server/app/api/task-card-questions/route_client-reference-manifest.js +1 -1
  152. package/.next/server/app/api/task-cards/[id]/route.js +1 -1
  153. package/.next/server/app/api/task-cards/[id]/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/api/task-cards/route.js +1 -1
  155. package/.next/server/app/api/task-cards/route_client-reference-manifest.js +1 -1
  156. package/.next/server/app/api/task-cards/unbind/route.js +3 -3
  157. package/.next/server/app/api/task-cards/unbind/route_client-reference-manifest.js +1 -1
  158. package/.next/server/app/api/task-scheduler/status/route.js +1 -1
  159. package/.next/server/app/api/task-scheduler/status/route.js.nft.json +1 -1
  160. package/.next/server/app/api/task-scheduler/status/route_client-reference-manifest.js +1 -1
  161. package/.next/server/app/api/tasks/[id]/assign-session/route.js +12 -12
  162. package/.next/server/app/api/tasks/[id]/assign-session/route_client-reference-manifest.js +1 -1
  163. package/.next/server/app/api/tasks/[id]/board/route.js +1 -1
  164. package/.next/server/app/api/tasks/[id]/board/route_client-reference-manifest.js +1 -1
  165. package/.next/server/app/api/tasks/[id]/route.js +1 -1
  166. package/.next/server/app/api/tasks/[id]/route.js.nft.json +1 -1
  167. package/.next/server/app/api/tasks/[id]/route_client-reference-manifest.js +1 -1
  168. package/.next/server/app/api/tasks/reorder/route.js +12 -12
  169. package/.next/server/app/api/tasks/reorder/route_client-reference-manifest.js +1 -1
  170. package/.next/server/app/api/tasks/route.js +12 -12
  171. package/.next/server/app/api/tasks/route_client-reference-manifest.js +1 -1
  172. package/.next/server/app/api/terminal/[id]/events/route.js +3 -3
  173. package/.next/server/app/api/terminal/[id]/events/route_client-reference-manifest.js +1 -1
  174. package/.next/server/app/api/terminal/[id]/route.js +2 -2
  175. package/.next/server/app/api/terminal/[id]/route_client-reference-manifest.js +1 -1
  176. package/.next/server/app/api/terminal/route.js +2 -2
  177. package/.next/server/app/api/terminal/route_client-reference-manifest.js +1 -1
  178. package/.next/server/app/api/worktrees/route.js +2 -2
  179. package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
  180. package/.next/server/app/api/yjs-version/route.js +1 -1
  181. package/.next/server/app/api/yjs-version/route_client-reference-manifest.js +1 -1
  182. package/.next/server/app/favicon.ico/route.js +1 -1
  183. package/.next/server/app/index.html +1 -1
  184. package/.next/server/app/index.rsc +6 -5
  185. package/.next/server/app/index.segments/__PAGE__.segment.rsc +6 -5
  186. package/.next/server/app/index.segments/_full.segment.rsc +6 -5
  187. package/.next/server/app/index.segments/_tree.segment.rsc +3 -2
  188. package/.next/server/app/manifest.webmanifest/route.js +2 -2
  189. package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
  190. package/.next/server/app/page.js +175 -8
  191. package/.next/server/app/page_client-reference-manifest.js +1 -1
  192. package/.next/server/app-paths-manifest.json +14 -10
  193. package/.next/server/chunks/1937.js +1 -1
  194. package/.next/server/chunks/2430.js +4 -4
  195. package/.next/server/chunks/3500.js +1 -1
  196. package/.next/server/chunks/402.js +13 -3
  197. package/.next/server/chunks/426.js +14 -0
  198. package/.next/server/chunks/4457.js +13 -13
  199. package/.next/server/chunks/542.js +23 -4
  200. package/.next/server/chunks/5794.js +2 -2
  201. package/.next/server/chunks/5940.js +1 -1
  202. package/.next/server/chunks/6429.js +15 -2
  203. package/.next/server/chunks/6525.js +12 -2
  204. package/.next/server/chunks/6583.js +1 -1
  205. package/.next/server/chunks/7048.js +6 -0
  206. package/.next/server/instrumentation.js +1 -1
  207. package/.next/server/middleware-build-manifest.js +1 -1
  208. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  209. package/.next/server/middleware.js +3 -3
  210. package/.next/server/next-font-manifest.js +1 -1
  211. package/.next/server/next-font-manifest.json +1 -1
  212. package/.next/server/pages/404.html +1 -1
  213. package/.next/server/pages/500.html +1 -1
  214. package/.next/server/server-reference-manifest.json +1 -1
  215. package/.next/static/chunks/752.adce8bc3ebbaf4b6.js +1 -0
  216. package/.next/static/chunks/8088.0cad1ff2e09fd8ff.js +1 -0
  217. package/.next/static/chunks/app/_global-error/page-ad6e695deb9de65c.js +1 -0
  218. package/.next/static/chunks/app/_not-found/page-63a39396532d2582.js +1 -0
  219. package/.next/static/chunks/app/api/agent/[id]/bash-output/route-ad6e695deb9de65c.js +1 -0
  220. package/.next/static/chunks/app/api/agent/[id]/events/route-ad6e695deb9de65c.js +1 -0
  221. package/.next/static/chunks/app/api/agent/[id]/route-ad6e695deb9de65c.js +1 -0
  222. package/.next/static/chunks/app/api/agent/new/route-ad6e695deb9de65c.js +1 -0
  223. package/.next/static/chunks/app/api/agent/running/route-ad6e695deb9de65c.js +1 -0
  224. package/.next/static/chunks/app/api/app-update/route-ad6e695deb9de65c.js +1 -0
  225. package/.next/static/chunks/app/api/auth/all-providers/route-ad6e695deb9de65c.js +1 -0
  226. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-ad6e695deb9de65c.js +1 -0
  227. package/.next/static/chunks/app/api/auth/login/[provider]/route-ad6e695deb9de65c.js +1 -0
  228. package/.next/static/chunks/app/api/auth/logout/[provider]/route-ad6e695deb9de65c.js +1 -0
  229. package/.next/static/chunks/app/api/auth/providers/route-ad6e695deb9de65c.js +1 -0
  230. package/.next/static/chunks/app/api/board-assets/[...path]/route-ad6e695deb9de65c.js +1 -0
  231. package/.next/static/chunks/app/api/board-assets/route-ad6e695deb9de65c.js +1 -0
  232. package/.next/static/chunks/app/api/boards/[id]/add-session/route-ad6e695deb9de65c.js +1 -0
  233. package/.next/static/chunks/app/api/boards/[id]/reconcile/route-ad6e695deb9de65c.js +1 -0
  234. package/.next/static/chunks/app/api/boards/[id]/route-ad6e695deb9de65c.js +1 -0
  235. package/.next/static/chunks/app/api/boards/reorder/route-ad6e695deb9de65c.js +1 -0
  236. package/.next/static/chunks/app/api/boards/route-ad6e695deb9de65c.js +1 -0
  237. package/.next/static/chunks/app/api/cwd/browse/route-ad6e695deb9de65c.js +1 -0
  238. package/.next/static/chunks/app/api/cwd/validate/route-ad6e695deb9de65c.js +1 -0
  239. package/.next/static/chunks/app/api/default-cwd/route-ad6e695deb9de65c.js +1 -0
  240. package/.next/static/chunks/app/api/file-index/route-ad6e695deb9de65c.js +1 -0
  241. package/.next/static/chunks/app/api/files/[...path]/route-ad6e695deb9de65c.js +1 -0
  242. package/.next/static/chunks/app/api/git/diff/route-ad6e695deb9de65c.js +1 -0
  243. package/.next/static/chunks/app/api/git/status/route-ad6e695deb9de65c.js +1 -0
  244. package/.next/static/chunks/app/api/home/route-ad6e695deb9de65c.js +1 -0
  245. package/.next/static/chunks/app/api/mcp/route-ad6e695deb9de65c.js +1 -0
  246. package/.next/static/chunks/app/api/models/route-ad6e695deb9de65c.js +1 -0
  247. package/.next/static/chunks/app/api/models-config/catalog/route-ad6e695deb9de65c.js +1 -0
  248. package/.next/static/chunks/app/api/models-config/discover/route-ad6e695deb9de65c.js +1 -0
  249. package/.next/static/chunks/app/api/models-config/route-ad6e695deb9de65c.js +1 -0
  250. package/.next/static/chunks/app/api/models-config/test/route-ad6e695deb9de65c.js +1 -0
  251. package/.next/static/chunks/app/api/plugins/route-ad6e695deb9de65c.js +1 -0
  252. package/.next/static/chunks/app/api/project-trust/route-ad6e695deb9de65c.js +1 -0
  253. package/.next/static/chunks/app/api/quota/route-ad6e695deb9de65c.js +1 -0
  254. package/.next/static/chunks/app/api/search/route-ad6e695deb9de65c.js +1 -0
  255. package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-ad6e695deb9de65c.js +1 -0
  256. package/.next/static/chunks/app/api/sessions/[id]/context/route-ad6e695deb9de65c.js +1 -0
  257. package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-ad6e695deb9de65c.js +1 -0
  258. package/.next/static/chunks/app/api/sessions/[id]/export/route-ad6e695deb9de65c.js +1 -0
  259. package/.next/static/chunks/app/api/sessions/[id]/minimap/route-ad6e695deb9de65c.js +1 -0
  260. package/.next/static/chunks/app/api/sessions/[id]/route-ad6e695deb9de65c.js +1 -0
  261. package/.next/static/chunks/app/api/sessions/[id]/state/route-ad6e695deb9de65c.js +1 -0
  262. package/.next/static/chunks/app/api/sessions/[id]/todos/route-ad6e695deb9de65c.js +1 -0
  263. package/.next/static/chunks/app/api/sessions/route-ad6e695deb9de65c.js +1 -0
  264. package/.next/static/chunks/app/api/sessions/summary/route-ad6e695deb9de65c.js +1 -0
  265. package/.next/static/chunks/app/api/skills/check/route-ad6e695deb9de65c.js +1 -0
  266. package/.next/static/chunks/app/api/skills/install/route-ad6e695deb9de65c.js +1 -0
  267. package/.next/static/chunks/app/api/skills/route-ad6e695deb9de65c.js +1 -0
  268. package/.next/static/chunks/app/api/skills/search/route-ad6e695deb9de65c.js +1 -0
  269. package/.next/static/chunks/app/api/skills/update/route-ad6e695deb9de65c.js +1 -0
  270. package/.next/static/chunks/app/api/subagents/[id]/route-ad6e695deb9de65c.js +1 -0
  271. package/.next/static/chunks/app/api/subagents/profiles/route-ad6e695deb9de65c.js +1 -0
  272. package/.next/static/chunks/app/api/subagents/settings/route-ad6e695deb9de65c.js +1 -0
  273. package/.next/static/chunks/app/api/task-card-questions/[id]/answer/route-ad6e695deb9de65c.js +1 -0
  274. package/.next/static/chunks/app/api/task-card-questions/route-ad6e695deb9de65c.js +1 -0
  275. package/.next/static/chunks/app/api/task-cards/[id]/route-ad6e695deb9de65c.js +1 -0
  276. package/.next/static/chunks/app/api/task-cards/route-ad6e695deb9de65c.js +1 -0
  277. package/.next/static/chunks/app/api/task-cards/unbind/route-ad6e695deb9de65c.js +1 -0
  278. package/.next/static/chunks/app/api/task-scheduler/status/route-ad6e695deb9de65c.js +1 -0
  279. package/.next/static/chunks/app/api/tasks/[id]/assign-session/route-ad6e695deb9de65c.js +1 -0
  280. package/.next/static/chunks/app/api/tasks/[id]/board/route-ad6e695deb9de65c.js +1 -0
  281. package/.next/static/chunks/app/api/tasks/[id]/route-ad6e695deb9de65c.js +1 -0
  282. package/.next/static/chunks/app/api/tasks/reorder/route-ad6e695deb9de65c.js +1 -0
  283. package/.next/static/chunks/app/api/tasks/route-ad6e695deb9de65c.js +1 -0
  284. package/.next/static/chunks/app/api/terminal/[id]/events/route-ad6e695deb9de65c.js +1 -0
  285. package/.next/static/chunks/app/api/terminal/[id]/route-ad6e695deb9de65c.js +1 -0
  286. package/.next/static/chunks/app/api/terminal/route-ad6e695deb9de65c.js +1 -0
  287. package/.next/static/chunks/app/api/worktrees/route-ad6e695deb9de65c.js +1 -0
  288. package/.next/static/chunks/app/api/yjs-version/route-ad6e695deb9de65c.js +1 -0
  289. package/.next/static/chunks/app/layout-87087b955e0c9507.js +1 -0
  290. package/.next/static/chunks/app/manifest.webmanifest/route-ad6e695deb9de65c.js +1 -0
  291. package/.next/static/chunks/app/page-efca6bf51a5f8ccb.js +256 -0
  292. package/.next/static/chunks/main-app-1f89e7ad7ae1ff87.js +1 -0
  293. package/.next/static/chunks/next/dist/client/components/builtin/app-error-ad6e695deb9de65c.js +1 -0
  294. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-ad6e695deb9de65c.js +1 -0
  295. package/.next/static/chunks/next/dist/client/components/builtin/global-error-d3784ab2e32f3c6e.js +1 -0
  296. package/.next/static/chunks/next/dist/client/components/builtin/not-found-ad6e695deb9de65c.js +1 -0
  297. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-ad6e695deb9de65c.js +1 -0
  298. package/.next/static/chunks/{webpack-3df5ae3897959c33.js → webpack-4de8e8dd9f2f3173.js} +1 -1
  299. package/.next/static/css/bb9361c7f832a89d.css +1 -0
  300. package/.next/static/css/c6d32b6031181be7.css +1 -0
  301. package/.next/static/k8ZFqwz7d-bnK0ZiUVgCc/_buildManifest.js +1 -0
  302. package/.next/trace +7 -8
  303. package/.next/trace-build +1 -1
  304. package/.next/types/app/api/agent/[id]/bash-output/route.ts +1 -1
  305. package/.next/types/app/api/agent/[id]/events/route.ts +1 -1
  306. package/.next/types/app/api/agent/[id]/route.ts +1 -1
  307. package/.next/types/app/api/agent/new/route.ts +1 -1
  308. package/.next/types/app/api/agent/running/route.ts +1 -1
  309. package/.next/types/app/api/app-update/route.ts +1 -1
  310. package/.next/types/app/api/auth/all-providers/route.ts +1 -1
  311. package/.next/types/app/api/auth/api-key/[provider]/route.ts +1 -1
  312. package/.next/types/app/api/auth/login/[provider]/route.ts +1 -1
  313. package/.next/types/app/api/auth/logout/[provider]/route.ts +1 -1
  314. package/.next/types/app/api/auth/providers/route.ts +1 -1
  315. package/.next/types/app/api/board-assets/[...path]/route.ts +1 -1
  316. package/.next/types/app/api/board-assets/route.ts +1 -1
  317. package/.next/types/app/api/boards/[id]/add-session/route.ts +353 -0
  318. package/.next/types/app/api/boards/[id]/reconcile/route.ts +1 -1
  319. package/.next/types/app/api/boards/[id]/route.ts +1 -1
  320. package/.next/types/app/api/boards/reorder/route.ts +1 -1
  321. package/.next/types/app/api/boards/route.ts +1 -1
  322. package/.next/types/app/api/cwd/browse/route.ts +1 -1
  323. package/.next/types/app/api/cwd/validate/route.ts +1 -1
  324. package/.next/types/app/api/default-cwd/route.ts +1 -1
  325. package/.next/types/app/api/file-index/route.ts +1 -1
  326. package/.next/types/app/api/files/[...path]/route.ts +1 -1
  327. package/.next/types/app/api/git/diff/route.ts +1 -1
  328. package/.next/types/app/api/git/status/route.ts +1 -1
  329. package/.next/types/app/api/home/route.ts +1 -1
  330. package/.next/types/app/api/mcp/route.ts +1 -1
  331. package/.next/types/app/api/models/route.ts +1 -1
  332. package/.next/types/app/api/models-config/catalog/route.ts +1 -1
  333. package/.next/types/app/api/models-config/discover/route.ts +1 -1
  334. package/.next/types/app/api/models-config/route.ts +1 -1
  335. package/.next/types/app/api/models-config/test/route.ts +1 -1
  336. package/.next/types/app/api/plugins/route.ts +1 -1
  337. package/.next/types/app/api/project-trust/route.ts +1 -1
  338. package/.next/types/app/api/quota/route.ts +1 -1
  339. package/.next/types/app/api/search/route.ts +1 -1
  340. package/.next/types/app/api/sessions/[id]/auto-name/route.ts +1 -1
  341. package/.next/types/app/api/sessions/[id]/context/route.ts +1 -1
  342. package/.next/types/app/api/sessions/[id]/entries/[entryId]/thinking/route.ts +1 -1
  343. package/.next/types/app/api/sessions/[id]/export/route.ts +1 -1
  344. package/.next/types/app/api/sessions/[id]/minimap/route.ts +1 -1
  345. package/.next/types/app/api/sessions/[id]/route.ts +1 -1
  346. package/.next/types/app/api/sessions/[id]/state/route.ts +1 -1
  347. package/.next/types/app/api/sessions/[id]/todos/route.ts +1 -1
  348. package/.next/types/app/api/sessions/route.ts +1 -1
  349. package/.next/types/app/api/sessions/summary/route.ts +1 -1
  350. package/.next/types/app/api/skills/check/route.ts +1 -1
  351. package/.next/types/app/api/skills/install/route.ts +1 -1
  352. package/.next/types/app/api/skills/route.ts +1 -1
  353. package/.next/types/app/api/skills/search/route.ts +1 -1
  354. package/.next/types/app/api/skills/update/route.ts +1 -1
  355. package/.next/types/app/api/subagents/[id]/route.ts +353 -0
  356. package/.next/types/app/api/subagents/profiles/route.ts +353 -0
  357. package/.next/types/app/api/subagents/settings/route.ts +353 -0
  358. package/.next/types/app/api/task-card-questions/[id]/answer/route.ts +1 -1
  359. package/.next/types/app/api/task-card-questions/route.ts +1 -1
  360. package/.next/types/app/api/task-cards/[id]/route.ts +1 -1
  361. package/.next/types/app/api/task-cards/route.ts +1 -1
  362. package/.next/types/app/api/task-cards/unbind/route.ts +1 -1
  363. package/.next/types/app/api/task-scheduler/status/route.ts +1 -1
  364. package/.next/types/app/api/tasks/[id]/assign-session/route.ts +1 -1
  365. package/.next/types/app/api/tasks/[id]/board/route.ts +1 -1
  366. package/.next/types/app/api/tasks/[id]/route.ts +1 -1
  367. package/.next/types/app/api/tasks/reorder/route.ts +1 -1
  368. package/.next/types/app/api/tasks/route.ts +1 -1
  369. package/.next/types/app/api/terminal/[id]/events/route.ts +1 -1
  370. package/.next/types/app/api/terminal/[id]/route.ts +1 -1
  371. package/.next/types/app/api/terminal/route.ts +1 -1
  372. package/.next/types/app/api/worktrees/route.ts +1 -1
  373. package/.next/types/app/api/yjs-version/route.ts +1 -1
  374. package/.next/types/app/layout.ts +1 -1
  375. package/.next/types/app/page.ts +1 -1
  376. package/.next/types/routes.d.ts +5 -1
  377. package/.next/types/validator.ts +36 -0
  378. package/lib/api-types.ts +10 -0
  379. package/lib/board-events.rename.test.mjs +39 -0
  380. package/lib/board-events.ts +17 -0
  381. package/lib/board-reconcile.test.mjs +20 -0
  382. package/lib/board-reconcile.ts +54 -0
  383. package/lib/chat-lazy-load.test.mjs +0 -62
  384. package/lib/chat-lazy-load.ts +0 -13
  385. package/lib/chat-only.ts +23 -0
  386. package/lib/draft-stash.ts +4 -5
  387. package/lib/file-types.ts +16 -1
  388. package/lib/i18n/messages/en.ts +91 -0
  389. package/lib/i18n/messages/zh-CN.ts +91 -0
  390. package/lib/id.test.mjs +38 -0
  391. package/lib/id.ts +17 -0
  392. package/lib/message-text.ts +24 -0
  393. package/lib/models-cache.ts +1 -1
  394. package/lib/pi-types.ts +19 -1
  395. package/lib/pin-store.ts +85 -0
  396. package/lib/powershell-settings.ts +106 -0
  397. package/lib/quoted-selection.ts +4 -0
  398. package/lib/rpc-manager.ts +208 -22
  399. package/lib/session-family.test.mjs +49 -0
  400. package/lib/session-family.ts +80 -0
  401. package/lib/session-index-scanner.ts +39 -0
  402. package/lib/session-time-group.test.mjs +38 -0
  403. package/lib/session-time-group.ts +55 -0
  404. package/lib/settings-navigation.ts +124 -0
  405. package/lib/subagent-extension.test.mjs +424 -0
  406. package/lib/subagent-extension.ts +309 -0
  407. package/lib/subagent-input.test.mjs +63 -0
  408. package/lib/subagent-input.ts +71 -0
  409. package/lib/subagent-isolation.test.mjs +51 -0
  410. package/lib/subagent-profile-precedence.ts +20 -0
  411. package/lib/subagent-prompt.test.mjs +64 -0
  412. package/lib/subagent-prompt.ts +31 -0
  413. package/lib/subagent-queue.test.mjs +35 -0
  414. package/lib/subagent-queue.ts +74 -0
  415. package/lib/subagent-runtime.test.mjs +150 -0
  416. package/lib/subagent-runtime.ts +587 -0
  417. package/lib/subagent-settings.test.mjs +50 -0
  418. package/lib/subagent-settings.ts +93 -0
  419. package/lib/subagents.test.mjs +601 -0
  420. package/lib/subagents.ts +602 -0
  421. package/lib/tool-presets.ts +4 -0
  422. package/lib/types.ts +19 -0
  423. package/package.json +1 -1
  424. package/.next/static/chunks/752.3119700876725f3b.js +0 -1
  425. package/.next/static/chunks/7975.95edcabc69eee4eb.js +0 -1
  426. package/.next/static/chunks/app/_global-error/page-2d5f6296c7e693c6.js +0 -1
  427. package/.next/static/chunks/app/_not-found/page-af4fe23994cb3d66.js +0 -1
  428. package/.next/static/chunks/app/api/agent/[id]/bash-output/route-2d5f6296c7e693c6.js +0 -1
  429. package/.next/static/chunks/app/api/agent/[id]/events/route-2d5f6296c7e693c6.js +0 -1
  430. package/.next/static/chunks/app/api/agent/[id]/route-2d5f6296c7e693c6.js +0 -1
  431. package/.next/static/chunks/app/api/agent/new/route-2d5f6296c7e693c6.js +0 -1
  432. package/.next/static/chunks/app/api/agent/running/route-2d5f6296c7e693c6.js +0 -1
  433. package/.next/static/chunks/app/api/app-update/route-2d5f6296c7e693c6.js +0 -1
  434. package/.next/static/chunks/app/api/auth/all-providers/route-2d5f6296c7e693c6.js +0 -1
  435. package/.next/static/chunks/app/api/auth/api-key/[provider]/route-2d5f6296c7e693c6.js +0 -1
  436. package/.next/static/chunks/app/api/auth/login/[provider]/route-2d5f6296c7e693c6.js +0 -1
  437. package/.next/static/chunks/app/api/auth/logout/[provider]/route-2d5f6296c7e693c6.js +0 -1
  438. package/.next/static/chunks/app/api/auth/providers/route-2d5f6296c7e693c6.js +0 -1
  439. package/.next/static/chunks/app/api/board-assets/[...path]/route-2d5f6296c7e693c6.js +0 -1
  440. package/.next/static/chunks/app/api/board-assets/route-2d5f6296c7e693c6.js +0 -1
  441. package/.next/static/chunks/app/api/boards/[id]/reconcile/route-2d5f6296c7e693c6.js +0 -1
  442. package/.next/static/chunks/app/api/boards/[id]/route-2d5f6296c7e693c6.js +0 -1
  443. package/.next/static/chunks/app/api/boards/reorder/route-2d5f6296c7e693c6.js +0 -1
  444. package/.next/static/chunks/app/api/boards/route-2d5f6296c7e693c6.js +0 -1
  445. package/.next/static/chunks/app/api/cwd/browse/route-2d5f6296c7e693c6.js +0 -1
  446. package/.next/static/chunks/app/api/cwd/validate/route-2d5f6296c7e693c6.js +0 -1
  447. package/.next/static/chunks/app/api/default-cwd/route-2d5f6296c7e693c6.js +0 -1
  448. package/.next/static/chunks/app/api/file-index/route-2d5f6296c7e693c6.js +0 -1
  449. package/.next/static/chunks/app/api/files/[...path]/route-2d5f6296c7e693c6.js +0 -1
  450. package/.next/static/chunks/app/api/git/diff/route-2d5f6296c7e693c6.js +0 -1
  451. package/.next/static/chunks/app/api/git/status/route-2d5f6296c7e693c6.js +0 -1
  452. package/.next/static/chunks/app/api/home/route-2d5f6296c7e693c6.js +0 -1
  453. package/.next/static/chunks/app/api/mcp/route-2d5f6296c7e693c6.js +0 -1
  454. package/.next/static/chunks/app/api/models/route-2d5f6296c7e693c6.js +0 -1
  455. package/.next/static/chunks/app/api/models-config/catalog/route-2d5f6296c7e693c6.js +0 -1
  456. package/.next/static/chunks/app/api/models-config/discover/route-2d5f6296c7e693c6.js +0 -1
  457. package/.next/static/chunks/app/api/models-config/route-2d5f6296c7e693c6.js +0 -1
  458. package/.next/static/chunks/app/api/models-config/test/route-2d5f6296c7e693c6.js +0 -1
  459. package/.next/static/chunks/app/api/plugins/route-2d5f6296c7e693c6.js +0 -1
  460. package/.next/static/chunks/app/api/project-trust/route-2d5f6296c7e693c6.js +0 -1
  461. package/.next/static/chunks/app/api/quota/route-2d5f6296c7e693c6.js +0 -1
  462. package/.next/static/chunks/app/api/search/route-2d5f6296c7e693c6.js +0 -1
  463. package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-2d5f6296c7e693c6.js +0 -1
  464. package/.next/static/chunks/app/api/sessions/[id]/context/route-2d5f6296c7e693c6.js +0 -1
  465. package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-2d5f6296c7e693c6.js +0 -1
  466. package/.next/static/chunks/app/api/sessions/[id]/export/route-2d5f6296c7e693c6.js +0 -1
  467. package/.next/static/chunks/app/api/sessions/[id]/minimap/route-2d5f6296c7e693c6.js +0 -1
  468. package/.next/static/chunks/app/api/sessions/[id]/route-2d5f6296c7e693c6.js +0 -1
  469. package/.next/static/chunks/app/api/sessions/[id]/state/route-2d5f6296c7e693c6.js +0 -1
  470. package/.next/static/chunks/app/api/sessions/[id]/todos/route-2d5f6296c7e693c6.js +0 -1
  471. package/.next/static/chunks/app/api/sessions/route-2d5f6296c7e693c6.js +0 -1
  472. package/.next/static/chunks/app/api/sessions/summary/route-2d5f6296c7e693c6.js +0 -1
  473. package/.next/static/chunks/app/api/skills/check/route-2d5f6296c7e693c6.js +0 -1
  474. package/.next/static/chunks/app/api/skills/install/route-2d5f6296c7e693c6.js +0 -1
  475. package/.next/static/chunks/app/api/skills/route-2d5f6296c7e693c6.js +0 -1
  476. package/.next/static/chunks/app/api/skills/search/route-2d5f6296c7e693c6.js +0 -1
  477. package/.next/static/chunks/app/api/skills/update/route-2d5f6296c7e693c6.js +0 -1
  478. package/.next/static/chunks/app/api/task-card-questions/[id]/answer/route-2d5f6296c7e693c6.js +0 -1
  479. package/.next/static/chunks/app/api/task-card-questions/route-2d5f6296c7e693c6.js +0 -1
  480. package/.next/static/chunks/app/api/task-cards/[id]/route-2d5f6296c7e693c6.js +0 -1
  481. package/.next/static/chunks/app/api/task-cards/route-2d5f6296c7e693c6.js +0 -1
  482. package/.next/static/chunks/app/api/task-cards/unbind/route-2d5f6296c7e693c6.js +0 -1
  483. package/.next/static/chunks/app/api/task-scheduler/status/route-2d5f6296c7e693c6.js +0 -1
  484. package/.next/static/chunks/app/api/tasks/[id]/assign-session/route-2d5f6296c7e693c6.js +0 -1
  485. package/.next/static/chunks/app/api/tasks/[id]/board/route-2d5f6296c7e693c6.js +0 -1
  486. package/.next/static/chunks/app/api/tasks/[id]/route-2d5f6296c7e693c6.js +0 -1
  487. package/.next/static/chunks/app/api/tasks/reorder/route-2d5f6296c7e693c6.js +0 -1
  488. package/.next/static/chunks/app/api/tasks/route-2d5f6296c7e693c6.js +0 -1
  489. package/.next/static/chunks/app/api/terminal/[id]/events/route-2d5f6296c7e693c6.js +0 -1
  490. package/.next/static/chunks/app/api/terminal/[id]/route-2d5f6296c7e693c6.js +0 -1
  491. package/.next/static/chunks/app/api/terminal/route-2d5f6296c7e693c6.js +0 -1
  492. package/.next/static/chunks/app/api/worktrees/route-2d5f6296c7e693c6.js +0 -1
  493. package/.next/static/chunks/app/api/yjs-version/route-2d5f6296c7e693c6.js +0 -1
  494. package/.next/static/chunks/app/layout-636e9e756d0734c7.js +0 -1
  495. package/.next/static/chunks/app/manifest.webmanifest/route-2d5f6296c7e693c6.js +0 -1
  496. package/.next/static/chunks/app/page-44daed9dcf9134eb.js +0 -89
  497. package/.next/static/chunks/main-app-b3e8a8d15d83e1f0.js +0 -1
  498. package/.next/static/chunks/next/dist/client/components/builtin/app-error-2d5f6296c7e693c6.js +0 -1
  499. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-2d5f6296c7e693c6.js +0 -1
  500. package/.next/static/chunks/next/dist/client/components/builtin/global-error-00a4badaccb388e6.js +0 -1
  501. package/.next/static/chunks/next/dist/client/components/builtin/not-found-2d5f6296c7e693c6.js +0 -1
  502. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-2d5f6296c7e693c6.js +0 -1
  503. package/.next/static/css/4a609fb185e7cff1.css +0 -1
  504. package/.next/static/h-hr7x0pqNUx3TyuarqWJ/_buildManifest.js +0 -1
  505. package/lib/extension-command.test.mjs +0 -63
  506. package/lib/extension-command.ts +0 -81
  507. package/lib/subagent-widget.test.mjs +0 -87
  508. package/lib/subagent-widget.ts +0 -109
  509. /package/.next/static/{h-hr7x0pqNUx3TyuarqWJ → k8ZFqwz7d-bnK0ZiUVgCc}/_ssgManifest.js +0 -0
@@ -13,7 +13,14 @@ import {
13
13
  preferUserBashExtension,
14
14
  } from "./project-command-env";
15
15
  import { createTodoExtension } from "./todo-extension";
16
- import { cacheSessionPath, invalidateSessionListCache } from "./session-reader";
16
+ import { createSubagentExtension, preferPiWebSubagentExtension } from "./subagent-extension";
17
+ import { listSubagentProfiles, readSubagentRun, readSubagentSessionResources, SUBAGENT_CONTROL_TOOL_NAMES } from "./subagents";
18
+ import { createSubagentController } from "./subagent-runtime";
19
+ import { isBuiltInSubagentsEnabled } from "./subagent-settings";
20
+ import { resolveShellTools } from "./powershell-settings";
21
+ import { CHAT_ONLY_RESOURCE_LOADER_OPTIONS } from "./chat-only";
22
+ import { cacheSessionPath, invalidateSessionListCache, resolveSessionPath } from "./session-reader";
23
+ import { indexSessionFileNow } from "./session-index-scanner";
17
24
  import { projectIdentityKey } from "./project-identity";
18
25
  import { resolveProject } from "./worktree";
19
26
  import { ensureSessionMetaRow } from "./task-store";
@@ -27,6 +34,7 @@ import type {
27
34
  ExtensionUiRequest,
28
35
  ExtensionUiResponse,
29
36
  ExtensionWidgetItem,
37
+ SessionEntry,
30
38
  SessionInfo,
31
39
  SessionMessageEntry,
32
40
  } from "./types";
@@ -97,6 +105,11 @@ type ExtensionBindingOptions = {
97
105
  forceEmptySystemPrompt?: boolean;
98
106
  };
99
107
 
108
+ type AgentSessionWrapperOptions = {
109
+ exactSystemPrompt?: () => string;
110
+ chatOnly?: boolean;
111
+ };
112
+
100
113
  const IDLE_RESET_EVENT_TYPES = new Set([
101
114
  "agent_end",
102
115
  "agent_settled",
@@ -104,6 +117,28 @@ const IDLE_RESET_EVENT_TYPES = new Set([
104
117
  "compaction_end",
105
118
  ]);
106
119
 
120
+ const DEFAULT_SESSION_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
121
+
122
+ /**
123
+ * Resolves the PI_WEB_IDLE_TIMEOUT_MS environment variable into a session idle
124
+ * timeout in milliseconds. An unset/blank value returns the 10-minute default,
125
+ * `0` disables idle shutdown, and positive values up to Node's timer limit
126
+ * (2147483647 ms) are used as-is. Invalid or out-of-range values fall back to
127
+ * the default with a console warning.
128
+ */
129
+ export function resolveSessionIdleTimeoutMs(
130
+ rawValue: string | undefined = process.env.PI_WEB_IDLE_TIMEOUT_MS,
131
+ ): number {
132
+ if (rawValue !== undefined && rawValue.trim() !== "") {
133
+ const parsed = Number(rawValue);
134
+ if (Number.isFinite(parsed) && parsed >= 0 && parsed <= 2_147_483_647) return parsed;
135
+ console.warn(`[pi-web] invalid PI_WEB_IDLE_TIMEOUT_MS "${rawValue}", falling back to 10 minutes`);
136
+ }
137
+ return DEFAULT_SESSION_IDLE_TIMEOUT_MS;
138
+ }
139
+
140
+ const SESSION_IDLE_TIMEOUT_MS = resolveSessionIdleTimeoutMs();
141
+
107
142
  export interface RpcSessionStartOptions {
108
143
  toolNames?: string[];
109
144
  initialModel?: { provider: string; modelId: string };
@@ -180,8 +215,15 @@ export class AgentSessionWrapper {
180
215
  private _alive = true;
181
216
  /** 最近一次 agent_start 的时间(看板“运行时长”用);0 = 未知 */
182
217
  private lastAgentStart = 0;
183
-
184
- constructor(public readonly inner: AgentSessionLike) {}
218
+ private readonly exactSystemPrompt?: () => string;
219
+ private readonly chatOnly: boolean;
220
+
221
+ constructor(public readonly inner: AgentSessionLike, options: AgentSessionWrapperOptions = {}) {
222
+ this.exactSystemPrompt = options.exactSystemPrompt;
223
+ this.chatOnly = options.chatOnly ?? false;
224
+ this.installExactSystemPromptContinuation();
225
+ this.applyExactSystemPrompt();
226
+ }
185
227
 
186
228
  /** 是否有等待用户响应的扩展 UI 请求(waiting_input 判定用) */
187
229
  hasPendingUiRequest(): boolean {
@@ -217,6 +259,10 @@ export class AgentSessionWrapper {
217
259
  return this._alive;
218
260
  }
219
261
 
262
+ isChatOnly(): boolean {
263
+ return this.chatOnly;
264
+ }
265
+
220
266
  isRunning(): boolean {
221
267
  return this._alive && (this.pendingPromptCount > 0 || this.inner.isStreaming || this.inner.isCompacting || this.inner.isBashRunning);
222
268
  }
@@ -250,10 +296,31 @@ export class AgentSessionWrapper {
250
296
  await this.waitForExtensionsBound();
251
297
  }
252
298
 
299
+ private applyExactSystemPrompt(): void {
300
+ if (!this.exactSystemPrompt || !this.inner.agent.state) return;
301
+ this.inner.agent.state.systemPrompt = this.exactSystemPrompt();
302
+ }
303
+
304
+ private installExactSystemPromptContinuation(): void {
305
+ if (!this.exactSystemPrompt) return;
306
+ const previous = this.inner.agent.prepareNextTurnWithContext;
307
+ this.inner.agent.prepareNextTurnWithContext = async (turn, signal) => {
308
+ const prepared = await previous?.(turn, signal);
309
+ return {
310
+ ...prepared,
311
+ context: {
312
+ ...(prepared?.context ?? turn.context),
313
+ systemPrompt: this.exactSystemPrompt!(),
314
+ },
315
+ };
316
+ };
317
+ }
318
+
253
319
  private ensureExtensionsBound(options: ExtensionBindingOptions = {}): Promise<void> {
254
320
  if (options.forceEmptySystemPrompt) this.forceEmptySystemPrompt = true;
255
321
  if (this.extensionsBound) {
256
322
  this.applyForcedEmptySystemPrompt();
323
+ this.applyExactSystemPrompt();
257
324
  return Promise.resolve();
258
325
  }
259
326
  if (this.extensionBindingPromise) return this.extensionBindingPromise;
@@ -293,6 +360,7 @@ export class AgentSessionWrapper {
293
360
  }
294
361
  this.extensionsBound = true;
295
362
  this.applyForcedEmptySystemPrompt();
363
+ this.applyExactSystemPrompt();
296
364
  console.log(`[pi-web] session_start dispatched to extensions for session ${this.inner.sessionId}`);
297
365
  })().catch((err) => {
298
366
  this.extensionBindingError = err;
@@ -361,6 +429,8 @@ export class AgentSessionWrapper {
361
429
  }
362
430
 
363
431
  private resetIdleTimer(): void {
432
+ // A resolved timeout of 0 disables idle shutdown entirely.
433
+ if (SESSION_IDLE_TIMEOUT_MS === 0) return;
364
434
  if (this.idleTimer) clearTimeout(this.idleTimer);
365
435
  this.idleTimer = setTimeout(() => {
366
436
  if (this.isRunning()) {
@@ -370,7 +440,7 @@ export class AgentSessionWrapper {
370
440
  void this.shutdown().catch((error) => {
371
441
  console.error("[pi-web] failed to shut down idle session:", error instanceof Error ? error.message : error);
372
442
  });
373
- }, 10 * 60 * 1000);
443
+ }, SESSION_IDLE_TIMEOUT_MS);
374
444
  }
375
445
 
376
446
  onEvent(listener: EventListener): () => void {
@@ -531,6 +601,40 @@ export class AgentSessionWrapper {
531
601
  return { id: model.id, provider: model.provider };
532
602
  }
533
603
 
604
+ case "fork_branch": {
605
+ if (this.isRunning()) {
606
+ throw new Error("Cannot fork while the session is running");
607
+ }
608
+ const entryId = command.entryId as string;
609
+ const sessionManager = this.inner.sessionManager;
610
+ const currentSessionFile = this.inner.sessionFile;
611
+ if (!sessionManager.isPersisted()) return { cancelled: true };
612
+ if (!currentSessionFile) throw new Error("Persisted session is missing a session file");
613
+ if (!sessionManager.getEntry(entryId)) throw new Error("Invalid entry ID for forking");
614
+
615
+ const sessionDir = sessionManager.getSessionDir();
616
+ const sourceManager = SessionManager.open(currentSessionFile, sessionDir);
617
+ const forkedPath = sourceManager.createBranchedSession(entryId);
618
+ if (!forkedPath) throw new Error("Failed to create forked session");
619
+ // SDK 惰性落盘契约:fork 点之前无 assistant 消息时不写文件(flushed=false),
620
+ // 只返回路径字符串。不强制落盘则后续 open 不存在文件会 newSession 生成新 id,
621
+ // 索引/列表/看板全部读不到——必须和 fork case 一样手动写盘。
622
+ if (!existsSync(forkedPath)) {
623
+ const content = [sourceManager.getHeader(), ...sourceManager.getEntries()]
624
+ .map((entry) => JSON.stringify(entry))
625
+ .join("\n") + "\n";
626
+ writeFileSync(forkedPath, content, { encoding: "utf8", flag: "wx" });
627
+ (sourceManager as unknown as { flushed: boolean }).flushed = true;
628
+ }
629
+
630
+ const newSessionId = SessionManager.open(forkedPath, sessionDir).getSessionId();
631
+ cacheSessionPath(newSessionId, forkedPath);
632
+ invalidateSessionListCache();
633
+ // 立即进 session_meta 索引(否则要等 30s 后台扫描才出现在会话列表)
634
+ await indexSessionFileNow(forkedPath, sessionManager.getSessionId());
635
+ return { cancelled: false, newSessionId };
636
+ }
637
+
534
638
  case "fork": {
535
639
  if (this.inner.isBashRunning) {
536
640
  throw new Error("Cannot fork while a shell command is running");
@@ -685,9 +789,10 @@ export class AgentSessionWrapper {
685
789
  case "get_tools": {
686
790
  const all: ToolInfo[] = this.inner.getAllTools();
687
791
  const active = new Set<string>(this.inner.getActiveToolNames());
792
+ // 对齐上游:展开整个 ToolInfo(含 parameters / promptGuidelines),
793
+ // 之前只挑 name/description/active 会把参数 schema 丢掉 → 面板参数全是“无”。
688
794
  return all.map((t) => ({
689
- name: t.name,
690
- description: t.description,
795
+ ...t,
691
796
  active: active.has(t.name),
692
797
  }));
693
798
  }
@@ -723,6 +828,9 @@ export class AgentSessionWrapper {
723
828
 
724
829
  case "set_tools": {
725
830
  const toolNames = command.toolNames as string[];
831
+ if (readSubagentSessionResources(this.inner.sessionManager.getEntries() as unknown as SessionEntry[])) {
832
+ throw new Error("Subagent tool selection is fixed by its profile");
833
+ }
726
834
  this.setForceEmptySystemPrompt(toolNames.length === 0);
727
835
  this.inner.setActiveToolsByName(withExtensionTools(this.inner, toolNames));
728
836
  this.applyForcedEmptySystemPrompt();
@@ -1518,6 +1626,8 @@ export function getRpcSessionInfos(): SessionInfo[] {
1518
1626
  // loads commands. Do not leak it into history before a prompt is accepted.
1519
1627
  if (!persisted && (!session.isRunning() || !firstUserMessage)) continue;
1520
1628
 
1629
+ const subagent = readSubagentRun(entries as unknown as SessionEntry[], header?.id ?? session.sessionId, sessionFile ?? "");
1630
+
1521
1631
  const created = header?.timestamp
1522
1632
  ?? entries[0]?.timestamp
1523
1633
  ?? new Date().toISOString();
@@ -1537,6 +1647,16 @@ export function getRpcSessionInfos(): SessionInfo[] {
1537
1647
  modified: new Date(lastActivityMs).toISOString(),
1538
1648
  messageCount: messages.length,
1539
1649
  firstMessage: firstUserMessage ? runtimeMessageText(firstUserMessage) || "(no messages)" : "(no messages)",
1650
+ ...(subagent ? {
1651
+ parentSessionId: subagent.parentSessionId,
1652
+ relation: {
1653
+ kind: "subagent" as const,
1654
+ parentSessionId: subagent.parentSessionId,
1655
+ profile: subagent.profile,
1656
+ description: subagent.description,
1657
+ status: session.isRunning() ? "running" as const : subagent.status,
1658
+ },
1659
+ } : {}),
1540
1660
  transient: !persisted,
1541
1661
  });
1542
1662
  }
@@ -1646,14 +1766,25 @@ export async function startRpcSession(
1646
1766
  const sessionCwd = sessionManager.getCwd();
1647
1767
  const finishStartingSession = trackStartingSession(sessionCwd);
1648
1768
  const starting = (async () => {
1769
+ // subagent 会话(含恢复)按 profile 的资源/工具加载选项重建;普通会话不命中。
1770
+ const subagentResources = sessionFile
1771
+ ? readSubagentSessionResources(sessionManager.getEntries() as unknown as SessionEntry[])
1772
+ : null;
1773
+ const subagentLoadsResources = Boolean(subagentResources?.loadExtensions || subagentResources?.loadSkills);
1774
+ // chatOnly 仅当“显式空工具列表”时为真:subagent 会话按 profile 的 tools 判定,
1775
+ // 普通会话按调用方传入的 toolNames(undefined = 默认全量,[] = 全关)。
1776
+ const chatOnly = subagentResources
1777
+ ? subagentResources.tools.length === 0 && !subagentLoadsResources
1778
+ : toolNames?.length === 0;
1779
+
1649
1780
  // Some extensions access the SDK's global theme even outside the terminal UI.
1650
- initTheme();
1781
+ if (!chatOnly) initTheme();
1651
1782
  const agentDir = getAgentDir();
1652
1783
 
1653
1784
  // Determine which tools to pass based on requested toolNames.
1654
1785
  // Since v0.68.0, session creation expects string[] tool names instead of Tool[] instances.
1655
- let toolsOption: string[] | undefined;
1656
- if (toolNames !== undefined) {
1786
+ let toolsOption: string[] | undefined = subagentResources?.tools;
1787
+ if (!subagentResources && toolNames !== undefined) {
1657
1788
  // toolNames === [] -> "all off" (an empty allow-list disables every tool).
1658
1789
  // Otherwise DO NOT pass a builtin-only allow-list: passing CODING_TOOL_NAMES
1659
1790
  // set allowedToolNames to coding builtins only, which filtered every
@@ -1674,18 +1805,37 @@ export async function startRpcSession(
1674
1805
  cwd: sessionCwd,
1675
1806
  agentDir,
1676
1807
  settingsManager,
1677
- resourceLoaderOptions: {
1678
- extensionFactories: [
1679
- createProjectCommandBashExtension({
1680
- cwd: sessionCwd,
1681
- settings: settingsManager,
1682
- }),
1683
- // 内建会话 TODO(工具 + pi-todo.state 快照),装 pi-web-sky 即自带,
1684
- // 无需用户安装任何 pi 包。工具重名时用户扩展优先(见 pi-todo 文档)。
1685
- createTodoExtension(),
1686
- ],
1687
- extensionsOverride: preferUserBashExtension,
1688
- },
1808
+ resourceLoaderOptions: subagentResources
1809
+ ? {
1810
+ noExtensions: !subagentResources.loadExtensions,
1811
+ noSkills: !subagentResources.loadSkills,
1812
+ noPromptTemplates: true,
1813
+ noThemes: true,
1814
+ noContextFiles: true,
1815
+ systemPrompt: " ",
1816
+ appendSystemPrompt: subagentResources.appendSystemPrompt,
1817
+ }
1818
+ : chatOnly
1819
+ ? CHAT_ONLY_RESOURCE_LOADER_OPTIONS
1820
+ : {
1821
+ extensionFactories: [
1822
+ createProjectCommandBashExtension({
1823
+ cwd: sessionCwd,
1824
+ settings: settingsManager,
1825
+ }),
1826
+ // 内建会话 TODO(工具 + pi-todo.state 快照),装 pi-web-sky 即自带,
1827
+ // 无需用户安装任何 pi 包。工具重名时用户扩展优先(见 pi-todo 文档)。
1828
+ createTodoExtension(),
1829
+ // 内置 subagent(Agent/steer_subagent/get_subagent_result 工具),
1830
+ // 开关见 agents 设置(isBuiltInSubagentsEnabled)。
1831
+ createSubagentExtension(
1832
+ SUBAGENT_CONTROLLER.extensionRuntime,
1833
+ () => listSubagentProfiles(sessionCwd),
1834
+ isBuiltInSubagentsEnabled,
1835
+ ),
1836
+ ],
1837
+ extensionsOverride: (base) => preferUserBashExtension(preferPiWebSubagentExtension(base)),
1838
+ },
1689
1839
  ...(trustReloadOptions ? { resourceLoaderReloadOptions: trustReloadOptions } : {}),
1690
1840
  });
1691
1841
  const scope = await resolveVisibleModels(
@@ -1751,7 +1901,7 @@ export async function startRpcSession(
1751
1901
 
1752
1902
  wrapper.onDestroy(() => registry.delete(realSessionId));
1753
1903
  registry.set(realSessionId, wrapper);
1754
- wrapper.beginExtensionBinding({ forceEmptySystemPrompt: toolNames?.length === 0 });
1904
+ if (!chatOnly) wrapper.beginExtensionBinding({ forceEmptySystemPrompt: toolNames?.length === 0 });
1755
1905
 
1756
1906
  return { session: wrapper, realSessionId };
1757
1907
  })().finally(() => {
@@ -1762,3 +1912,39 @@ export async function startRpcSession(
1762
1912
  locks.set(sessionId, starting);
1763
1913
  return starting;
1764
1914
  }
1915
+
1916
+ const SUBAGENT_CONTROLLER = createSubagentController({
1917
+ getSession: (sessionId) => getRegistry().get(sessionId),
1918
+ registerSession: (inner, options) => {
1919
+ const wrapper = new AgentSessionWrapper(inner, {
1920
+ ...(options?.exactSystemPrompt !== undefined
1921
+ ? { exactSystemPrompt: () => options.exactSystemPrompt! }
1922
+ : {}),
1923
+ chatOnly: options?.chatOnly,
1924
+ });
1925
+ const realSessionId = inner.sessionId as string;
1926
+ const realSessionFile = inner.sessionFile as string | undefined;
1927
+ if (realSessionFile) cacheSessionPath(realSessionId, realSessionFile);
1928
+ wrapper.onDestroy(() => getRegistry().delete(realSessionId));
1929
+ getRegistry().set(realSessionId, wrapper);
1930
+ wrapper.start();
1931
+ if (!wrapper.isChatOnly()) wrapper.beginExtensionBinding();
1932
+ },
1933
+ reopenSession: async (sessionId, sessionFile) =>
1934
+ (await startRpcSession(sessionId, sessionFile, undefined)).session,
1935
+ resolveSessionPath,
1936
+ invalidateSessionList: invalidateSessionListCache,
1937
+ isBuiltInSubagentsEnabled,
1938
+ });
1939
+
1940
+ export function getSubagentRun(sessionId: string) {
1941
+ return SUBAGENT_CONTROLLER.get(sessionId);
1942
+ }
1943
+
1944
+ export function steerSubagent(sessionId: string, message: string) {
1945
+ return SUBAGENT_CONTROLLER.steer(sessionId, message);
1946
+ }
1947
+
1948
+ export function abortSubagent(sessionId: string) {
1949
+ return SUBAGENT_CONTROLLER.abort(sessionId);
1950
+ }
@@ -0,0 +1,49 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { createJiti } from "jiti";
4
+
5
+ const { getSessionFamily, listSessionFamilies } = await createJiti(import.meta.url).import("./session-family.ts");
6
+
7
+ function session(id, modified, relation) {
8
+ return {
9
+ path: `/tmp/${id}.jsonl`,
10
+ id,
11
+ cwd: "/tmp",
12
+ created: modified,
13
+ modified,
14
+ messageCount: 1,
15
+ firstMessage: id,
16
+ ...(relation ? { relation } : {}),
17
+ };
18
+ }
19
+
20
+ test("groups nested subagents under their main session and uses family activity for sorting", () => {
21
+ const main = session("main", "2026-01-01T00:00:00.000Z");
22
+ const child = session("child", "2026-01-04T00:00:00.000Z", {
23
+ kind: "subagent", parentSessionId: "main", profile: "explore", description: "Explore", status: "completed",
24
+ });
25
+ const grandchild = session("grandchild", "2026-01-03T00:00:00.000Z", {
26
+ kind: "subagent", parentSessionId: "child", profile: "review", description: "Review", status: "running",
27
+ });
28
+ const newerRoot = session("newer-root", "2026-01-02T00:00:00.000Z");
29
+
30
+ const families = listSessionFamilies([main, child, grandchild, newerRoot]);
31
+ assert.deepEqual(families.map((family) => family.root.id), ["main", "newer-root"]);
32
+ assert.deepEqual(families[0].subagents.map((item) => item.id), ["child", "grandchild"]);
33
+ assert.equal(getSessionFamily([main, child, grandchild], "grandchild")?.root.id, "main");
34
+ });
35
+
36
+ test("does not promote orphaned or cyclic subagent metadata into the main session list", () => {
37
+ const orphan = session("orphan", "2026-01-03T00:00:00.000Z", {
38
+ kind: "subagent", parentSessionId: "missing", profile: "explore", description: "Explore", status: "interrupted",
39
+ });
40
+ const a = session("a", "2026-01-01T00:00:00.000Z", {
41
+ kind: "subagent", parentSessionId: "b", profile: "a", description: "A", status: "interrupted",
42
+ });
43
+ const b = session("b", "2026-01-02T00:00:00.000Z", {
44
+ kind: "subagent", parentSessionId: "a", profile: "b", description: "B", status: "interrupted",
45
+ });
46
+
47
+ assert.deepEqual(listSessionFamilies([orphan, a, b]), []);
48
+ assert.equal(getSessionFamily([orphan, a, b], "orphan"), null);
49
+ });
@@ -0,0 +1,80 @@
1
+ import type { SessionInfo } from "./types";
2
+
3
+ export interface SessionFamily {
4
+ root: SessionInfo;
5
+ subagents: SessionInfo[];
6
+ latestModified: string;
7
+ }
8
+
9
+ function resolveFamilyRoots(sessions: readonly SessionInfo[]): Map<string, string | null> {
10
+ const byId = new Map(sessions.map((session) => [session.id, session]));
11
+ const roots = new Map<string, string | null>();
12
+
13
+ for (const session of sessions) {
14
+ if (roots.has(session.id)) continue;
15
+
16
+ const path: string[] = [];
17
+ const visited = new Set<string>();
18
+ let currentId = session.id;
19
+ let rootId: string | null = null;
20
+
21
+ while (true) {
22
+ if (roots.has(currentId)) {
23
+ rootId = roots.get(currentId) ?? null;
24
+ break;
25
+ }
26
+ if (visited.has(currentId)) break;
27
+
28
+ visited.add(currentId);
29
+ path.push(currentId);
30
+ const current = byId.get(currentId);
31
+ if (!current) break;
32
+ if (current.relation?.kind !== "subagent") {
33
+ rootId = current.id;
34
+ break;
35
+ }
36
+ currentId = current.relation.parentSessionId;
37
+ }
38
+
39
+ for (const id of path) roots.set(id, rootId);
40
+ }
41
+
42
+ return roots;
43
+ }
44
+
45
+ /** Groups visible main/fork sessions with every persisted subagent descendant. */
46
+ export function listSessionFamilies(sessions: readonly SessionInfo[]): SessionFamily[] {
47
+ const rootsBySessionId = resolveFamilyRoots(sessions);
48
+ const families = new Map<string, SessionFamily>();
49
+
50
+ for (const session of sessions) {
51
+ if (session.relation?.kind === "subagent") continue;
52
+ families.set(session.id, {
53
+ root: session,
54
+ subagents: [],
55
+ latestModified: session.modified,
56
+ });
57
+ }
58
+
59
+ for (const session of sessions) {
60
+ if (session.relation?.kind !== "subagent") continue;
61
+ const rootId = rootsBySessionId.get(session.id);
62
+ const family = rootId ? families.get(rootId) : undefined;
63
+ if (!family) continue;
64
+ family.subagents.push(session);
65
+ if (session.modified > family.latestModified) family.latestModified = session.modified;
66
+ }
67
+
68
+ return [...families.values()].sort((a, b) => b.latestModified.localeCompare(a.latestModified));
69
+ }
70
+
71
+ export function getSessionFamily(
72
+ sessions: readonly SessionInfo[],
73
+ sessionId: string | null | undefined,
74
+ ): SessionFamily | null {
75
+ if (!sessionId) return null;
76
+ return listSessionFamilies(sessions).find((family) => (
77
+ family.root.id === sessionId
78
+ || family.subagents.some((session) => session.id === sessionId)
79
+ )) ?? null;
80
+ }
@@ -40,6 +40,45 @@ export interface SessionIndexScanSummary {
40
40
  /** 默认扫描周期:30s(外部/CLI 新建会话最长等一个周期进列表)。 */
41
41
  export const SESSION_INDEX_SCAN_INTERVAL_MS = 30_000;
42
42
 
43
+ /**
44
+ * 单文件立即索引:新会话文件已落盘但等不及后台扫描时调用(如 fork_branch 创建
45
+ * 的引用分支,需立刻出现在会话列表)。幂等 upsert,parent 由调用方显式指定
46
+ * (磁盘 header 的父路径反查需要全量 path 映射,单文件场景不划算)。
47
+ */
48
+ export async function indexSessionFileNow(
49
+ filePath: string,
50
+ parentSessionId: string | null,
51
+ ): Promise<void> {
52
+ const head = scanOneSessionHead(filePath);
53
+ if (!head) return;
54
+ const projectKey = await resolveProject(head.cwd).then((p) => projectIdentityKey(p?.projectRoot ?? head.cwd));
55
+ getDb()
56
+ .prepare(
57
+ `INSERT INTO session_meta
58
+ (session_id, task_id, updated, pinned, path, cwd, project_key, title, first_message, parent_id, created, modified)
59
+ VALUES (?, NULL, ?, 0, ?, ?, ?, NULL, ?, ?, ?, ?)
60
+ ON CONFLICT(session_id) DO UPDATE SET
61
+ path = excluded.path,
62
+ cwd = excluded.cwd,
63
+ project_key = excluded.project_key,
64
+ first_message = excluded.first_message,
65
+ parent_id = excluded.parent_id,
66
+ created = excluded.created,
67
+ modified = excluded.modified`,
68
+ )
69
+ .run(
70
+ head.id,
71
+ Date.now(),
72
+ filePath,
73
+ head.cwd,
74
+ projectKey,
75
+ head.firstMessage || null,
76
+ parentSessionId,
77
+ head.created.getTime(),
78
+ Date.now(),
79
+ );
80
+ }
81
+
43
82
  /** 一轮全量扫描。`sessionsDir` 仅在测试注入(默认走 agentDir)。 */
44
83
  export async function runSessionIndexScan(
45
84
  sessionsDir?: string,
@@ -0,0 +1,38 @@
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, { tsconfigPaths: true });
6
+ const { sessionTimeGroup } = await jiti.import("./session-time-group.ts");
7
+
8
+ /** 2026-06-10 是周三;用它当"现在"可以同时覆盖昨天/本周/近一月/更久。 */
9
+ const now = new Date(2026, 5, 10, 15, 0, 0).getTime();
10
+ const at = (...args) => new Date(...args).getTime();
11
+
12
+ test("按日历天分今天/昨天,而不是按 24 小时窗口", () => {
13
+ assert.equal(sessionTimeGroup(at(2026, 5, 10, 0, 0, 0), now), "today");
14
+ assert.equal(sessionTimeGroup(at(2026, 5, 9, 23, 59, 0), now), "yesterday");
15
+ // 只是 16 小时前,但已经跨过零点 → 属于昨天
16
+ assert.equal(sessionTimeGroup(at(2026, 5, 9, 23, 0, 0), now), "yesterday");
17
+ });
18
+
19
+ test("本周从周一起算,且不吞掉昨天", () => {
20
+ assert.equal(sessionTimeGroup(at(2026, 5, 8, 10, 0, 0), now), "thisWeek"); // 周一
21
+ assert.equal(sessionTimeGroup(at(2026, 5, 7, 10, 0, 0), now), "thisMonth"); // 上周日
22
+ });
23
+
24
+ test("周一当天本周为空:周日的会话算昨天", () => {
25
+ const monday = new Date(2026, 5, 8, 9, 0, 0).getTime();
26
+ assert.equal(sessionTimeGroup(at(2026, 5, 7, 20, 0, 0), monday), "yesterday");
27
+ assert.equal(sessionTimeGroup(at(2026, 5, 6, 20, 0, 0), monday), "thisMonth");
28
+ });
29
+
30
+ test("近一月是滚动 30 天,第 31 天落到更久之前", () => {
31
+ assert.equal(sessionTimeGroup(at(2026, 4, 12, 9, 0, 0), now), "thisMonth"); // 29 天前
32
+ assert.equal(sessionTimeGroup(at(2026, 4, 11, 9, 0, 0), now), "older"); // 30 天前
33
+ });
34
+
35
+ test("无法解析的时间按更久之前处理", () => {
36
+ assert.equal(sessionTimeGroup("not-a-date", now), "older");
37
+ assert.equal(sessionTimeGroup(new Date(2026, 5, 10, 15).toISOString(), now), "today");
38
+ });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 会话列表的时间分组(今天 / 昨天 / 本周 / 近一月 / 更久之前)。
3
+ *
4
+ * 只做分类,不排序:调用方(侧栏聊天区)已按 modified 倒序排好,
5
+ * 这里只负责给每条会话贴上它属于哪一段。
6
+ */
7
+ export type SessionTimeGroup = "today" | "yesterday" | "thisWeek" | "thisMonth" | "older";
8
+
9
+ /** 分组展示顺序(由近及远)。 */
10
+ export const SESSION_TIME_GROUP_ORDER: readonly SessionTimeGroup[] = [
11
+ "today",
12
+ "yesterday",
13
+ "thisWeek",
14
+ "thisMonth",
15
+ "older",
16
+ ];
17
+
18
+ /** 近一月的窗口长度(含今天),滚动 30 天而非自然月。 */
19
+ const RECENT_MONTH_DAYS = 30;
20
+
21
+ function startOfDay(ms: number): number {
22
+ const d = new Date(ms);
23
+ d.setHours(0, 0, 0, 0);
24
+ return d.getTime();
25
+ }
26
+
27
+ /** 加减自然日(走 Date,跨夏令时不会少/多一小时)。 */
28
+ function addDays(ms: number, days: number): number {
29
+ const d = new Date(ms);
30
+ d.setDate(d.getDate() + days);
31
+ return d.getTime();
32
+ }
33
+
34
+ /** 本周起始(周一 00:00),与中文习惯一致。 */
35
+ function startOfWeek(ms: number): number {
36
+ const day = new Date(ms).getDay(); // 0 = 周日
37
+ return addDays(startOfDay(ms), -((day + 6) % 7));
38
+ }
39
+
40
+ /**
41
+ * 判断一条会话的修改时间落在哪个时间分组。
42
+ * @param modified 会话修改时间(ISO 字符串或毫秒时间戳)
43
+ * @param now 当前时间(毫秒),用于测试注入
44
+ * @returns 时间分组;时间不可解析时按「更久之前」处理
45
+ */
46
+ export function sessionTimeGroup(modified: string | number, now: number = Date.now()): SessionTimeGroup {
47
+ const ms = typeof modified === "number" ? modified : Date.parse(modified);
48
+ if (!Number.isFinite(ms)) return "older";
49
+ // 未来时间(客户端与服务端时钟漂移)归今天,别把刚写的会话沉到底部。
50
+ if (ms >= startOfDay(now)) return "today";
51
+ if (ms >= addDays(startOfDay(now), -1)) return "yesterday";
52
+ if (ms >= startOfWeek(now)) return "thisWeek";
53
+ if (ms >= addDays(startOfDay(now), -(RECENT_MONTH_DAYS - 1))) return "thisMonth";
54
+ return "older";
55
+ }