@jetcrab/pi-desk 1.1.25 → 1.1.29-dev

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 (340) hide show
  1. package/package.json +1 -1
  2. package/src/app/api/work-sessions/search/route.ts +1 -0
  3. package/src/app/project-preview/page.tsx +1 -3
  4. package/src/client/l2_biz/project-preview/hooks/l2-use-project-preview.ts +1 -14
  5. package/src/client/l2_biz/project-preview/l2-project-preview-dialog.tsx +0 -1
  6. package/src/client/l2_biz/project-preview/l2-project-preview-page.tsx +0 -1
  7. package/src/client/l2_biz/project-preview/l2-project-preview.tsx +1 -4
  8. package/src/client/l2_biz/project-preview/views/l2-project-branch-select.tsx +42 -3
  9. package/src/client/l2_biz/workbench/git-workspace/l2-git-workspace-runtime.ts +11 -10
  10. package/src/client/l2_biz/workbench/hooks/l2-work-session-search.ts +52 -0
  11. package/src/client/l2_biz/workbench/hooks/l2-workbench-branch-picker.ts +42 -7
  12. package/src/client/l2_biz/workbench/hooks/l2-workbench-session-history.ts +143 -131
  13. package/src/client/l2_biz/workbench/hooks/l2-workbench-session-search.ts +97 -0
  14. package/src/client/l2_biz/workbench/l2-work-session-columns.tsx +8 -0
  15. package/src/client/l2_biz/workbench/l2-work-session-sidebar.tsx +129 -10
  16. package/src/client/l2_biz/workbench/l2-workbench-biz.ts +20 -2
  17. package/src/client/l2_biz/workbench/l2-workbench-branch-filter-preferences.ts +34 -0
  18. package/src/client/l2_biz/workbench/l2-workbench-branch-picker.tsx +34 -71
  19. package/src/client/l2_biz/workbench/l2-workbench-chat-input-runtime.ts +38 -1
  20. package/src/client/l2_biz/workbench/l2-workbench-chat-view.tsx +11 -1
  21. package/src/client/l2_biz/workbench/l2-workbench-picker-dialog.tsx +55 -215
  22. package/src/client/l2_biz/workbench/l2-workbench.tsx +20 -38
  23. package/src/client/l2_biz/workbench/views/l2-workbench-session-search.tsx +123 -0
  24. package/src/client/l3_modules/conversation/l3-conversation-messages.tsx +43 -4
  25. package/src/client/l3_modules/project-files/l3-markdown-preview.module.css +1 -1
  26. package/src/client/l3_modules/project-files/l3-project-document-git.ts +13 -29
  27. package/src/client/l3_modules/project-files/l3-project-document-view.tsx +1 -4
  28. package/src/client/l3_modules/project-files/l3-project-file-browser.module.css +14 -18
  29. package/src/client/l3_modules/project-files/l3-project-file-browser.tsx +52 -95
  30. package/src/client/l3_modules/project-files/l3-project-file-runtime.ts +52 -21
  31. package/src/client/l3_modules/project-files/l3-project-file-tabs.tsx +41 -62
  32. package/src/client/l3_modules/project-files/l3-project-git-runtime.ts +5 -4
  33. package/src/client/l3_modules/project-files/l3-use-project-refresh.ts +36 -0
  34. package/src/client/l3_modules/project-files/views/l3-project-search-results.tsx +2 -2
  35. package/src/client/l3_modules/project-files/views/l3-project-tree-folder.tsx +86 -0
  36. package/src/client/l4_foundation/ui/file-icons/README.md +1 -1
  37. package/src/client/l4_foundation/ui/file-icons/l4-file-icon.tsx +49 -2
  38. package/src/common/l2_biz/pi-session/l2-pi-session-contract.ts +11 -24
  39. package/src/common/l2_biz/project-preview/l2-project-preview-navigation.ts +5 -12
  40. package/src/common/l2_biz/work-session/l2-work-session-search-contract.ts +26 -0
  41. package/src/common/l2_biz/work-session/l2-work-session-tree-contract.ts +9 -1
  42. package/src/common/l2_biz/workbench/l2-workbench-locale-messages.ts +12 -0
  43. package/src/common/l2_biz/workbench/l2-workbench-picker-messages.ts +6 -2
  44. package/src/common/l3_modules/conversation/l3-conversation-locale-messages.ts +2 -0
  45. package/src/common/l3_modules/pi/l3-pi-session-search-contract.ts +26 -0
  46. package/src/common/l4_foundation/locale/l4-host-error-messages.ts +2 -0
  47. package/src/server/l1_entry/api/l1-work-session-branch-route.ts +21 -15
  48. package/src/server/l1_entry/api/l1-work-session-search-route.ts +38 -0
  49. package/src/server/l2_biz/work-session/l2-work-session-chat-runtime.ts +4 -2
  50. package/src/server/l2_biz/work-session/l2-work-session-manage.ts +46 -10
  51. package/src/server/l3_modules/work-session/l3-work-session.ts +11 -6
  52. package/src/server/l4_foundation/pi/l4-pi-chat-worker.ts +35 -2
  53. package/src/server/l4_foundation/pi/l4-pi-session-history-catalog.ts +62 -113
  54. package/src/server/l4_foundation/pi/l4-pi-session-search.ts +166 -0
  55. package/src/server/l4_foundation/pi/l4-pi-work-session-runtime.ts +66 -5
  56. package/src/server/l4_foundation/process/l4-process-tree.js +159 -40
  57. package/temp/build/pi-desk/release/.next/BUILD_ID +1 -1
  58. package/temp/build/pi-desk/release/.next/app-path-routes-manifest.json +1 -0
  59. package/temp/build/pi-desk/release/.next/build-manifest.json +2 -2
  60. package/temp/build/pi-desk/release/.next/prerender-manifest.json +3 -3
  61. package/temp/build/pi-desk/release/.next/required-server-files.js +3 -3
  62. package/temp/build/pi-desk/release/.next/required-server-files.json +3 -3
  63. package/temp/build/pi-desk/release/.next/routes-manifest.json +6 -0
  64. package/temp/build/pi-desk/release/.next/server/app/_global-error/page.js +1 -1
  65. package/temp/build/pi-desk/release/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  66. package/temp/build/pi-desk/release/.next/server/app/_global-error.html +1 -1
  67. package/temp/build/pi-desk/release/.next/server/app/_global-error.rsc +1 -1
  68. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  69. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  70. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  71. package/temp/build/pi-desk/release/.next/server/app/_not-found/page.js +1 -1
  72. package/temp/build/pi-desk/release/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  73. package/temp/build/pi-desk/release/.next/server/app/api/auth/login/route.js +2 -2
  74. package/temp/build/pi-desk/release/.next/server/app/api/auth/logout/route.js +1 -1
  75. package/temp/build/pi-desk/release/.next/server/app/api/auth-settings/get/route.js +1 -1
  76. package/temp/build/pi-desk/release/.next/server/app/api/auth-settings/replace/route.js +1 -1
  77. package/temp/build/pi-desk/release/.next/server/app/api/capability-modes/get/route.js +1 -1
  78. package/temp/build/pi-desk/release/.next/server/app/api/capability-modes/replace/route.js +1 -1
  79. package/temp/build/pi-desk/release/.next/server/app/api/chat/context/get/route.js +1 -1
  80. package/temp/build/pi-desk/release/.next/server/app/api/chat/images/get/route.js +1 -1
  81. package/temp/build/pi-desk/release/.next/server/app/api/chat/messages/get/route.js +1 -1
  82. package/temp/build/pi-desk/release/.next/server/app/api/health/route.js +1 -1
  83. package/temp/build/pi-desk/release/.next/server/app/api/mcp-check/get/route.js +1 -1
  84. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/del/route.js +1 -1
  85. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/get/route.js +1 -1
  86. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/replace/route.js +1 -1
  87. package/temp/build/pi-desk/release/.next/server/app/api/model-catalog/list/route.js +1 -1
  88. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/get/route.js +1 -1
  89. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/preview/route.js +1 -1
  90. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/replace/route.js +1 -1
  91. package/temp/build/pi-desk/release/.next/server/app/api/pi/directories/list/route.js +1 -1
  92. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-entries/list/route.js +1 -1
  93. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-ignores/list/route.js +1 -1
  94. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-ignores/replace/route.js +1 -1
  95. package/temp/build/pi-desk/release/.next/server/app/api/pi/models/list/route.js +1 -1
  96. package/temp/build/pi-desk/release/.next/server/app/api/pi/session-user-messages/list/route.js +1 -1
  97. package/temp/build/pi-desk/release/.next/server/app/api/pi/sessions/list/route.js +1 -1
  98. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/get/route.js +1 -1
  99. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/search/route.js +1 -1
  100. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/settings-get/route.js +1 -1
  101. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/settings-replace/route.js +1 -1
  102. package/temp/build/pi-desk/release/.next/server/app/api/plugins/add/route.js +1 -1
  103. package/temp/build/pi-desk/release/.next/server/app/api/plugins/apply/route.js +1 -1
  104. package/temp/build/pi-desk/release/.next/server/app/api/plugins/batch/route.js +1 -1
  105. package/temp/build/pi-desk/release/.next/server/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route.js +1 -1
  106. package/temp/build/pi-desk/release/.next/server/app/api/plugins/del/route.js +1 -1
  107. package/temp/build/pi-desk/release/.next/server/app/api/plugins/enabled-set/route.js +1 -1
  108. package/temp/build/pi-desk/release/.next/server/app/api/plugins/get/route.js +1 -1
  109. package/temp/build/pi-desk/release/.next/server/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route.js +1 -1
  110. package/temp/build/pi-desk/release/.next/server/app/api/plugins/list/route.js +1 -1
  111. package/temp/build/pi-desk/release/.next/server/app/api/plugins/reload/route.js +1 -1
  112. package/temp/build/pi-desk/release/.next/server/app/api/plugins/update/route.js +1 -1
  113. package/temp/build/pi-desk/release/.next/server/app/api/project-files/get/route.js +2 -2
  114. package/temp/build/pi-desk/release/.next/server/app/api/project-files/list/route.js +1 -1
  115. package/temp/build/pi-desk/release/.next/server/app/api/project-files/reveal/route.js +1 -1
  116. package/temp/build/pi-desk/release/.next/server/app/api/project-files/search/route.js +1 -1
  117. package/temp/build/pi-desk/release/.next/server/app/api/project-git/get/route.js +2 -2
  118. package/temp/build/pi-desk/release/.next/server/app/api/project-git-branches/list/route.js +2 -2
  119. package/temp/build/pi-desk/release/.next/server/app/api/project-git-changes/list/route.js +1 -1
  120. package/temp/build/pi-desk/release/.next/server/app/api/project-git-diff/get/route.js +2 -2
  121. package/temp/build/pi-desk/release/.next/server/app/api/project-git-heads/get/route.js +1 -1
  122. package/temp/build/pi-desk/release/.next/server/app/api/project-git-log/list/route.js +2 -2
  123. package/temp/build/pi-desk/release/.next/server/app/api/project-model-default/get/route.js +1 -1
  124. package/temp/build/pi-desk/release/.next/server/app/api/project-model-default/replace/route.js +1 -1
  125. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/get/route.js +1 -1
  126. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/list/route.js +1 -1
  127. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/replace/route.js +1 -1
  128. package/temp/build/pi-desk/release/.next/server/app/api/skills/list/route.js +1 -1
  129. package/temp/build/pi-desk/release/.next/server/app/api/task-center/images/get/route.js +1 -1
  130. package/temp/build/pi-desk/release/.next/server/app/api/task-center/messages/get/route.js +1 -1
  131. package/temp/build/pi-desk/release/.next/server/app/api/work-session-git-branch/add/route.js +1 -1
  132. package/temp/build/pi-desk/release/.next/server/app/api/work-session-git-branch/replace/route.js +1 -1
  133. package/temp/build/pi-desk/release/.next/server/app/api/work-session-tree/get/route.js +1 -1
  134. package/temp/build/pi-desk/release/.next/server/app/api/work-session-tree-entry/get/route.js +1 -1
  135. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/add/route.js +1 -1
  136. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/branch/route.js +1 -1
  137. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/del/route.js +1 -1
  138. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/rename/route.js +1 -1
  139. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/replace/route.js +1 -1
  140. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/search/route.js +1 -0
  141. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/search/route_client-reference-manifest.js +1 -0
  142. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/sort/route.js +1 -1
  143. package/temp/build/pi-desk/release/.next/server/app/favicon.ico/route.js +1 -1
  144. package/temp/build/pi-desk/release/.next/server/app/login/page.js +1 -1
  145. package/temp/build/pi-desk/release/.next/server/app/login/page_client-reference-manifest.js +1 -1
  146. package/temp/build/pi-desk/release/.next/server/app/page.js +138 -138
  147. package/temp/build/pi-desk/release/.next/server/app/page_client-reference-manifest.js +1 -1
  148. package/temp/build/pi-desk/release/.next/server/app/project-preview/page.js +1 -1
  149. package/temp/build/pi-desk/release/.next/server/app/project-preview/page_client-reference-manifest.js +1 -1
  150. package/temp/build/pi-desk/release/.next/server/app-paths-manifest.json +1 -0
  151. package/temp/build/pi-desk/release/.next/server/chunks/23054.js +2 -2
  152. package/temp/build/pi-desk/release/.next/server/chunks/25406.js +8 -8
  153. package/temp/build/pi-desk/release/.next/server/chunks/27344.js +3 -3
  154. package/temp/build/pi-desk/release/.next/server/chunks/3108.js +3 -3
  155. package/temp/build/pi-desk/release/.next/server/chunks/35985.js +2 -2
  156. package/temp/build/pi-desk/release/.next/server/chunks/41856.js +1 -0
  157. package/temp/build/pi-desk/release/.next/server/chunks/62900.js +235 -0
  158. package/temp/build/pi-desk/release/.next/server/chunks/6917.js +1 -1
  159. package/temp/build/pi-desk/release/.next/server/chunks/81363.js +1 -1
  160. package/temp/build/pi-desk/release/.next/server/chunks/81912.js +3 -0
  161. package/temp/build/pi-desk/release/.next/server/chunks/92386.js +2 -2
  162. package/temp/build/pi-desk/release/.next/server/middleware-build-manifest.js +1 -1
  163. package/temp/build/pi-desk/release/.next/server/pages/500.html +1 -1
  164. package/temp/build/pi-desk/release/.next/server/server-reference-manifest.json +1 -1
  165. package/temp/build/pi-desk/release/.next/static/Yyfg-r17aYmEzlYYlIwDo/_buildManifest.js +1 -0
  166. package/temp/build/pi-desk/release/.next/static/chunks/10043-9d6c3735418ccaf9.js +238 -0
  167. package/temp/build/pi-desk/release/.next/static/chunks/11937-ac71fb0f7cef482a.js +1 -0
  168. package/temp/build/pi-desk/release/.next/static/chunks/30669-ffa76d39e0e25194.js +1 -0
  169. package/temp/build/pi-desk/release/.next/static/chunks/36643-f60e98278740073c.js +3 -0
  170. package/temp/build/pi-desk/release/.next/static/chunks/65232-a8571f78020eede2.js +15 -0
  171. package/temp/build/pi-desk/release/.next/static/chunks/{51474-8dc714cb76b2e60a.js → 65350-23bb961d87addf14.js} +3 -3
  172. package/temp/build/pi-desk/release/.next/static/chunks/app/_global-error/page-295cb63da5f7262f.js +1 -0
  173. package/temp/build/pi-desk/release/.next/static/chunks/app/_not-found/page-295cb63da5f7262f.js +1 -0
  174. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/login/route-295cb63da5f7262f.js +1 -0
  175. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/logout/route-295cb63da5f7262f.js +1 -0
  176. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth-settings/get/route-295cb63da5f7262f.js +1 -0
  177. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth-settings/replace/route-295cb63da5f7262f.js +1 -0
  178. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/get/route-295cb63da5f7262f.js +1 -0
  179. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/replace/route-295cb63da5f7262f.js +1 -0
  180. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/context/get/route-295cb63da5f7262f.js +1 -0
  181. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/images/get/route-295cb63da5f7262f.js +1 -0
  182. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/messages/get/route-295cb63da5f7262f.js +1 -0
  183. package/temp/build/pi-desk/release/.next/static/chunks/app/api/health/route-295cb63da5f7262f.js +1 -0
  184. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-check/get/route-295cb63da5f7262f.js +1 -0
  185. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/del/route-295cb63da5f7262f.js +1 -0
  186. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/get/route-295cb63da5f7262f.js +1 -0
  187. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/replace/route-295cb63da5f7262f.js +1 -0
  188. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-catalog/list/route-295cb63da5f7262f.js +1 -0
  189. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/get/route-295cb63da5f7262f.js +1 -0
  190. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/preview/route-295cb63da5f7262f.js +1 -0
  191. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/replace/route-295cb63da5f7262f.js +1 -0
  192. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directories/list/route-295cb63da5f7262f.js +1 -0
  193. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-entries/list/route-295cb63da5f7262f.js +1 -0
  194. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/list/route-295cb63da5f7262f.js +1 -0
  195. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/replace/route-295cb63da5f7262f.js +1 -0
  196. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/models/list/route-295cb63da5f7262f.js +1 -0
  197. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/session-user-messages/list/route-295cb63da5f7262f.js +1 -0
  198. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/sessions/list/route-295cb63da5f7262f.js +1 -0
  199. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/get/route-295cb63da5f7262f.js +1 -0
  200. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/search/route-295cb63da5f7262f.js +1 -0
  201. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-get/route-295cb63da5f7262f.js +1 -0
  202. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-replace/route-295cb63da5f7262f.js +1 -0
  203. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/add/route-295cb63da5f7262f.js +1 -0
  204. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/apply/route-295cb63da5f7262f.js +1 -0
  205. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/batch/route-295cb63da5f7262f.js +1 -0
  206. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route-295cb63da5f7262f.js +1 -0
  207. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/del/route-295cb63da5f7262f.js +1 -0
  208. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/enabled-set/route-295cb63da5f7262f.js +1 -0
  209. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/get/route-295cb63da5f7262f.js +1 -0
  210. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route-295cb63da5f7262f.js +1 -0
  211. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/list/route-295cb63da5f7262f.js +1 -0
  212. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/reload/route-295cb63da5f7262f.js +1 -0
  213. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/update/route-295cb63da5f7262f.js +1 -0
  214. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/get/route-295cb63da5f7262f.js +1 -0
  215. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/list/route-295cb63da5f7262f.js +1 -0
  216. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/reveal/route-295cb63da5f7262f.js +1 -0
  217. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/search/route-295cb63da5f7262f.js +1 -0
  218. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git/get/route-295cb63da5f7262f.js +1 -0
  219. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-branches/list/route-295cb63da5f7262f.js +1 -0
  220. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-changes/list/route-295cb63da5f7262f.js +1 -0
  221. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-diff/get/route-295cb63da5f7262f.js +1 -0
  222. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-heads/get/route-295cb63da5f7262f.js +1 -0
  223. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-log/list/route-295cb63da5f7262f.js +1 -0
  224. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/get/route-295cb63da5f7262f.js +1 -0
  225. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/replace/route-295cb63da5f7262f.js +1 -0
  226. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/get/route-295cb63da5f7262f.js +1 -0
  227. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/list/route-295cb63da5f7262f.js +1 -0
  228. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/replace/route-295cb63da5f7262f.js +1 -0
  229. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skills/list/route-295cb63da5f7262f.js +1 -0
  230. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/images/get/route-295cb63da5f7262f.js +1 -0
  231. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/messages/get/route-295cb63da5f7262f.js +1 -0
  232. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/add/route-295cb63da5f7262f.js +1 -0
  233. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/replace/route-295cb63da5f7262f.js +1 -0
  234. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree/get/route-295cb63da5f7262f.js +1 -0
  235. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree-entry/get/route-295cb63da5f7262f.js +1 -0
  236. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/add/route-295cb63da5f7262f.js +1 -0
  237. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/branch/route-295cb63da5f7262f.js +1 -0
  238. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/del/route-295cb63da5f7262f.js +1 -0
  239. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/rename/route-295cb63da5f7262f.js +1 -0
  240. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/replace/route-295cb63da5f7262f.js +1 -0
  241. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/search/route-295cb63da5f7262f.js +1 -0
  242. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/sort/route-295cb63da5f7262f.js +1 -0
  243. package/temp/build/pi-desk/release/.next/static/chunks/app/layout-2c6a573d3004516e.js +1 -0
  244. package/temp/build/pi-desk/release/.next/static/chunks/app/login/{page-0d3d4827802108e5.js → page-0df9df996aa68060.js} +1 -1
  245. package/temp/build/pi-desk/release/.next/static/chunks/app/page-f78be007d97f0d95.js +158 -0
  246. package/temp/build/pi-desk/release/.next/static/chunks/app/project-preview/{page-038b6c14e03ed878.js → page-e1dbfcfc0f333e01.js} +1 -1
  247. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/app-error-295cb63da5f7262f.js +1 -0
  248. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/forbidden-295cb63da5f7262f.js +1 -0
  249. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/unauthorized-295cb63da5f7262f.js +1 -0
  250. package/temp/build/pi-desk/release/.next/static/css/{1e780a4936aa6ba3.css → 2bb393927ba8f71d.css} +1 -1
  251. package/temp/build/pi-desk/release/.next/static/css/3a07dcc86ffdf88c.css +1 -0
  252. package/temp/build/pi-desk/release/.next/static/css/a677e7fbf6e2173e.css +1 -0
  253. package/temp/build/pi-desk/release/.next/trace-build +1 -1
  254. package/temp/build/pi-desk/release/.next/server/chunks/35348.js +0 -3
  255. package/temp/build/pi-desk/release/.next/server/chunks/56830.js +0 -235
  256. package/temp/build/pi-desk/release/.next/server/chunks/80780.js +0 -1
  257. package/temp/build/pi-desk/release/.next/static/6a4eIEjmpW5l8Qlgtzzzg/_buildManifest.js +0 -1
  258. package/temp/build/pi-desk/release/.next/static/chunks/11937-1d0a9822f2c7995b.js +0 -1
  259. package/temp/build/pi-desk/release/.next/static/chunks/14197-3d3ddd426ac06c9c.js +0 -3
  260. package/temp/build/pi-desk/release/.next/static/chunks/37313-d5970e2d0c270d22.js +0 -238
  261. package/temp/build/pi-desk/release/.next/static/chunks/65232-ef4e5a4174f62faf.js +0 -15
  262. package/temp/build/pi-desk/release/.next/static/chunks/79267-7d21d7cdbd7cfca1.js +0 -1
  263. package/temp/build/pi-desk/release/.next/static/chunks/app/_global-error/page-9969fe0baa238786.js +0 -1
  264. package/temp/build/pi-desk/release/.next/static/chunks/app/_not-found/page-9969fe0baa238786.js +0 -1
  265. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/login/route-9969fe0baa238786.js +0 -1
  266. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/logout/route-9969fe0baa238786.js +0 -1
  267. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth-settings/get/route-9969fe0baa238786.js +0 -1
  268. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth-settings/replace/route-9969fe0baa238786.js +0 -1
  269. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/get/route-9969fe0baa238786.js +0 -1
  270. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/replace/route-9969fe0baa238786.js +0 -1
  271. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/context/get/route-9969fe0baa238786.js +0 -1
  272. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/images/get/route-9969fe0baa238786.js +0 -1
  273. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/messages/get/route-9969fe0baa238786.js +0 -1
  274. package/temp/build/pi-desk/release/.next/static/chunks/app/api/health/route-9969fe0baa238786.js +0 -1
  275. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-check/get/route-9969fe0baa238786.js +0 -1
  276. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/del/route-9969fe0baa238786.js +0 -1
  277. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/get/route-9969fe0baa238786.js +0 -1
  278. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/replace/route-9969fe0baa238786.js +0 -1
  279. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-catalog/list/route-9969fe0baa238786.js +0 -1
  280. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/get/route-9969fe0baa238786.js +0 -1
  281. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/preview/route-9969fe0baa238786.js +0 -1
  282. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/replace/route-9969fe0baa238786.js +0 -1
  283. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directories/list/route-9969fe0baa238786.js +0 -1
  284. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-entries/list/route-9969fe0baa238786.js +0 -1
  285. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/list/route-9969fe0baa238786.js +0 -1
  286. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/replace/route-9969fe0baa238786.js +0 -1
  287. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/models/list/route-9969fe0baa238786.js +0 -1
  288. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/session-user-messages/list/route-9969fe0baa238786.js +0 -1
  289. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/sessions/list/route-9969fe0baa238786.js +0 -1
  290. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/get/route-9969fe0baa238786.js +0 -1
  291. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/search/route-9969fe0baa238786.js +0 -1
  292. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-get/route-9969fe0baa238786.js +0 -1
  293. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-replace/route-9969fe0baa238786.js +0 -1
  294. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/add/route-9969fe0baa238786.js +0 -1
  295. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/apply/route-9969fe0baa238786.js +0 -1
  296. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/batch/route-9969fe0baa238786.js +0 -1
  297. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route-9969fe0baa238786.js +0 -1
  298. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/del/route-9969fe0baa238786.js +0 -1
  299. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/enabled-set/route-9969fe0baa238786.js +0 -1
  300. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/get/route-9969fe0baa238786.js +0 -1
  301. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route-9969fe0baa238786.js +0 -1
  302. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/list/route-9969fe0baa238786.js +0 -1
  303. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/reload/route-9969fe0baa238786.js +0 -1
  304. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/update/route-9969fe0baa238786.js +0 -1
  305. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/get/route-9969fe0baa238786.js +0 -1
  306. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/list/route-9969fe0baa238786.js +0 -1
  307. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/reveal/route-9969fe0baa238786.js +0 -1
  308. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/search/route-9969fe0baa238786.js +0 -1
  309. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git/get/route-9969fe0baa238786.js +0 -1
  310. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-branches/list/route-9969fe0baa238786.js +0 -1
  311. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-changes/list/route-9969fe0baa238786.js +0 -1
  312. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-diff/get/route-9969fe0baa238786.js +0 -1
  313. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-heads/get/route-9969fe0baa238786.js +0 -1
  314. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-log/list/route-9969fe0baa238786.js +0 -1
  315. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/get/route-9969fe0baa238786.js +0 -1
  316. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/replace/route-9969fe0baa238786.js +0 -1
  317. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/get/route-9969fe0baa238786.js +0 -1
  318. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/list/route-9969fe0baa238786.js +0 -1
  319. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/replace/route-9969fe0baa238786.js +0 -1
  320. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skills/list/route-9969fe0baa238786.js +0 -1
  321. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/images/get/route-9969fe0baa238786.js +0 -1
  322. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/messages/get/route-9969fe0baa238786.js +0 -1
  323. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/add/route-9969fe0baa238786.js +0 -1
  324. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/replace/route-9969fe0baa238786.js +0 -1
  325. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree/get/route-9969fe0baa238786.js +0 -1
  326. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree-entry/get/route-9969fe0baa238786.js +0 -1
  327. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/add/route-9969fe0baa238786.js +0 -1
  328. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/branch/route-9969fe0baa238786.js +0 -1
  329. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/del/route-9969fe0baa238786.js +0 -1
  330. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/rename/route-9969fe0baa238786.js +0 -1
  331. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/replace/route-9969fe0baa238786.js +0 -1
  332. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/sort/route-9969fe0baa238786.js +0 -1
  333. package/temp/build/pi-desk/release/.next/static/chunks/app/layout-20718f1503910291.js +0 -1
  334. package/temp/build/pi-desk/release/.next/static/chunks/app/page-18d63fd3920dfae0.js +0 -158
  335. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/app-error-9969fe0baa238786.js +0 -1
  336. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/forbidden-9969fe0baa238786.js +0 -1
  337. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/unauthorized-9969fe0baa238786.js +0 -1
  338. package/temp/build/pi-desk/release/.next/static/css/2c3b0c0ff1f49c68.css +0 -1
  339. package/temp/build/pi-desk/release/.next/static/css/c43b04c244a64fb4.css +0 -1
  340. /package/temp/build/pi-desk/release/.next/static/{6a4eIEjmpW5l8Qlgtzzzg → Yyfg-r17aYmEzlYYlIwDo}/_ssgManifest.js +0 -0
@@ -26,13 +26,20 @@ import {
26
26
  } from '@earendil-works/pi-coding-agent'
27
27
  import { z } from 'zod'
28
28
  import { findL4PiSessionFile } from './l4-pi-session-discovery'
29
+ import {
30
+ compactL4PiSessionPreview as compactPreview,
31
+ matchL4PiSessionSearch,
32
+ normalizeL4PiSessionSearch as normalizeSearch,
33
+ readL4PiSessionMessageText as messageText,
34
+ readL4PiSessionTimestamp as timestampMs,
35
+ type L4PiSessionSearchMatch,
36
+ type L4PiSessionSearchScope
37
+ } from './l4-pi-session-search'
29
38
 
30
39
  const SCAN_CONCURRENCY = 10
31
- const MATCH_PREVIEW_MAX_LENGTH = 120
32
- const MATCH_PREVIEW_PREFIX_LENGTH = 16
33
40
  const FIRST_MESSAGE_PREVIEW_MAX_LENGTH = 160
34
41
  const CACHE_VALIDATION_INTERVAL_MS = 2_000
35
- const CACHE_IDLE_MS = 15 * 60_000
42
+ const CACHE_IDLE_MS = 60_000
36
43
  const CACHE_CWD_LIMIT = 8
37
44
  const CACHE_TEXT_BYTES_LIMIT = 32 * 1024 * 1024
38
45
  const CACHE_FILE_LIMIT = 4_096
@@ -41,7 +48,7 @@ const LEGACY_CACHE_FILE_NAME = 'pi-session-history.json'
41
48
  const PERSISTED_CACHE_VERSION = 2
42
49
  const CACHE_DISK_BYTES_LIMIT = 64 * 1024 * 1024
43
50
 
44
- export type L4PiSessionHistorySearchScope = 'title' | 'user' | 'assistant' | 'sessionId'
51
+ export type L4PiSessionHistorySearchScope = L4PiSessionSearchScope
45
52
 
46
53
  const DEFAULT_SEARCH_IN: readonly L4PiSessionHistorySearchScope[] = ['title', 'user']
47
54
 
@@ -72,13 +79,7 @@ interface L4PiSessionAssistantMessage {
72
79
  text: string
73
80
  }
74
81
 
75
- export interface L4PiSessionHistoryMatch {
76
- scope: L4PiSessionHistorySearchScope
77
- count: number
78
- entryId: string | null
79
- timestampMs: number | null
80
- preview: string
81
- }
82
+ export type L4PiSessionHistoryMatch = L4PiSessionSearchMatch
82
83
 
83
84
  export interface L4PiSessionHistoryItem {
84
85
  sessionId: string
@@ -184,6 +185,7 @@ interface L4PiSessionHistoryCatalogState {
184
185
  epochs: Map<string, number>
185
186
  activeLoads: Map<string, L4PiSessionHistoryCatalogLoad>
186
187
  catalogs: Map<string, L4PiSessionHistoryCatalog>
188
+ cleanupTimer: ReturnType<typeof setTimeout> | null
187
189
  }
188
190
 
189
191
  interface L4PiSessionScanState {
@@ -365,7 +367,8 @@ function historyCatalogState(): L4PiSessionHistoryCatalogState {
365
367
  globalThis.__piDeskPiSessionHistoryCatalogState = {
366
368
  epochs: new Map(),
367
369
  activeLoads: new Map(),
368
- catalogs: new Map()
370
+ catalogs: new Map(),
371
+ cleanupTimer: null
369
372
  }
370
373
  }
371
374
  return globalThis.__piDeskPiSessionHistoryCatalogState
@@ -375,37 +378,6 @@ function isRecord(value: unknown): value is Record<string, unknown> {
375
378
  return typeof value === 'object' && value !== null && !Array.isArray(value)
376
379
  }
377
380
 
378
- function timestampMs(value: unknown): number | null {
379
- if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value
380
- if (typeof value !== 'string') return null
381
- const parsed = Date.parse(value)
382
- return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null
383
- }
384
-
385
- function normalizeSearch(value: string): string {
386
- return value.trim().toLowerCase()
387
- }
388
-
389
- function compactPreview(text: string, maxLength: number): string {
390
- const content = text.replace(/\s+/g, ' ').trim()
391
- if (content.length <= maxLength) return content
392
- return `${content.slice(0, maxLength - 1).trimEnd()}…`
393
- }
394
-
395
- function messageText(message: unknown): string {
396
- if (!isRecord(message)) return ''
397
- const content = message.content
398
- if (typeof content === 'string') return content
399
- if (!Array.isArray(content)) return ''
400
-
401
- return content
402
- .flatMap((part) => {
403
- if (!isRecord(part) || part.type !== 'text' || typeof part.text !== 'string') return []
404
- return [part.text]
405
- })
406
- .join('\n')
407
- }
408
-
409
381
  function messageTimestamp(entry: Record<string, unknown>, fallback: number): number {
410
382
  const entryTimestamp = timestampMs(entry.timestamp)
411
383
  if (entryTimestamp !== null) return entryTimestamp
@@ -696,11 +668,31 @@ async function refreshCatalog(
696
668
  }
697
669
  }
698
670
 
699
- function pruneCatalogs(now: number): void {
700
- const state = historyCatalogState()
671
+ function scheduleCatalogRelease(state: L4PiSessionHistoryCatalogState): void {
672
+ if (state.cleanupTimer) clearTimeout(state.cleanupTimer)
673
+ state.cleanupTimer = null
674
+ if (state.catalogs.size === 0) return
675
+ const expiresAt = Math.min(
676
+ ...Array.from(state.catalogs.values(), (catalog) => catalog.lastAccessedAt + CACHE_IDLE_MS)
677
+ )
678
+ state.cleanupTimer = setTimeout(
679
+ () => {
680
+ state.cleanupTimer = null
681
+ pruneCatalogs(Date.now(), state)
682
+ },
683
+ Math.max(0, expiresAt - Date.now())
684
+ )
685
+ state.cleanupTimer.unref()
686
+ }
687
+
688
+ function pruneCatalogs(
689
+ now: number,
690
+ state: L4PiSessionHistoryCatalogState = historyCatalogState()
691
+ ): void {
701
692
  for (const [cwd, catalog] of state.catalogs) {
702
- if (now - catalog.lastAccessedAt > CACHE_IDLE_MS) state.catalogs.delete(cwd)
693
+ if (now - catalog.lastAccessedAt >= CACHE_IDLE_MS) state.catalogs.delete(cwd)
703
694
  }
695
+ scheduleCatalogRelease(state)
704
696
  }
705
697
 
706
698
  function touchCatalog(catalog: L4PiSessionHistoryCatalog, now: number): L4PiSessionHistoryCatalog {
@@ -708,6 +700,7 @@ function touchCatalog(catalog: L4PiSessionHistoryCatalog, now: number): L4PiSess
708
700
  const state = historyCatalogState()
709
701
  state.catalogs.delete(catalog.cwd)
710
702
  state.catalogs.set(catalog.cwd, catalog)
703
+ scheduleCatalogRelease(state)
711
704
  return catalog
712
705
  }
713
706
 
@@ -715,6 +708,7 @@ function storeCatalog(catalog: L4PiSessionHistoryCatalog, persist: boolean): voi
715
708
  const state = historyCatalogState()
716
709
  if (catalog.files.size > CACHE_FILE_LIMIT || catalog.textBytes > CACHE_TEXT_BYTES_LIMIT) {
717
710
  state.catalogs.delete(catalog.cwd)
711
+ scheduleCatalogRelease(state)
718
712
  if (persist) {
719
713
  const path = persistedHistoryCachePath(catalog.cwd)
720
714
  try {
@@ -747,6 +741,7 @@ function storeCatalog(catalog: L4PiSessionHistoryCatalog, persist: boolean): voi
747
741
  state.catalogs.delete(oldest[0])
748
742
  totalBytes -= oldest[1].textBytes
749
743
  }
744
+ scheduleCatalogRelease(state)
750
745
  if (persist) persistHistoryCatalog(catalog)
751
746
  }
752
747
 
@@ -768,8 +763,9 @@ async function getCatalog(
768
763
  const state = historyCatalogState()
769
764
  const now = Date.now()
770
765
  pruneCatalogs(now)
771
- const catalog = state.catalogs.get(cwd) ?? readPersistedHistoryCatalog(cwd)
772
- const epoch = forceRefresh ? incrementEpoch(cwd) : currentEpoch(cwd)
766
+ const cached = state.catalogs.get(cwd)
767
+ const catalog = cached ? touchCatalog(cached, now) : readPersistedHistoryCatalog(cwd)
768
+ const epoch = forceRefresh && state.activeLoads.has(cwd) ? incrementEpoch(cwd) : currentEpoch(cwd)
773
769
  if (forceRefresh && catalog) catalog.dirty = true
774
770
  const active = state.activeLoads.get(cwd)
775
771
  if (active && active.epoch === epoch) return active.promise
@@ -783,10 +779,12 @@ async function getCatalog(
783
779
  return touchCatalog(catalog, now)
784
780
  }
785
781
 
786
- const load = (async (): Promise<L4PiSessionHistoryCatalog> => {
782
+ const load: Promise<L4PiSessionHistoryCatalog> = Promise.resolve().then(async () => {
787
783
  const refresh = await refreshCatalog(cwd, catalog)
788
784
  const refreshed = refresh.catalog
789
- if (currentEpoch(cwd) === epoch) storeCatalog(refreshed, refresh.changed)
785
+ if (state.activeLoads.get(cwd)?.promise === load && currentEpoch(cwd) === epoch) {
786
+ storeCatalog(refreshed, refresh.changed)
787
+ }
790
788
  console.info('[Pi Desk][PiSessionHistoryCatalog] 已更新历史会话内存投影', {
791
789
  cwd,
792
790
  sessionCount: refreshed.sessions.length,
@@ -795,43 +793,15 @@ async function getCatalog(
795
793
  forceRefresh
796
794
  })
797
795
  return refreshed
798
- })()
796
+ })
799
797
  state.activeLoads.set(cwd, { epoch, promise: load })
800
798
  try {
801
799
  return await load
802
800
  } finally {
803
- if (state.activeLoads.get(cwd)?.promise === load) state.activeLoads.delete(cwd)
804
- }
805
- }
806
-
807
- function previewForMatch(text: string, normalizedQuery: string): string {
808
- const normalizedText = text.toLowerCase()
809
- const matchIndex = normalizedText.indexOf(normalizedQuery)
810
- if (matchIndex < 0) return ''
811
-
812
- const start = Math.max(0, matchIndex - MATCH_PREVIEW_PREFIX_LENGTH)
813
- const requiredEnd = matchIndex + normalizedQuery.length
814
- const end = Math.min(text.length, Math.max(start + MATCH_PREVIEW_MAX_LENGTH, requiredEnd))
815
- const content = text.slice(start, end).replace(/\s+/g, ' ').trim()
816
- return `${start > 0 ? '…' : ''}${content}${end < text.length ? '…' : ''}`
817
- }
818
-
819
- function messageMatch(
820
- scope: 'user' | 'assistant',
821
- messages: readonly L4PiSessionUserMessage[] | readonly L4PiSessionAssistantMessage[],
822
- normalizedQuery: string
823
- ): L4PiSessionHistoryMatch | null {
824
- const matching = messages.filter((message) =>
825
- message.text.toLowerCase().includes(normalizedQuery)
826
- )
827
- if (matching.length === 0) return null
828
- const latest = matching.at(-1)!
829
- return {
830
- scope,
831
- count: matching.length,
832
- entryId: latest.entryId,
833
- timestampMs: latest.timestampMs,
834
- preview: previewForMatch(latest.text, normalizedQuery)
801
+ if (state.activeLoads.get(cwd)?.promise === load) {
802
+ state.activeLoads.delete(cwd)
803
+ state.epochs.delete(cwd)
804
+ }
835
805
  }
836
806
  }
837
807
 
@@ -854,33 +824,7 @@ function projectHistoryItem(
854
824
  }
855
825
  }
856
826
 
857
- const matches: L4PiSessionHistoryMatch[] = []
858
- if (searchIn.has('title') && session.name?.toLowerCase().includes(normalizedQuery)) {
859
- matches.push({
860
- scope: 'title',
861
- count: 1,
862
- entryId: null,
863
- timestampMs: null,
864
- preview: compactPreview(session.name, FIRST_MESSAGE_PREVIEW_MAX_LENGTH)
865
- })
866
- }
867
- if (searchIn.has('user')) {
868
- const match = messageMatch('user', session.userMessages, normalizedQuery)
869
- if (match) matches.push(match)
870
- }
871
- if (searchIn.has('assistant')) {
872
- const match = messageMatch('assistant', session.assistantMessages, normalizedQuery)
873
- if (match) matches.push(match)
874
- }
875
- if (searchIn.has('sessionId') && session.sessionId.toLowerCase().includes(normalizedQuery)) {
876
- matches.push({
877
- scope: 'sessionId',
878
- count: 1,
879
- entryId: null,
880
- timestampMs: null,
881
- preview: session.sessionId
882
- })
883
- }
827
+ const matches = matchL4PiSessionSearch(session, normalizedQuery, searchIn)
884
828
  if (matches.length === 0) return null
885
829
 
886
830
  return {
@@ -917,7 +861,10 @@ async function getSessionRecord(
917
861
  ): Promise<L4PiSessionHistoryRecord | null> {
918
862
  const cwd = resolve(cwdInput)
919
863
  pruneCatalogs(Date.now())
920
- const catalog = historyCatalogState().catalogs.get(cwd) ?? readPersistedHistoryCatalog(cwd)
864
+ const memoryCatalog = historyCatalogState().catalogs.get(cwd)
865
+ const catalog = memoryCatalog
866
+ ? touchCatalog(memoryCatalog, Date.now())
867
+ : readPersistedHistoryCatalog(cwd)
921
868
  const previous = catalog?.sessions.find((session) => session.sessionId === sessionId)
922
869
  const path = await findL4PiSessionFile(cwd, sessionId, previous?.path)
923
870
  if (!path) return null
@@ -972,8 +919,10 @@ export function resolveCachedL4PiSessionHistoryPath(
972
919
  cwd: string,
973
920
  sessionId: string
974
921
  ): string | undefined {
922
+ pruneCatalogs(Date.now())
975
923
  const catalog = historyCatalogState().catalogs.get(resolve(cwd))
976
924
  if (!catalog || catalog.dirty) return undefined
925
+ touchCatalog(catalog, Date.now())
977
926
  return catalog.sessions.find((session) => session.sessionId === sessionId)?.path
978
927
  }
979
928
 
@@ -981,7 +930,7 @@ export function invalidateL4PiSessionHistoryCache(cwd?: string): void {
981
930
  const state = historyCatalogState()
982
931
  if (cwd) {
983
932
  const normalizedCwd = resolve(cwd)
984
- incrementEpoch(normalizedCwd)
933
+ if (state.activeLoads.has(normalizedCwd)) incrementEpoch(normalizedCwd)
985
934
  const catalog = state.catalogs.get(normalizedCwd)
986
935
  if (catalog) catalog.dirty = true
987
936
  return
@@ -989,7 +938,7 @@ export function invalidateL4PiSessionHistoryCache(cwd?: string): void {
989
938
 
990
939
  const affectedCwds = new Set([...state.catalogs.keys(), ...state.activeLoads.keys()])
991
940
  for (const catalogCwd of affectedCwds) {
992
- incrementEpoch(catalogCwd)
941
+ if (state.activeLoads.has(catalogCwd)) incrementEpoch(catalogCwd)
993
942
  const catalog = state.catalogs.get(catalogCwd)
994
943
  if (catalog) catalog.dirty = true
995
944
  }
@@ -0,0 +1,166 @@
1
+ import 'server-only'
2
+
3
+ import type { SessionManager } from '@earendil-works/pi-coding-agent'
4
+
5
+ export type L4PiSessionSearchScope = 'title' | 'user' | 'assistant' | 'sessionId'
6
+
7
+ export interface L4PiSessionSearchMatch {
8
+ scope: L4PiSessionSearchScope
9
+ count: number
10
+ entryId: string | null
11
+ timestampMs: number | null
12
+ preview: string
13
+ }
14
+
15
+ export interface L4PiSessionSearchQuery {
16
+ query: string
17
+ searchIn: readonly L4PiSessionSearchScope[]
18
+ }
19
+
20
+ const MATCH_PREVIEW_MAX_LENGTH = 120
21
+ const MATCH_PREVIEW_PREFIX_LENGTH = 16
22
+
23
+ export interface L4PiSessionSearchMessage {
24
+ entryId: string
25
+ timestampMs: number
26
+ text: string
27
+ }
28
+
29
+ function isRecord(value: unknown): value is Record<string, unknown> {
30
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
31
+ }
32
+
33
+ export function readL4PiSessionTimestamp(value: unknown): number | null {
34
+ if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) return value
35
+ if (typeof value !== 'string') return null
36
+ const parsed = Date.parse(value)
37
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null
38
+ }
39
+
40
+ export function normalizeL4PiSessionSearch(value: string): string {
41
+ return value.trim().toLowerCase()
42
+ }
43
+
44
+ export function compactL4PiSessionPreview(text: string, maxLength: number): string {
45
+ const content = text.replace(/\s+/g, ' ').trim()
46
+ if (content.length <= maxLength) return content
47
+ return `${content.slice(0, maxLength - 1).trimEnd()}…`
48
+ }
49
+
50
+ export function readL4PiSessionMessageText(message: unknown): string {
51
+ if (!isRecord(message)) return ''
52
+ const content = message.content
53
+ if (typeof content === 'string') return content
54
+ if (!Array.isArray(content)) return ''
55
+ return content
56
+ .flatMap((part) => {
57
+ if (!isRecord(part) || part.type !== 'text' || typeof part.text !== 'string') return []
58
+ return [part.text]
59
+ })
60
+ .join('\n')
61
+ }
62
+
63
+ function previewForMatch(text: string, normalizedQuery: string): string {
64
+ const matchIndex = text.toLowerCase().indexOf(normalizedQuery)
65
+ if (matchIndex < 0) return ''
66
+ const start = Math.max(0, matchIndex - MATCH_PREVIEW_PREFIX_LENGTH)
67
+ const requiredEnd = matchIndex + normalizedQuery.length
68
+ const end = Math.min(text.length, Math.max(start + MATCH_PREVIEW_MAX_LENGTH, requiredEnd))
69
+ const content = text.slice(start, end).replace(/\s+/g, ' ').trim()
70
+ return `${start > 0 ? '…' : ''}${content}${end < text.length ? '…' : ''}`
71
+ }
72
+
73
+ function messageMatch(
74
+ scope: 'user' | 'assistant',
75
+ messages: Iterable<L4PiSessionSearchMessage>,
76
+ normalizedQuery: string
77
+ ): L4PiSessionSearchMatch | null {
78
+ let count = 0
79
+ let latest: L4PiSessionSearchMessage | undefined
80
+ for (const message of messages) {
81
+ if (!message.text.toLowerCase().includes(normalizedQuery)) continue
82
+ count += 1
83
+ latest = message
84
+ }
85
+ if (!latest) return null
86
+ return {
87
+ scope,
88
+ count,
89
+ entryId: latest.entryId,
90
+ timestampMs: latest.timestampMs,
91
+ preview: previewForMatch(latest.text, normalizedQuery)
92
+ }
93
+ }
94
+
95
+ export function matchL4PiSessionSearch(
96
+ session: {
97
+ sessionId: string
98
+ name: string | null
99
+ userMessages: Iterable<L4PiSessionSearchMessage>
100
+ assistantMessages: Iterable<L4PiSessionSearchMessage>
101
+ },
102
+ normalizedQuery: string,
103
+ searchIn: ReadonlySet<L4PiSessionSearchScope>
104
+ ): L4PiSessionSearchMatch[] {
105
+ if (!normalizedQuery) return []
106
+ const matches: L4PiSessionSearchMatch[] = []
107
+ if (searchIn.has('title') && session.name?.toLowerCase().includes(normalizedQuery)) {
108
+ matches.push({
109
+ scope: 'title',
110
+ count: 1,
111
+ entryId: null,
112
+ timestampMs: null,
113
+ preview: compactL4PiSessionPreview(session.name, 160)
114
+ })
115
+ }
116
+ if (searchIn.has('user')) {
117
+ const match = messageMatch('user', session.userMessages, normalizedQuery)
118
+ if (match) matches.push(match)
119
+ }
120
+ if (searchIn.has('assistant')) {
121
+ const match = messageMatch('assistant', session.assistantMessages, normalizedQuery)
122
+ if (match) matches.push(match)
123
+ }
124
+ if (searchIn.has('sessionId') && session.sessionId.toLowerCase().includes(normalizedQuery)) {
125
+ matches.push({
126
+ scope: 'sessionId',
127
+ count: 1,
128
+ entryId: null,
129
+ timestampMs: null,
130
+ preview: session.sessionId
131
+ })
132
+ }
133
+ return matches
134
+ }
135
+
136
+ export function searchL4PiSessionBranch(
137
+ sessionManager: SessionManager,
138
+ input: L4PiSessionSearchQuery
139
+ ): L4PiSessionSearchMatch[] {
140
+ const branch = sessionManager.getBranch()
141
+ function* messages(role: 'user' | 'assistant'): Generator<L4PiSessionSearchMessage> {
142
+ for (const entry of branch) {
143
+ if (entry.type !== 'message' || entry.message.role !== role) continue
144
+ const text = readL4PiSessionMessageText(entry.message)
145
+ if (!text.trim()) continue
146
+ yield {
147
+ entryId: entry.id,
148
+ timestampMs:
149
+ readL4PiSessionTimestamp(entry.timestamp) ??
150
+ readL4PiSessionTimestamp(entry.message.timestamp) ??
151
+ 0,
152
+ text
153
+ }
154
+ }
155
+ }
156
+ return matchL4PiSessionSearch(
157
+ {
158
+ sessionId: sessionManager.getSessionId(),
159
+ name: sessionManager.getSessionName()?.trim() || null,
160
+ userMessages: messages('user'),
161
+ assistantMessages: messages('assistant')
162
+ },
163
+ normalizeL4PiSessionSearch(input.query),
164
+ new Set(input.searchIn)
165
+ )
166
+ }
@@ -4,6 +4,11 @@ import { existsSync } from 'node:fs'
4
4
  import { resolve } from 'node:path'
5
5
  import { ModelRuntime, SessionManager, type SessionEntry } from '@earendil-works/pi-coding-agent'
6
6
  import type { PluginJsonObject, SessionPluginSource } from '@jetcrab/pi-desk-sdk'
7
+ import {
8
+ searchL4PiSessionBranch,
9
+ type L4PiSessionSearchMatch,
10
+ type L4PiSessionSearchQuery
11
+ } from './l4-pi-session-search'
7
12
  import { getL4CapabilityModesStore } from '@server/l4_foundation/capability-modes/l4-capability-modes-store'
8
13
  import {
9
14
  L4PiChatWorker,
@@ -17,7 +22,7 @@ import {
17
22
  type L4PiNativeCommandInfo,
18
23
  type L4PiNativeToolInfo
19
24
  } from './l4-pi-chat-worker'
20
- import type { L4PiInputImage } from './l4-pi-image'
25
+ import { decodeL4PiImage, type L4PiInputImage } from './l4-pi-image'
21
26
  import type { L4PiModelCatalog } from './l4-pi-model-catalog'
22
27
  import { L4PiSessionBranchIndex, type L4PendingBranchAppend } from './l4-pi-session-branch'
23
28
  import {
@@ -48,6 +53,19 @@ export class L4PiSessionBranchBlockedError extends Error {
48
53
  }
49
54
  }
50
55
 
56
+ export class L4PiSessionResendTargetError extends Error {
57
+ constructor(entryId: string) {
58
+ super(`Pi session resend target is not a user message: ${entryId}`)
59
+ this.name = 'L4PiSessionResendTargetError'
60
+ }
61
+ }
62
+
63
+ export interface L4PiResendInput {
64
+ mode: 'resend'
65
+ entryId: string
66
+ onBranchChanged: () => Promise<void>
67
+ }
68
+
51
69
  export class L4PiSessionCloneEmptyError extends Error {
52
70
  constructor() {
53
71
  super('Pi session has no durable entry to clone')
@@ -166,6 +184,11 @@ export class L4PiWorkSessionRuntime {
166
184
  return projectL4PiChatSnapshot(this.sessionManager)
167
185
  }
168
186
 
187
+ search(input: L4PiSessionSearchQuery): L4PiSessionSearchMatch[] {
188
+ this.assertActive()
189
+ return searchL4PiSessionBranch(this.sessionManager, input)
190
+ }
191
+
169
192
  async readSessionTree(): Promise<L4PiSessionTree> {
170
193
  this.assertActive()
171
194
  const models = this.sessionManager.getEntries().some((entry) => entry.type === 'model_change')
@@ -290,9 +313,35 @@ export class L4PiWorkSessionRuntime {
290
313
  await this.chatWorker.abortDirectBash()
291
314
  }
292
315
 
293
- async send(input: L4PiChatSendInput): Promise<{ tempId: string }> {
316
+ async send(input: L4PiChatSendInput | L4PiResendInput): Promise<{ tempId: string }> {
294
317
  this.assertActive()
295
- return this.chatWorker.send(input)
318
+ if (input.mode !== 'resend') return this.chatWorker.send(input)
319
+
320
+ const entry = this.sessionManager.getEntry(input.entryId)
321
+ if (!entry) throw new L4PiSessionEntryNotFoundError(input.entryId)
322
+ if (entry.type !== 'message' || entry.message.role !== 'user') {
323
+ throw new L4PiSessionResendTargetError(input.entryId)
324
+ }
325
+ const content = entry.message.content
326
+ const text =
327
+ typeof content === 'string'
328
+ ? content
329
+ : content.flatMap((part) => (part.type === 'text' ? [part.text] : [])).join('')
330
+ const images =
331
+ typeof content === 'string'
332
+ ? []
333
+ : content.flatMap((part) =>
334
+ part.type === 'image'
335
+ ? [{ ...decodeL4PiImage(part.data).metadata, data: part.data }]
336
+ : []
337
+ )
338
+ const blockReason = this.chatWorker.readPluginReloadBlockReason()
339
+ if (blockReason) throw new L4PiSessionBranchBlockedError(blockReason)
340
+ const selection = await this.navigateBranch(input.entryId, true, input.onBranchChanged)
341
+ if (!selection.changed) {
342
+ throw new L4PiSessionBranchBlockedError('分支切换已取消,未重发消息')
343
+ }
344
+ return this.chatWorker.send({ mode: 'auto', text, images })
296
345
  }
297
346
 
298
347
  async interrupt(): Promise<void> {
@@ -375,10 +424,20 @@ export class L4PiWorkSessionRuntime {
375
424
  }
376
425
 
377
426
  async branch(entryId: string): Promise<L4PiBranchSelection> {
427
+ return this.navigateBranch(entryId)
428
+ }
429
+
430
+ private async navigateBranch(
431
+ entryId: string,
432
+ beforeUserMessage = false,
433
+ onBranchChanged?: () => Promise<void>
434
+ ): Promise<L4PiBranchSelection> {
378
435
  this.assertActive()
379
436
  this.assertBranchable()
380
437
  this.syncBranchIndex()
381
- const selection = resolveL4PiBranchSelection(this.sessionManager, entryId)
438
+ const selection: L4PiBranchSelection = beforeUserMessage
439
+ ? { ...resolveL4PiForkSelection(this.sessionManager, entryId), changed: true }
440
+ : resolveL4PiBranchSelection(this.sessionManager, entryId)
382
441
  if (!selection.changed) return selection
383
442
 
384
443
  const previousLeaf = this.sessionManager.getLeafId()
@@ -387,7 +446,7 @@ export class L4PiWorkSessionRuntime {
387
446
  this.pendingBranchAppend = null
388
447
  let changed = false
389
448
  try {
390
- changed = await this.chatWorker.navigateTree(selection.selectedEntryId)
449
+ changed = await this.chatWorker.navigateTree(selection.selectedEntryId, beforeUserMessage)
391
450
  return changed
392
451
  ? selection
393
452
  : {
@@ -402,6 +461,8 @@ export class L4PiWorkSessionRuntime {
402
461
  if (changed || leaf !== previousLeaf) {
403
462
  this.pendingBranchAppend = this.branchIndex.predictAppend(leaf)
404
463
  this.branchIdValue = this.pendingBranchAppend.branchId
464
+ // 包括后置钩子抛错的已提交导航;刷新后再恢复消息,不能丢失临时消息。
465
+ await onBranchChanged?.()
405
466
  } else {
406
467
  this.pendingBranchAppend = previousPending
407
468
  }