@xopcai/xopc 0.0.8 → 0.0.11

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 (242) hide show
  1. package/dist/extensions/telegram/src/plugin.js +1 -1
  2. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  3. package/dist/extensions/weixin/src/plugin.js +1 -1
  4. package/dist/gateway/static/root/assets/{agents-BSNzJWbQ.js → agents-BdC4Y-HX.js} +2 -2
  5. package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
  6. package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js → apps-page-C-oaSHkm.js} +2 -2
  7. package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js.map → apps-page-C-oaSHkm.js.map} +1 -1
  8. package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
  9. package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js → channels-settings-BqEUppPO.js} +2 -2
  10. package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js.map → channels-settings-BqEUppPO.js.map} +1 -1
  11. package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js → chat-agents-api-BhqjQ7iL.js} +2 -2
  12. package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
  13. package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js → cron-page-Cli49RKR.js} +2 -2
  14. package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js.map → cron-page-Cli49RKR.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js → cron-utils-Dkj-Ldpf.js} +2 -2
  16. package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
  17. package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js → electron-env-BDtJw9AY.js} +2 -2
  18. package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js.map → electron-env-BDtJw9AY.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js → extension-debug-page-BMcZlaxF.js} +2 -2
  20. package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
  21. package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js → extension-iframe-host-D5HEF0KR.js} +2 -2
  22. package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
  23. package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js → extension-page-CXdCSSPl.js} +2 -2
  24. package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js.map → extension-page-CXdCSSPl.js.map} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js → extension-provider-DZCZgQE2.js} +2 -2
  26. package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js → extension-settings-page-CX6STpx3.js} +2 -2
  28. package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
  29. package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js → gateway-config-swr-Cph02QZn.js} +2 -2
  30. package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
  31. package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
  32. package/dist/gateway/static/root/assets/{index-BXUJbteW.js → index-iTUyfzNr.js} +10 -10
  33. package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
  34. package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js → logs-page-B9O5l3I8.js} +2 -2
  35. package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js.map → logs-page-B9O5l3I8.js.map} +1 -1
  36. package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js → model-selector-BLiY_O25.js} +2 -2
  37. package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js.map → model-selector-BLiY_O25.js.map} +1 -1
  38. package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
  39. package/dist/gateway/static/root/assets/{page-header-store-DJHD9Ean.js.map → page-header-store-BFpnFTed.js.map} +1 -1
  40. package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js → session-api-DEhQXWJg.js} +2 -2
  41. package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js.map → session-api-DEhQXWJg.js.map} +1 -1
  42. package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js → session-working-directory-control-DKOtWs3-.js} +2 -2
  43. package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
  44. package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js → sessions-page-BYlWP1ep.js} +2 -2
  45. package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
  46. package/dist/gateway/static/root/assets/{settings-page-B3QrJm-E.js → settings-page-oCnIavdg.js} +2 -2
  47. package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
  48. package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js → skill-api-DWrn8Az0.js} +2 -2
  49. package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js.map → skill-api-DWrn8Az0.js.map} +1 -1
  50. package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js → skills-page-C59WQpM1.js} +2 -2
  51. package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js.map → skills-page-C59WQpM1.js.map} +1 -1
  52. package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js → theme-store-CywXkKml.js} +2 -2
  53. package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js.map → theme-store-CywXkKml.js.map} +1 -1
  54. package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
  55. package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
  56. package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js → useTranslation-CACj0DBJ.js} +2 -2
  57. package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
  58. package/dist/gateway/static/root/index.html +15 -15
  59. package/dist/package.js +1 -1
  60. package/dist/src/agent/agent-manager.d.ts +1 -0
  61. package/dist/src/agent/agent-manager.js +20 -12
  62. package/dist/src/agent/agent-manager.js.map +1 -1
  63. package/dist/src/agent/background-review/run-background-review.js +2 -0
  64. package/dist/src/agent/background-review/run-background-review.js.map +1 -1
  65. package/dist/src/agent/child-agent-factory.js +2 -0
  66. package/dist/src/agent/child-agent-factory.js.map +1 -1
  67. package/dist/src/agent/context/expand-at-file-mentions.d.ts +4 -0
  68. package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
  69. package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
  70. package/dist/src/agent/context/workspace-seed.js +1 -1
  71. package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
  72. package/dist/src/agent/ipc/bus.js +1 -1
  73. package/dist/src/agent/ipc/inbox.js +1 -1
  74. package/dist/src/agent/ipc/socket.js +1 -1
  75. package/dist/src/agent/memory/compaction.d.ts +1 -1
  76. package/dist/src/agent/memory/compaction.js +38 -11
  77. package/dist/src/agent/memory/compaction.js.map +1 -1
  78. package/dist/src/agent/messaging/command-handler.d.ts +13 -0
  79. package/dist/src/agent/messaging/command-handler.js +14 -2
  80. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  81. package/dist/src/agent/models/manager.js +1 -1
  82. package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
  83. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  84. package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
  85. package/dist/src/agent/service.d.ts +16 -1
  86. package/dist/src/agent/service.js +178 -20
  87. package/dist/src/agent/service.js.map +1 -1
  88. package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
  89. package/dist/src/agent/skills/index.js +1 -1
  90. package/dist/src/agent/skills/scanner.js +1 -1
  91. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  92. package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
  93. package/dist/src/agent/skills/skill-manager.js +1 -1
  94. package/dist/src/agent/tools/browser/tools.js.map +1 -1
  95. package/dist/src/agent/tools/factory.js +1 -1
  96. package/dist/src/agent/tools/image-tool.js.map +1 -1
  97. package/dist/src/agent/tools/send-media.js +1 -1
  98. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  99. package/dist/src/agent/tools/write.js +1 -1
  100. package/dist/src/auth/credentials.js +2 -2
  101. package/dist/src/auth/sync-provider-auth.js +1 -1
  102. package/dist/src/channels/attachments/inbound-persist.js +1 -1
  103. package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
  104. package/dist/src/channels/index.d.ts +1 -1
  105. package/dist/src/channels/index.js +2 -2
  106. package/dist/src/channels/pipeline.d.ts +8 -1
  107. package/dist/src/channels/pipeline.js +49 -4
  108. package/dist/src/channels/pipeline.js.map +1 -1
  109. package/dist/src/chat-commands/builtins/config.d.ts +4 -0
  110. package/dist/src/chat-commands/builtins/config.js +197 -0
  111. package/dist/src/chat-commands/builtins/config.js.map +1 -0
  112. package/dist/src/chat-commands/builtins/context.d.ts +4 -0
  113. package/dist/src/chat-commands/builtins/context.js +44 -0
  114. package/dist/src/chat-commands/builtins/context.js.map +1 -0
  115. package/dist/src/chat-commands/builtins/session.js +111 -0
  116. package/dist/src/chat-commands/builtins/session.js.map +1 -1
  117. package/dist/src/chat-commands/builtins/thinking.js +49 -21
  118. package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
  119. package/dist/src/chat-commands/config-paths.d.ts +10 -0
  120. package/dist/src/chat-commands/config-paths.js +45 -0
  121. package/dist/src/chat-commands/config-paths.js.map +1 -0
  122. package/dist/src/chat-commands/config-value.d.ts +12 -0
  123. package/dist/src/chat-commands/config-value.js +53 -0
  124. package/dist/src/chat-commands/config-value.js.map +1 -0
  125. package/dist/src/chat-commands/context.d.ts +24 -1
  126. package/dist/src/chat-commands/context.js +41 -0
  127. package/dist/src/chat-commands/context.js.map +1 -1
  128. package/dist/src/chat-commands/index.d.ts +2 -0
  129. package/dist/src/chat-commands/index.js +5 -1
  130. package/dist/src/chat-commands/index.js.map +1 -1
  131. package/dist/src/chat-commands/types.d.ts +33 -1
  132. package/dist/src/cli/commands/agent/interactive.js +1 -1
  133. package/dist/src/cli/commands/agent/interactive.js.map +1 -1
  134. package/dist/src/cli/commands/agent.js +22 -10
  135. package/dist/src/cli/commands/agent.js.map +1 -1
  136. package/dist/src/cli/commands/auth.js.map +1 -1
  137. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  138. package/dist/src/cli/commands/extension.js +10 -0
  139. package/dist/src/cli/commands/extension.js.map +1 -1
  140. package/dist/src/cli/commands/init.js +3 -4
  141. package/dist/src/cli/commands/init.js.map +1 -1
  142. package/dist/src/cli/commands/session/utils.js.map +1 -1
  143. package/dist/src/cli/commands/update.d.ts +1 -0
  144. package/dist/src/cli/commands/update.js +171 -0
  145. package/dist/src/cli/commands/update.js.map +1 -0
  146. package/dist/src/cli/index.d.ts +1 -1
  147. package/dist/src/cli/index.js +3 -1
  148. package/dist/src/cli/index.js.map +1 -1
  149. package/dist/src/config/index.d.ts +1 -0
  150. package/dist/src/config/index.js +5 -4
  151. package/dist/src/config/index.js.map +1 -1
  152. package/dist/src/config/loader.js +1 -1
  153. package/dist/src/config/models-json.js +1 -1
  154. package/dist/src/config/paths.js.map +1 -1
  155. package/dist/src/config/profile.js +2 -2
  156. package/dist/src/config/runtime-overrides.d.ts +8 -0
  157. package/dist/src/config/runtime-overrides.js +40 -0
  158. package/dist/src/config/runtime-overrides.js.map +1 -0
  159. package/dist/src/config/schema.d.ts +35 -0
  160. package/dist/src/config/schema.js +19 -3
  161. package/dist/src/config/schema.js.map +1 -1
  162. package/dist/src/cron/executor.js +2 -2
  163. package/dist/src/cron/persistence.js +1 -1
  164. package/dist/src/cron/run-log-store.js +1 -1
  165. package/dist/src/extensions/health.js +1 -1
  166. package/dist/src/extensions/loader.d.ts +1 -1
  167. package/dist/src/extensions/loader.js +6 -9
  168. package/dist/src/extensions/loader.js.map +1 -1
  169. package/dist/src/extensions/lockfile.js +1 -1
  170. package/dist/src/extensions/sdk/index.js +6 -1
  171. package/dist/src/extensions/sdk/index.js.map +1 -0
  172. package/dist/src/gateway/agents-admin.js +2 -2
  173. package/dist/src/gateway/agents-admin.js.map +1 -1
  174. package/dist/src/gateway/hono/oauth.js +1 -1
  175. package/dist/src/gateway/hono/routes/config.js +1 -1
  176. package/dist/src/gateway/hono/routes/index.js +2 -0
  177. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  178. package/dist/src/gateway/hono/routes/models.js +64 -11
  179. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  180. package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
  181. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  182. package/dist/src/gateway/hono/routes/update.d.ts +3 -0
  183. package/dist/src/gateway/hono/routes/update.js +141 -0
  184. package/dist/src/gateway/hono/routes/update.js.map +1 -0
  185. package/dist/src/gateway/hono/routes/workspace.js +84 -4
  186. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  187. package/dist/src/gateway/hono/sse.js +2 -2
  188. package/dist/src/gateway/service.d.ts +1 -0
  189. package/dist/src/gateway/service.js +16 -4
  190. package/dist/src/gateway/service.js.map +1 -1
  191. package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
  192. package/dist/src/gateway/workspace-fs-file-list.js +56 -0
  193. package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
  194. package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
  195. package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
  196. package/dist/src/gateway/workspace-ripgrep.js +88 -4
  197. package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
  198. package/dist/src/infra/update-channels.d.ts +14 -0
  199. package/dist/src/infra/update-channels.js +30 -0
  200. package/dist/src/infra/update-channels.js.map +1 -0
  201. package/dist/src/infra/update-check.d.ts +53 -0
  202. package/dist/src/infra/update-check.js +155 -0
  203. package/dist/src/infra/update-check.js.map +1 -0
  204. package/dist/src/infra/update-runner.d.ts +18 -0
  205. package/dist/src/infra/update-runner.js +112 -0
  206. package/dist/src/infra/update-runner.js.map +1 -0
  207. package/dist/src/infra/update-startup.d.ts +20 -0
  208. package/dist/src/infra/update-startup.js +246 -0
  209. package/dist/src/infra/update-startup.js.map +1 -0
  210. package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
  211. package/dist/src/providers/extension-stream-bridge.js +239 -0
  212. package/dist/src/providers/extension-stream-bridge.js.map +1 -0
  213. package/dist/src/providers/index.d.ts +7 -2
  214. package/dist/src/providers/index.js +77 -14
  215. package/dist/src/providers/index.js.map +1 -1
  216. package/dist/src/providers/model-registry.js +1 -1
  217. package/dist/src/providers/plugin-registry.js +92 -87
  218. package/dist/src/providers/plugin-registry.js.map +1 -1
  219. package/dist/src/session/chat-export.d.ts +5 -0
  220. package/dist/src/session/chat-export.js +35 -0
  221. package/dist/src/session/chat-export.js.map +1 -0
  222. package/dist/src/session/config-store.js +1 -1
  223. package/dist/src/session/manager.d.ts +1 -1
  224. package/dist/src/session/manager.js +2 -2
  225. package/dist/src/session/manager.js.map +1 -1
  226. package/dist/src/session/session-title.js +1 -1
  227. package/dist/src/session/store.d.ts +1 -1
  228. package/dist/src/session/store.js +5 -5
  229. package/dist/src/session/store.js.map +1 -1
  230. package/dist/src/utils/logger/audit.js +1 -1
  231. package/dist/src/utils/logger/log-store.js +1 -1
  232. package/dist/src/utils/logger/rotation.js +1 -1
  233. package/dist/src/voice/tts/audio.js +1 -1
  234. package/package.json +2 -1
  235. package/dist/gateway/static/root/assets/agents-BSNzJWbQ.js.map +0 -1
  236. package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +0 -1
  237. package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +0 -1
  238. package/dist/gateway/static/root/assets/index-CQLMxWSA.css +0 -2
  239. package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +0 -2
  240. package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +0 -1
  241. package/dist/gateway/static/root/assets/url-CtSqjF9J.js +0 -2
  242. package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ import{c as e}from"./url-D7yWllI8.js";var t={startExtra:null,main:null,end:null},n=e(e=>({...t,setPageHeader:t=>e(t),clearPageHeader:()=>e(t)}));export{n as t};
2
+ //# sourceMappingURL=page-header-store-BFpnFTed.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"page-header-store-DJHD9Ean.js","names":[],"sources":["../../../../../web/src/stores/page-header-store.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { create } from 'zustand';\n\nexport type PageHeaderPayload = {\n startExtra: ReactNode | null;\n main: ReactNode | null;\n end: ReactNode | null;\n};\n\ntype PageHeaderState = PageHeaderPayload & {\n setPageHeader: (p: PageHeaderPayload) => void;\n clearPageHeader: () => void;\n};\n\nconst emptyHeader: PageHeaderPayload = {\n startExtra: null,\n main: null,\n end: null,\n};\n\nexport const usePageHeaderStore = create<PageHeaderState>((set) => ({\n ...emptyHeader,\n setPageHeader: (p) => set(p),\n clearPageHeader: () => set(emptyHeader),\n}));\n"],"mappings":"sCAcA,IAAM,EAAiC,CACrC,WAAY,KACZ,KAAM,KACN,IAAK,KACN,CAEY,EAAqB,EAAyB,IAAS,CAClE,GAAG,EACH,cAAgB,GAAM,EAAI,EAAE,CAC5B,oBAAuB,EAAI,EAAY,CACxC,EAAE"}
1
+ {"version":3,"file":"page-header-store-BFpnFTed.js","names":[],"sources":["../../../../../web/src/stores/page-header-store.ts"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { create } from 'zustand';\n\nexport type PageHeaderPayload = {\n startExtra: ReactNode | null;\n main: ReactNode | null;\n end: ReactNode | null;\n};\n\ntype PageHeaderState = PageHeaderPayload & {\n setPageHeader: (p: PageHeaderPayload) => void;\n clearPageHeader: () => void;\n};\n\nconst emptyHeader: PageHeaderPayload = {\n startExtra: null,\n main: null,\n end: null,\n};\n\nexport const usePageHeaderStore = create<PageHeaderState>((set) => ({\n ...emptyHeader,\n setPageHeader: (p) => set(p),\n clearPageHeader: () => set(emptyHeader),\n}));\n"],"mappings":"sCAcA,IAAM,EAAiC,CACrC,WAAY,KACZ,KAAM,KACN,IAAK,KACN,CAEY,EAAqB,EAAyB,IAAS,CAClE,GAAG,EACH,cAAgB,GAAM,EAAI,EAAE,CAC5B,oBAAuB,EAAI,EAAY,CACxC,EAAE"}
@@ -1,2 +1,2 @@
1
- import{r as e,t}from"./url-CtSqjF9J.js";function n(e){let t=new URLSearchParams;if(!e)return``;e.status&&t.set(`status`,e.status),e.search&&t.set(`search`,e.search),e.channel&&t.set(`channel`,e.channel),e.limit!=null&&t.set(`limit`,String(e.limit)),e.offset!=null&&t.set(`offset`,String(e.offset));let n=t.toString();return n?`?${n}`:``}function r(e){return e?JSON.stringify({status:e.status,search:e.search,channel:e.channel,limit:e.limit,offset:e.offset}):`default`}var i=new Map;async function a(a){let o=r(a),s=i.get(o);if(s)return s;let c=e(t(`/api/sessions${n(a)}`)).finally(()=>{i.delete(o)});return i.set(o,c),c}async function o(){return e(t(`/api/sessions/stats`))}async function s(n){let r=await e(t(`/api/sessions/${encodeURIComponent(n)}`));if(!r.session)throw Error(`Session not found`);return r.session}async function c(n){await e(t(`/api/sessions/${encodeURIComponent(n)}`),{method:`DELETE`})}async function l(n,r){return e(t(`/api/sessions/${encodeURIComponent(n)}/rename`),{method:`POST`,body:JSON.stringify({name:r})})}async function u(n){await e(t(`/api/sessions/${encodeURIComponent(n)}/archive`),{method:`POST`})}async function d(n){await e(t(`/api/sessions/${encodeURIComponent(n)}/unarchive`),{method:`POST`})}async function f(n){await e(t(`/api/sessions/${encodeURIComponent(n)}/pin`),{method:`POST`})}async function p(n){await e(t(`/api/sessions/${encodeURIComponent(n)}/unpin`),{method:`POST`})}async function m(n){return(await e(t(`/api/sessions/${encodeURIComponent(n)}/export?format=json`))).content}export{o as a,l as c,s as i,d as l,c as n,a as o,m as r,f as s,u as t,p as u};
2
- //# sourceMappingURL=session-api-n-4O5d9U.js.map
1
+ import{r as e,t}from"./url-D7yWllI8.js";function n(e){let t=new URLSearchParams;if(!e)return``;e.status&&t.set(`status`,e.status),e.search&&t.set(`search`,e.search),e.channel&&t.set(`channel`,e.channel),e.limit!=null&&t.set(`limit`,String(e.limit)),e.offset!=null&&t.set(`offset`,String(e.offset));let n=t.toString();return n?`?${n}`:``}function r(e){return e?JSON.stringify({status:e.status,search:e.search,channel:e.channel,limit:e.limit,offset:e.offset}):`default`}var i=new Map;async function a(a){let o=r(a),s=i.get(o);if(s)return s;let c=e(t(`/api/sessions${n(a)}`)).finally(()=>{i.delete(o)});return i.set(o,c),c}async function o(){return e(t(`/api/sessions/stats`))}async function s(n){let r=await e(t(`/api/sessions/${encodeURIComponent(n)}`));if(!r.session)throw Error(`Session not found`);return r.session}async function c(n){await e(t(`/api/sessions/${encodeURIComponent(n)}`),{method:`DELETE`})}async function l(n,r){return e(t(`/api/sessions/${encodeURIComponent(n)}/rename`),{method:`POST`,body:JSON.stringify({name:r})})}async function u(n){await e(t(`/api/sessions/${encodeURIComponent(n)}/archive`),{method:`POST`})}async function d(n){await e(t(`/api/sessions/${encodeURIComponent(n)}/unarchive`),{method:`POST`})}async function f(n){await e(t(`/api/sessions/${encodeURIComponent(n)}/pin`),{method:`POST`})}async function p(n){await e(t(`/api/sessions/${encodeURIComponent(n)}/unpin`),{method:`POST`})}async function m(n){return(await e(t(`/api/sessions/${encodeURIComponent(n)}/export?format=json`))).content}export{o as a,l as c,s as i,d as l,c as n,a as o,m as r,f as s,u as t,p as u};
2
+ //# sourceMappingURL=session-api-DEhQXWJg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-api-n-4O5d9U.js","names":[],"sources":["../../../../../web/src/features/sessions/session-api.ts"],"sourcesContent":["import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nimport type {\n PaginatedResult,\n SessionDetail,\n SessionListQuery,\n SessionMetadata,\n SessionStats,\n} from '@/features/sessions/session.types';\n\nfunction buildListQuery(query?: SessionListQuery): string {\n const params = new URLSearchParams();\n if (!query) return '';\n if (query.status) params.set('status', query.status);\n if (query.search) params.set('search', query.search);\n if (query.channel) params.set('channel', query.channel);\n if (query.limit != null) params.set('limit', String(query.limit));\n if (query.offset != null) params.set('offset', String(query.offset));\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n\nfunction listSessionsDedupeKey(query?: SessionListQuery): string {\n if (!query) return 'default';\n return JSON.stringify({\n status: query.status,\n search: query.search,\n channel: query.channel,\n limit: query.limit,\n offset: query.offset,\n });\n}\n\nconst listSessionsInflight = new Map<string, Promise<PaginatedResult<SessionMetadata>>>();\n\n/**\n * List sessions (paginated). Concurrent calls with the same query share one HTTP request so the\n * sidebar and chat bootstrap do not triple-fetch the first page on load.\n */\nexport async function listSessions(query?: SessionListQuery): Promise<PaginatedResult<SessionMetadata>> {\n const key = listSessionsDedupeKey(query);\n const existing = listSessionsInflight.get(key);\n if (existing) return existing;\n\n const url = apiUrl(`/api/sessions${buildListQuery(query)}`);\n const pending = fetchJson<PaginatedResult<SessionMetadata>>(url).finally(() => {\n listSessionsInflight.delete(key);\n });\n listSessionsInflight.set(key, pending);\n return pending;\n}\n\nexport async function getSessionStats(): Promise<SessionStats> {\n return fetchJson<SessionStats>(apiUrl('/api/sessions/stats'));\n}\n\nexport async function getSessionDetail(key: string): Promise<SessionDetail> {\n const data = await fetchJson<{ session: SessionDetail }>(apiUrl(`/api/sessions/${encodeURIComponent(key)}`));\n if (!data.session) throw new Error('Session not found');\n return data.session;\n}\n\nexport async function deleteSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}`), { method: 'DELETE' });\n}\n\nexport async function renameSession(key: string, name: string): Promise<{ renamed: boolean }> {\n return fetchJson<{ renamed: boolean }>(apiUrl(`/api/sessions/${encodeURIComponent(key)}/rename`), {\n method: 'POST',\n body: JSON.stringify({ name }),\n });\n}\n\nexport async function archiveSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}/archive`), { method: 'POST' });\n}\n\nexport async function unarchiveSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}/unarchive`), { method: 'POST' });\n}\n\nexport async function pinSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}/pin`), { method: 'POST' });\n}\n\nexport async function unpinSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}/unpin`), { method: 'POST' });\n}\n\nexport async function exportSessionJson(key: string): Promise<string> {\n const data = await fetchJson<{ content: string }>(\n apiUrl(`/api/sessions/${encodeURIComponent(key)}/export?format=json`),\n );\n return data.content;\n}\n"],"mappings":"wCAWA,SAAS,EAAe,EAAkC,CACxD,IAAM,EAAS,IAAI,gBACnB,GAAI,CAAC,EAAO,MAAO,GACf,EAAM,QAAQ,EAAO,IAAI,SAAU,EAAM,OAAO,CAChD,EAAM,QAAQ,EAAO,IAAI,SAAU,EAAM,OAAO,CAChD,EAAM,SAAS,EAAO,IAAI,UAAW,EAAM,QAAQ,CACnD,EAAM,OAAS,MAAM,EAAO,IAAI,QAAS,OAAO,EAAM,MAAM,CAAC,CAC7D,EAAM,QAAU,MAAM,EAAO,IAAI,SAAU,OAAO,EAAM,OAAO,CAAC,CACpE,IAAM,EAAK,EAAO,UAAU,CAC5B,OAAO,EAAK,IAAI,IAAO,GAGzB,SAAS,EAAsB,EAAkC,CAE/D,OADK,EACE,KAAK,UAAU,CACpB,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,QAAS,EAAM,QACf,MAAO,EAAM,MACb,OAAQ,EAAM,OACf,CAAC,CAPiB,UAUrB,IAAM,EAAuB,IAAI,IAMjC,eAAsB,EAAa,EAAqE,CACtG,IAAM,EAAM,EAAsB,EAAM,CAClC,EAAW,EAAqB,IAAI,EAAI,CAC9C,GAAI,EAAU,OAAO,EAGrB,IAAM,EAAU,EADJ,EAAO,gBAAgB,EAAe,EAAM,GAAG,CACK,CAAC,YAAc,CAC7E,EAAqB,OAAO,EAAI,EAChC,CAEF,OADA,EAAqB,IAAI,EAAK,EAAQ,CAC/B,EAGT,eAAsB,GAAyC,CAC7D,OAAO,EAAwB,EAAO,sBAAsB,CAAC,CAG/D,eAAsB,EAAiB,EAAqC,CAC1E,IAAM,EAAO,MAAM,EAAsC,EAAO,iBAAiB,mBAAmB,EAAI,GAAG,CAAC,CAC5G,GAAI,CAAC,EAAK,QAAS,MAAU,MAAM,oBAAoB,CACvD,OAAO,EAAK,QAGd,eAAsB,EAAc,EAA4B,CAC9D,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,GAAG,CAAE,CAAE,OAAQ,SAAU,CAAC,CAG3F,eAAsB,EAAc,EAAa,EAA6C,CAC5F,OAAO,EAAgC,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,SAAS,CAAE,CAChG,OAAQ,OACR,KAAM,KAAK,UAAU,CAAE,OAAM,CAAC,CAC/B,CAAC,CAGJ,eAAsB,EAAe,EAA4B,CAC/D,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,UAAU,CAAE,CAAE,OAAQ,OAAQ,CAAC,CAGjG,eAAsB,EAAiB,EAA4B,CACjE,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,YAAY,CAAE,CAAE,OAAQ,OAAQ,CAAC,CAGnG,eAAsB,EAAW,EAA4B,CAC3D,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,MAAM,CAAE,CAAE,OAAQ,OAAQ,CAAC,CAG7F,eAAsB,EAAa,EAA4B,CAC7D,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,QAAQ,CAAE,CAAE,OAAQ,OAAQ,CAAC,CAG/F,eAAsB,EAAkB,EAA8B,CAIpE,OAHa,MAAM,EACjB,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,qBAAqB,CACtE,EACW"}
1
+ {"version":3,"file":"session-api-DEhQXWJg.js","names":[],"sources":["../../../../../web/src/features/sessions/session-api.ts"],"sourcesContent":["import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nimport type {\n PaginatedResult,\n SessionDetail,\n SessionListQuery,\n SessionMetadata,\n SessionStats,\n} from '@/features/sessions/session.types';\n\nfunction buildListQuery(query?: SessionListQuery): string {\n const params = new URLSearchParams();\n if (!query) return '';\n if (query.status) params.set('status', query.status);\n if (query.search) params.set('search', query.search);\n if (query.channel) params.set('channel', query.channel);\n if (query.limit != null) params.set('limit', String(query.limit));\n if (query.offset != null) params.set('offset', String(query.offset));\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n}\n\nfunction listSessionsDedupeKey(query?: SessionListQuery): string {\n if (!query) return 'default';\n return JSON.stringify({\n status: query.status,\n search: query.search,\n channel: query.channel,\n limit: query.limit,\n offset: query.offset,\n });\n}\n\nconst listSessionsInflight = new Map<string, Promise<PaginatedResult<SessionMetadata>>>();\n\n/**\n * List sessions (paginated). Concurrent calls with the same query share one HTTP request so the\n * sidebar and chat bootstrap do not triple-fetch the first page on load.\n */\nexport async function listSessions(query?: SessionListQuery): Promise<PaginatedResult<SessionMetadata>> {\n const key = listSessionsDedupeKey(query);\n const existing = listSessionsInflight.get(key);\n if (existing) return existing;\n\n const url = apiUrl(`/api/sessions${buildListQuery(query)}`);\n const pending = fetchJson<PaginatedResult<SessionMetadata>>(url).finally(() => {\n listSessionsInflight.delete(key);\n });\n listSessionsInflight.set(key, pending);\n return pending;\n}\n\nexport async function getSessionStats(): Promise<SessionStats> {\n return fetchJson<SessionStats>(apiUrl('/api/sessions/stats'));\n}\n\nexport async function getSessionDetail(key: string): Promise<SessionDetail> {\n const data = await fetchJson<{ session: SessionDetail }>(apiUrl(`/api/sessions/${encodeURIComponent(key)}`));\n if (!data.session) throw new Error('Session not found');\n return data.session;\n}\n\nexport async function deleteSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}`), { method: 'DELETE' });\n}\n\nexport async function renameSession(key: string, name: string): Promise<{ renamed: boolean }> {\n return fetchJson<{ renamed: boolean }>(apiUrl(`/api/sessions/${encodeURIComponent(key)}/rename`), {\n method: 'POST',\n body: JSON.stringify({ name }),\n });\n}\n\nexport async function archiveSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}/archive`), { method: 'POST' });\n}\n\nexport async function unarchiveSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}/unarchive`), { method: 'POST' });\n}\n\nexport async function pinSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}/pin`), { method: 'POST' });\n}\n\nexport async function unpinSession(key: string): Promise<void> {\n await fetchJson(apiUrl(`/api/sessions/${encodeURIComponent(key)}/unpin`), { method: 'POST' });\n}\n\nexport async function exportSessionJson(key: string): Promise<string> {\n const data = await fetchJson<{ content: string }>(\n apiUrl(`/api/sessions/${encodeURIComponent(key)}/export?format=json`),\n );\n return data.content;\n}\n"],"mappings":"wCAWA,SAAS,EAAe,EAAkC,CACxD,IAAM,EAAS,IAAI,gBACnB,GAAI,CAAC,EAAO,MAAO,GACf,EAAM,QAAQ,EAAO,IAAI,SAAU,EAAM,OAAO,CAChD,EAAM,QAAQ,EAAO,IAAI,SAAU,EAAM,OAAO,CAChD,EAAM,SAAS,EAAO,IAAI,UAAW,EAAM,QAAQ,CACnD,EAAM,OAAS,MAAM,EAAO,IAAI,QAAS,OAAO,EAAM,MAAM,CAAC,CAC7D,EAAM,QAAU,MAAM,EAAO,IAAI,SAAU,OAAO,EAAM,OAAO,CAAC,CACpE,IAAM,EAAK,EAAO,UAAU,CAC5B,OAAO,EAAK,IAAI,IAAO,GAGzB,SAAS,EAAsB,EAAkC,CAE/D,OADK,EACE,KAAK,UAAU,CACpB,OAAQ,EAAM,OACd,OAAQ,EAAM,OACd,QAAS,EAAM,QACf,MAAO,EAAM,MACb,OAAQ,EAAM,OACf,CAAC,CAPiB,UAUrB,IAAM,EAAuB,IAAI,IAMjC,eAAsB,EAAa,EAAqE,CACtG,IAAM,EAAM,EAAsB,EAAM,CAClC,EAAW,EAAqB,IAAI,EAAI,CAC9C,GAAI,EAAU,OAAO,EAGrB,IAAM,EAAU,EADJ,EAAO,gBAAgB,EAAe,EAAM,GAAG,CACK,CAAC,YAAc,CAC7E,EAAqB,OAAO,EAAI,EAChC,CAEF,OADA,EAAqB,IAAI,EAAK,EAAQ,CAC/B,EAGT,eAAsB,GAAyC,CAC7D,OAAO,EAAwB,EAAO,sBAAsB,CAAC,CAG/D,eAAsB,EAAiB,EAAqC,CAC1E,IAAM,EAAO,MAAM,EAAsC,EAAO,iBAAiB,mBAAmB,EAAI,GAAG,CAAC,CAC5G,GAAI,CAAC,EAAK,QAAS,MAAU,MAAM,oBAAoB,CACvD,OAAO,EAAK,QAGd,eAAsB,EAAc,EAA4B,CAC9D,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,GAAG,CAAE,CAAE,OAAQ,SAAU,CAAC,CAG3F,eAAsB,EAAc,EAAa,EAA6C,CAC5F,OAAO,EAAgC,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,SAAS,CAAE,CAChG,OAAQ,OACR,KAAM,KAAK,UAAU,CAAE,OAAM,CAAC,CAC/B,CAAC,CAGJ,eAAsB,EAAe,EAA4B,CAC/D,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,UAAU,CAAE,CAAE,OAAQ,OAAQ,CAAC,CAGjG,eAAsB,EAAiB,EAA4B,CACjE,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,YAAY,CAAE,CAAE,OAAQ,OAAQ,CAAC,CAGnG,eAAsB,EAAW,EAA4B,CAC3D,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,MAAM,CAAE,CAAE,OAAQ,OAAQ,CAAC,CAG7F,eAAsB,EAAa,EAA4B,CAC7D,MAAM,EAAU,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,QAAQ,CAAE,CAAE,OAAQ,OAAQ,CAAC,CAG/F,eAAsB,EAAkB,EAA8B,CAIpE,OAHa,MAAM,EACjB,EAAO,iBAAiB,mBAAmB,EAAI,CAAC,qBAAqB,CACtE,EACW"}
@@ -1,6 +1,6 @@
1
- import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{l as r,n as i,r as a,s as o,t as s}from"./url-CtSqjF9J.js";import{r as c,t as l}from"./cn-DPF56z7S.js";import{t as u}from"./loader-circle-B2bCE4iI.js";import{a as d,i as f,n as p,o as m,r as h,s as g}from"./dist-Db1dcLr9.js";import{t as _}from"./button-CRQ9eQGX.js";import{s as v}from"./form-field-width-BlpNwrfn.js";import{c as y}from"./attachment-utils-core-B42k7cMx.js";import{o as b}from"./session-api-n-4O5d9U.js";import{n as x}from"./interaction-C4gx-xSs.js";var S=c(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),C=c(`folder-input`,[[`path`,{d:`M2 9V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-1`,key:`fm4g5t`}],[`path`,{d:`M2 13h10`,key:`pgb2dq`}],[`path`,{d:`m9 16 3-3-3-3`,key:`6m91ic`}]]),w=c(`layout-template`,[[`rect`,{width:`18`,height:`7`,x:`3`,y:`3`,rx:`1`,key:`f1a2em`}],[`rect`,{width:`9`,height:`7`,x:`3`,y:`14`,rx:`1`,key:`jqznyg`}],[`rect`,{width:`5`,height:`7`,x:`16`,y:`14`,rx:`1`,key:`q5h2i8`}]]),T=e(t(),1);function E(e){return typeof e==`object`&&!!e&&`role`in e}function D(e){if(!e||typeof e!=`object`)return!1;let t=e.type;return t===`tool_use`||t===`tool_call`||t===`toolCall`}function O(e){return typeof e.id==`string`&&e.id.length>0?e.id:crypto.randomUUID()}function k(e){if(typeof e!=`string`)return e;try{return JSON.parse(e)}catch{return e}}function A(e){return k(e.args??e.arguments??e.input??e.function?.arguments)??{}}function j(e){return typeof e==`object`&&!!e}function M(e){if(e.type!==`thinking`)return``;let t=e;return typeof t.text==`string`?t.text:typeof t.thinking==`string`?t.thinking:``}function N(e,t){let n=e.map(e=>({...e})),r=new Map;for(let e=0;e<n.length;e++){let t=n[e];t.type===`tool_use`&&r.set(t.id,e)}for(let e of t){if(e.type===`tool_use`&&r.has(e.id)){let t=r.get(e.id);n[t]={...e};continue}if(e.type===`thinking`&&n.length>0){let t=n[n.length-1];if(t.type===`thinking`&&M(t)===M(e))continue}e.type===`tool_use`&&r.set(e.id,n.length),n.push({...e})}return n}function P(e){if(e.length<2)return e;let t=[];for(let n of e){if(n.role!==`assistant`){t.push(n);continue}let e=t[t.length-1];e?.role===`assistant`?(e.content=N(e.content,n.content),n.timestamp!=null&&(e.timestamp=n.timestamp),n.usage&&(e.usage=n.usage),n.attachments?.length&&(e.attachments=V([...e.attachments??[],...n.attachments]))):t.push({...n,content:[...n.content]})}return t}function F(e){let t=[];for(let n of e){if(!E(n))continue;let e=n,r=String(e.role??``);if(r!==`system`){if(r===`toolResult`||r===`tool`){ae(t,e);continue}if(r===`user`||r===`user-with-attachments`){t.push(U(e));continue}if(r===`assistant`){t.push(re(e));continue}}}return P(t)}function I(e){if(Array.isArray(e))return e.map(e=>ee(e))}function ee(e){if(!e||typeof e!=`object`)return{name:`file`,mimeType:`application/octet-stream`};let t=e,n=typeof t.data==`string`?t.data:void 0,r=typeof t.content==`string`&&t.content.length>0?t.content:n,i=typeof t.name==`string`&&t.name.length>0?t.name:`file`,a=typeof t.mimeType==`string`&&t.mimeType.length>0?t.mimeType:``;!a&&typeof t.type==`string`&&t.type.includes(`/`)&&(a=t.type),a||=`application/octet-stream`;let o=a.split(`;`)[0]?.trim().toLowerCase()??``;if(o===`application/octet-stream`||o===``){let e=y(i);e&&(a=e)}let s=typeof t.preview==`string`&&t.preview.length>0?t.preview:a.startsWith(`image/`)&&r?r:void 0;return{id:typeof t.id==`string`?t.id:void 0,name:i,mimeType:a,type:typeof t.type==`string`?t.type:void 0,size:typeof t.size==`number`?t.size:void 0,content:r,data:n??r,preview:s,extractedText:typeof t.extractedText==`string`?t.extractedText:void 0,workspaceRelativePath:typeof t.workspaceRelativePath==`string`&&t.workspaceRelativePath.length>0?t.workspaceRelativePath:void 0}}function te(e){if(typeof e!=`string`||!e.includes(`## Skill:`))return e;let t=e.match(/## Skill:\s*([^\s\r\n]+)/);if(!t)return e;let n=t[1]??``;if(!n)return e;let r=[...e.matchAll(/\*\*Arguments\*\*:\s*([^\r\n]+)/g)],i=r.length>0?(r[r.length-1]?.[1]??``).trim():``;return i?`/skill:${n} ${i}`:`/skill:${n}`}function ne(e){if(!e.includes(`xopc-path:`))return e;let t=e;return t=t.replace(/\s*\[File:[^\]]+\]\s*\r?\nxopc-path:rel:[^\r\n]+\r?\n\s*xopc-path:abs:[^\r\n]+/g,``),t=t.replace(/\s*\[File:[^\]]+\]\s+xopc-path:rel:\S+\s+xopc-path:abs:\S+/g,``),t=t.replace(/\s*\[File:[^\]]+\]\s*xopc-path:rel:\S+\s*xopc-path:abs:\S+/g,``),t.replace(/\n{3,}/g,`
1
+ import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{l as r,n as i,r as a,s as o,t as s}from"./url-D7yWllI8.js";import{r as c,t as l}from"./cn-DPF56z7S.js";import{t as u}from"./loader-circle-B2bCE4iI.js";import{a as d,i as f,n as p,o as m,r as h,s as g}from"./dist-Db1dcLr9.js";import{t as _}from"./button-CRQ9eQGX.js";import{s as v}from"./form-field-width-BlpNwrfn.js";import{c as y}from"./attachment-utils-core-B42k7cMx.js";import{o as b}from"./session-api-DEhQXWJg.js";import{n as x}from"./interaction-C4gx-xSs.js";var S=c(`chevron-up`,[[`path`,{d:`m18 15-6-6-6 6`,key:`153udz`}]]),C=c(`folder-input`,[[`path`,{d:`M2 9V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-1`,key:`fm4g5t`}],[`path`,{d:`M2 13h10`,key:`pgb2dq`}],[`path`,{d:`m9 16 3-3-3-3`,key:`6m91ic`}]]),w=c(`layout-template`,[[`rect`,{width:`18`,height:`7`,x:`3`,y:`3`,rx:`1`,key:`f1a2em`}],[`rect`,{width:`9`,height:`7`,x:`3`,y:`14`,rx:`1`,key:`jqznyg`}],[`rect`,{width:`5`,height:`7`,x:`16`,y:`14`,rx:`1`,key:`q5h2i8`}]]),T=e(t(),1);function E(e){return typeof e==`object`&&!!e&&`role`in e}function D(e){if(!e||typeof e!=`object`)return!1;let t=e.type;return t===`tool_use`||t===`tool_call`||t===`toolCall`}function O(e){return typeof e.id==`string`&&e.id.length>0?e.id:crypto.randomUUID()}function k(e){if(typeof e!=`string`)return e;try{return JSON.parse(e)}catch{return e}}function A(e){return k(e.args??e.arguments??e.input??e.function?.arguments)??{}}function j(e){return typeof e==`object`&&!!e}function M(e){if(e.type!==`thinking`)return``;let t=e;return typeof t.text==`string`?t.text:typeof t.thinking==`string`?t.thinking:``}function N(e,t){let n=e.map(e=>({...e})),r=new Map;for(let e=0;e<n.length;e++){let t=n[e];t.type===`tool_use`&&r.set(t.id,e)}for(let e of t){if(e.type===`tool_use`&&r.has(e.id)){let t=r.get(e.id);n[t]={...e};continue}if(e.type===`thinking`&&n.length>0){let t=n[n.length-1];if(t.type===`thinking`&&M(t)===M(e))continue}e.type===`tool_use`&&r.set(e.id,n.length),n.push({...e})}return n}function P(e){if(e.length<2)return e;let t=[];for(let n of e){if(n.role!==`assistant`){t.push(n);continue}let e=t[t.length-1];e?.role===`assistant`?(e.content=N(e.content,n.content),n.timestamp!=null&&(e.timestamp=n.timestamp),n.usage&&(e.usage=n.usage),n.attachments?.length&&(e.attachments=V([...e.attachments??[],...n.attachments]))):t.push({...n,content:[...n.content]})}return t}function F(e){let t=[];for(let n of e){if(!E(n))continue;let e=n,r=String(e.role??``);if(r!==`system`){if(r===`toolResult`||r===`tool`){ae(t,e);continue}if(r===`user`||r===`user-with-attachments`){t.push(U(e));continue}if(r===`assistant`){t.push(re(e));continue}}}return P(t)}function I(e){if(Array.isArray(e))return e.map(e=>ee(e))}function ee(e){if(!e||typeof e!=`object`)return{name:`file`,mimeType:`application/octet-stream`};let t=e,n=typeof t.data==`string`?t.data:void 0,r=typeof t.content==`string`&&t.content.length>0?t.content:n,i=typeof t.name==`string`&&t.name.length>0?t.name:`file`,a=typeof t.mimeType==`string`&&t.mimeType.length>0?t.mimeType:``;!a&&typeof t.type==`string`&&t.type.includes(`/`)&&(a=t.type),a||=`application/octet-stream`;let o=a.split(`;`)[0]?.trim().toLowerCase()??``;if(o===`application/octet-stream`||o===``){let e=y(i);e&&(a=e)}let s=typeof t.preview==`string`&&t.preview.length>0?t.preview:a.startsWith(`image/`)&&r?r:void 0;return{id:typeof t.id==`string`?t.id:void 0,name:i,mimeType:a,type:typeof t.type==`string`?t.type:void 0,size:typeof t.size==`number`?t.size:void 0,content:r,data:n??r,preview:s,extractedText:typeof t.extractedText==`string`?t.extractedText:void 0,workspaceRelativePath:typeof t.workspaceRelativePath==`string`&&t.workspaceRelativePath.length>0?t.workspaceRelativePath:void 0}}function te(e){if(typeof e!=`string`||!e.includes(`## Skill:`))return e;let t=e.match(/## Skill:\s*([^\s\r\n]+)/);if(!t)return e;let n=t[1]??``;if(!n)return e;let r=[...e.matchAll(/\*\*Arguments\*\*:\s*([^\r\n]+)/g)],i=r.length>0?(r[r.length-1]?.[1]??``).trim():``;return i?`/skill:${n} ${i}`:`/skill:${n}`}function ne(e){if(!e.includes(`xopc-path:`))return e;let t=e;return t=t.replace(/\s*\[File:[^\]]+\]\s*\r?\nxopc-path:rel:[^\r\n]+\r?\n\s*xopc-path:abs:[^\r\n]+/g,``),t=t.replace(/\s*\[File:[^\]]+\]\s+xopc-path:rel:\S+\s+xopc-path:abs:\S+/g,``),t=t.replace(/\s*\[File:[^\]]+\]\s*xopc-path:rel:\S+\s*xopc-path:abs:\S+/g,``),t.replace(/\n{3,}/g,`
2
2
 
3
3
  `).trim()}function L(e){let t=e.match(/^([^(]+?)\s*\(/),n=t?t[1].trim():`file`,r=e.match(/\(\s*([^,]+)\s*,\s*(\d+)\s*bytes\s*\)/i);return{name:n,mimeType:r?r[1].trim():`application/octet-stream`,size:r?parseInt(r[2],10):0}}function R(e){let t=[];if(typeof e==`string`)t.push(e);else if(Array.isArray(e)){for(let n of e)if(n&&typeof n==`object`&&n.type===`text`){let e=n.text;typeof e==`string`&&t.push(e)}}let n=t.join(`
4
4
  `);if(!n.includes(`xopc-path:rel:`))return;let r=[],i=new Set,a=/\[File: ([^\]]+)\]\s*xopc-path:rel:(\S+)\s*xopc-path:abs:\S+/g,o;for(;(o=a.exec(n))!==null;){let e=o[2].trim();if(i.has(e))continue;i.add(e);let{name:t,mimeType:n,size:a}=L(o[1]);r.push({name:t,mimeType:n,size:a,type:`document`,workspaceRelativePath:e})}let s=/\[File: ([^\]]+)\]\s*\r?\nxopc-path:rel:([^\r\n]+)\r?\n\s*xopc-path:abs:[^\r\n]+/g;for(;(o=s.exec(n))!==null;){let e=o[2].trim();if(i.has(e))continue;i.add(e);let{name:t,mimeType:n,size:a}=L(o[1]);r.push({name:t,mimeType:n,size:a,type:`document`,workspaceRelativePath:e})}return r.length?r:void 0}function z(e,t){return e!==`user`&&e!==`user-with-attachments`?t:t.map(e=>{if(e.type===`text`&&typeof e.text==`string`){let t=ne(e.text);return{...e,text:te(t)}}return e}).filter(e=>!(e.type===`text`&&(!e.text||!e.text.trim())))}function B(e){let t=e.workspaceRelativePath?.replace(/\\/g,`/`).trim();return t?`rel:${t}`:e.id?`id:${e.id}`:`name:${e.name??`file`}|${e.mimeType??``}`}function V(e){if(!e?.length)return;let t=[],n=new Set;for(let r of e){let e=B(r);n.has(e)||(n.add(e),t.push(r))}return t.length?t:void 0}function H(e,t){return V([...e??[],...t??[]])}function U(e){let t=String(e.role??`user`),n=t===`user`||t===`user-with-attachments`?t:`assistant`,r=R(e.content);return{role:n,content:z(n,G(e.content)),attachments:H(I(e.attachments),r),timestamp:typeof e.timestamp==`number`?e.timestamp:W(e.timestamp),usage:e.usage}}function re(e){return{role:`assistant`,content:ie(e),attachments:V(I(e.attachments)),timestamp:typeof e.timestamp==`number`?e.timestamp:W(e.timestamp),usage:e.usage}}function W(e){if(typeof e==`string`){let t=Date.parse(e);return Number.isNaN(t)?Date.now():t}return Date.now()}function ie(e){let t=G(e.content),n=e.tool_calls;if(Array.isArray(n))for(let e of n){if(!e?.id||t.some(t=>t.type===`tool_use`&&t.id===e.id))continue;let n=e.function?.arguments;if(typeof n==`string`)try{n=JSON.parse(n)}catch{}t.push({type:`tool_use`,id:e.id,name:e.function?.name||`tool`,input:n,status:`running`})}let r=e.toolCalls;if(Array.isArray(r))for(let e of r){let n=e.id??crypto.randomUUID();t.some(e=>e.type===`tool_use`&&e.id===n)||t.push({type:`tool_use`,id:n,name:e.name||`tool`,input:e.args,status:`running`})}return t}function ae(e,t){let n=oe(e);if(!n)return;let r=String(t.tool_call_id??t.toolCallId??``),i=se(t.content),a=!!t.isError,o=r?n.content.find(e=>e.type===`tool_use`&&e.id===r):void 0;if(o){o.status=a?`error`:`done`,o.result=i;return}let s=n.content.filter(e=>e.type===`tool_use`&&e.status===`running`);s.length===1&&(s[0].status=a?`error`:`done`,s[0].result=i)}function oe(e){for(let t=e.length-1;t>=0;t--)if(e[t].role===`assistant`)return e[t];return null}function se(e){return typeof e==`string`?e:Array.isArray(e)?e.filter(e=>j(e)&&e.type===`text`).map(e=>String(e.text??``)).join(`
5
5
  `):String(e??``)}function ce(e){let t=e.source?.data;if(typeof t==`string`&&t.length>0)return{type:`image`,source:{data:t}};let n=e.data;if(typeof n!=`string`||n.length===0)return null;let r=n.trim();return r.startsWith(`data:`)?{type:`image`,source:{data:r}}:{type:`image`,source:{data:`data:${typeof e.mimeType==`string`&&e.mimeType.includes(`/`)?e.mimeType:`image/png`};base64,${r.replace(/\s/g,``)}`}}}function G(e){if(e==null)return[];if(typeof e==`string`)return e.trim()?[{type:`text`,text:e}]:[];if(!Array.isArray(e))return[{type:`text`,text:String(e)}];let t=[];for(let n of e){if(!j(n))continue;let e=n.type;if(e===`text`&&typeof n.text==`string`)t.push({type:`text`,text:n.text});else if(e===`thinking`){let e=typeof n.thinking==`string`?n.thinking:typeof n.text==`string`?n.text:``;t.push({type:`thinking`,text:e,streaming:!1})}else if(e===`image`){let e=ce(n);e&&t.push(e)}else if(e===`tool_use`||e===`tool_call`){if(!D(n))continue;let e=O(n),r=String(n.name??n.function?.name??`tool`),i=n.input??n.function?.arguments;t.push({type:`tool_use`,id:e,name:r,input:i,status:`done`,result:typeof n.result==`string`?n.result:void 0})}else if(e===`toolCall`){if(!D(n))continue;let e=O(n),r=String(n.name??n.function?.name??`tool`);t.push({type:`tool_use`,id:e,name:r,input:A(n),status:`done`,result:typeof n.result==`string`?n.result:void 0})}}return t}function K(e){return e.startsWith(`gateway:`)||e.includes(`:gateway:`)||e.includes(`:webchat:`)}var q=new Map,le=class{async loadSessions(){return(await b({limit:20,offset:0})).items.filter(e=>K(e.key)).map(e=>({key:e.key,name:e.name,updatedAt:e.updatedAt,messageCount:e.messageCount})).sort((e,t)=>new Date(t.updatedAt).getTime()-new Date(e.updatedAt).getTime())}async loadSessionAgentConfig(e){let t=q.get(e);if(t)return t;let n=(async()=>{let t=await i(s(`/api/sessions/${encodeURIComponent(e)}/agent-config`));if(!t.ok)throw Error(`HTTP ${t.status}`);let n=await t.json();return{thinkingLevel:n.payload?.thinkingLevel??`medium`,model:typeof n.payload?.model==`string`?n.payload.model:``,reasoningLevel:n.payload?.reasoningLevel??`off`,effectiveWorkspacePath:typeof n.payload?.effectiveWorkspacePath==`string`?n.payload.effectiveWorkspacePath:``,workingDirectoryLocked:!!n.payload?.workingDirectoryLocked}})().finally(()=>{q.delete(e)});return q.set(e,n),n}async patchSessionAgentConfig(e,t){let n=await i(s(`/api/sessions/${encodeURIComponent(e)}/agent-config`),{method:`PATCH`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)});if(!n.ok){let e=await n.json().catch(()=>({}));throw Error(e.error??`HTTP ${n.status}`)}}async loadSession(e,t=0){let n=await i(s(`/api/sessions/${encodeURIComponent(e)}?offset=${t}&limit=50`));if(!n.ok)throw Error(`HTTP ${n.status}: ${n.statusText}`);let r=await n.json(),a=r.session?.messages||[],o=F(a),c=typeof r.session?.name==`string`&&r.session.name.trim()?r.session.name.trim():void 0;return{messages:o,hasMore:a.length>=50,name:c}}async createSession(e){let t={channel:`webchat`},n=e?.agentId?.trim();n&&(t.agentId=n.toLowerCase());let r=await i(s(`/api/sessions`),{method:`POST`,body:JSON.stringify(t)});if(!r.ok)throw Error(`HTTP ${r.status}`);return(await r.json()).session}async fetchSessionName(e){let t=await i(s(`/api/sessions/${encodeURIComponent(e)}?offset=0&limit=1`));if(!t.ok)return;let n=(await t.json()).session?.name;return typeof n==`string`&&n.trim()?n.trim():void 0}updateUrl(e){let t=`#/chat/${encodeURIComponent(e)}`;location.hash!==t&&history.replaceState(null,``,t)}parseSessionFromHash(){let e=location.hash.slice(1),t=e.match(/^\/chat\/(.+)$/)||e.match(/^chat\/(.+)$/),n=t?decodeURIComponent(t[1]):null;return n&&n!==`new`?n:null}};async function J(e){let t=new URLSearchParams;e!=null&&e!==``&&t.set(`path`,e);let n=t.toString();return(await a(s(`/api/host/fs/list${n?`?${n}`:``}`))).payload}async function ue(){return(await a(s(`/api/host/fs/meta`))).payload}var Y=n();function de(){return l(`w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 font-mono text-sm text-fg`,`placeholder:text-fg-subtle`,v,`dark:border-edge`)}function X(e){return/^[A-Za-z]:\\?$/.test(e.replace(/\\$/,`\\`))}function fe(e){return!e||e.currentPath===``?!1:e.parentPath===null?e.currentPath===`/`?!1:!!X(e.currentPath):!0}function Z({open:e,onOpenChange:t,initialAbsolutePath:n,onConfirm:r,wd:i}){let a=(0,T.useId)(),[o,s]=(0,T.useState)(null),[c,v]=(0,T.useState)(null),[y,b]=(0,T.useState)(!1),[w,E]=(0,T.useState)(null),[D,O]=(0,T.useState)(``),k=(0,T.useCallback)(async e=>{b(!0),E(null);try{v(await J(e))}catch(e){E(e instanceof Error?e.message:String(e)),v(null)}finally{b(!1)}},[]);(0,T.useEffect)(()=>{if(!e)return;let t=!1;return(async()=>{try{let e=await ue();t||s(e.hostname)}catch{t||s(null)}})(),()=>{t=!0}},[e]),(0,T.useEffect)(()=>{if(!e)return;let t=n?.trim()??``;O(t);let r=!1;return(async()=>{b(!0),E(null);try{if(t)try{let e=await J(t);r||v(e)}catch{let e=await J();r||v(e)}else{let e=await J();r||v(e)}}catch(e){let t=e instanceof Error?e.message:String(e);r||(E(t),v(null))}finally{r||b(!1)}})(),()=>{r=!0}},[e,n]);let A=e=>{e.isDirectory&&k(e.absolutePath)},j=()=>{if(!c)return;let{parentPath:e,currentPath:t}=c;if(e!==null){k(e);return}t===`/`||t===``||X(t)&&k(void 0)},M=c?.currentPath===``?i.pickerDrives:c?.currentPath??``,N=!!c&&c.currentPath!==``&&!y&&!w,P=async()=>{if(!(!c||c.currentPath===``))try{await r(c.currentPath),t(!1)}catch{}},F=async()=>{let e=D.trim();if(e)try{await r(e),t(!1)}catch{}},I=y&&c;return(0,Y.jsx)(m,{open:e,onOpenChange:t,children:(0,Y.jsxs)(d,{children:[(0,Y.jsx)(f,{className:`fixed inset-0 z-[80] bg-scrim backdrop-blur-[2px]`}),(0,Y.jsxs)(p,{className:l(`fixed left-1/2 top-1/2 z-[81] flex max-h-[min(90vh,32rem)] w-[min(100%-2rem,28rem)] -translate-x-1/2 -translate-y-1/2 flex-col rounded-xl border border-edge bg-surface-panel p-4 shadow-popover`,`dark:border-edge`),onOpenAutoFocus:e=>e.preventDefault(),children:[(0,Y.jsx)(g,{className:`text-base font-semibold text-fg`,children:i.pathModalTitle}),(0,Y.jsx)(h,{className:`mt-1 text-sm leading-relaxed text-fg-muted`,children:i.pathModalDescription}),o?(0,Y.jsx)(`p`,{className:`mt-2 text-xs text-fg-muted`,children:i.pickerHostHint.replace(`{{hostname}}`,o)}):null,(0,Y.jsxs)(`div`,{className:`mt-3 flex min-h-0 flex-1 flex-col gap-2`,children:[(0,Y.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Y.jsxs)(_,{type:`button`,variant:`secondary`,className:`shrink-0 gap-1 px-2 py-1.5 text-xs`,disabled:y||!fe(c)||!!w,onClick:()=>j(),title:i.pickerUp,children:[(0,Y.jsx)(S,{className:`size-4`,"aria-hidden":!0}),i.pickerUp]}),(0,Y.jsx)(`div`,{className:`min-w-0 flex-1 truncate rounded-md border border-edge-subtle/80 bg-surface-hover/30 px-2 py-1.5 font-mono text-xs text-fg`,title:M,children:y&&!c?i.pickerLoading:M||`—`})]}),(0,Y.jsxs)(`div`,{className:l(`relative min-h-[12rem] overflow-y-auto rounded-lg border border-edge-subtle/80 bg-surface-hover/20 p-1 dark:border-edge-subtle`,x.focusRingPanel),role:`listbox`,"aria-label":i.pathModalTitle,children:[I?(0,Y.jsx)(`div`,{className:`absolute inset-0 z-[1] flex items-center justify-center rounded-lg bg-surface-panel/60`,"aria-hidden":!0,children:(0,Y.jsx)(u,{className:`size-6 animate-spin text-fg-muted`})}):null,y&&!c?(0,Y.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-12 text-sm text-fg-muted`,children:[(0,Y.jsx)(u,{className:`size-4 animate-spin`,"aria-hidden":!0}),i.pickerLoading]}):null,w?(0,Y.jsx)(`p`,{className:`px-2 py-8 text-center text-sm text-fg-muted`,children:i.pickerListError}):null,!y&&c&&!w&&c.entries.length===0?(0,Y.jsx)(`p`,{className:`px-2 py-8 text-center text-sm text-fg-muted`,children:i.pickerEmptyFolder}):null,c?.entries.map(e=>(0,Y.jsxs)(`button`,{type:`button`,role:`option`,disabled:!e.isDirectory,className:l(`flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm`,e.isDirectory?`cursor-pointer text-fg hover:bg-surface-hover`:`cursor-default text-fg-muted opacity-60`),onClick:()=>A(e),children:[(0,Y.jsx)(C,{className:`size-4 shrink-0 text-fg-muted`,"aria-hidden":!0}),(0,Y.jsx)(`span`,{className:`min-w-0 truncate`,children:e.name})]},e.absolutePath))]}),(0,Y.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,Y.jsx)(`label`,{htmlFor:a,className:`text-xs text-fg-muted`,children:i.pickerManualPath}),(0,Y.jsx)(`input`,{id:a,type:`text`,value:D,onChange:e=>O(e.target.value),placeholder:i.pathInputPlaceholder,className:de(),autoComplete:`off`,spellCheck:!1,onKeyDown:e=>{e.key===`Enter`&&D.trim()&&(e.preventDefault(),F())}})]})]}),(0,Y.jsxs)(`div`,{className:`mt-4 flex flex-wrap items-center justify-end gap-2 border-t border-edge-subtle/60 pt-3`,children:[(0,Y.jsx)(_,{type:`button`,variant:`ghost`,onClick:()=>t(!1),children:i.pathModalCancel}),(0,Y.jsx)(_,{type:`button`,variant:`secondary`,disabled:!D.trim(),onClick:()=>void F(),children:i.pickerApplyManual}),(0,Y.jsx)(_,{type:`button`,disabled:!N,onClick:()=>void P(),children:i.pickerUseThisFolder})]})]})]})})}var Q=`xopc.recentWorkspaceDirs.v1`,pe=10;function me(){try{let e=localStorage.getItem(Q);if(!e)return[];let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`string`&&e.trim().length>0):[]}catch{return[]}}function he(e){let t=e.trim();if(t)try{let e=[t,...me().filter(e=>e!==t)].slice(0,pe);localStorage.setItem(Q,JSON.stringify(e))}catch{}}function $(e){let t=e.trim().replace(/[/\\]+$/,``);if(!t)return e;let n=t.split(/[/\\]/);return n[n.length-1]||t}var ge=(0,T.memo)(function({sessionKey:e,disabled:t,canSelectWorkingDirectory:n,sessionMgr:i}){let a=r(o(e=>e.language)).chat.workingDirectory,[s,c]=(0,T.useState)(``),[u,d]=(0,T.useState)(!1),[f,p]=(0,T.useState)(!1),m=(0,T.useCallback)(async()=>{if(!e){c(``);return}d(!0);try{c((await i.loadSessionAgentConfig(e)).effectiveWorkspacePath)}catch{c(``)}finally{d(!1)}},[e,i]);(0,T.useEffect)(()=>{m()},[m]);let h=(0,T.useCallback)(async t=>{if(!(!e?.trim()||!t.trim()))try{await i.patchSessionAgentConfig(e,{workingDirectory:t.trim()}),he(t.trim()),await m()}catch(e){let t=e instanceof Error?e.message:String(e);throw window.alert(t),e}},[e,i,m]),g=(0,T.useCallback)(async()=>{let e=typeof window<`u`?window.electronAPI?.file?.openDirectory:void 0;return e?e():null},[]),_=typeof window<`u`&&!!window.electronAPI?.file?.openDirectory,v=(0,T.useCallback)(()=>{_?(async()=>{let e=await g();e&&await h(e)})():p(!0)},[h,_,g]),y=(0,T.useCallback)(async e=>{let t=s.trim();if(t)try{await navigator.clipboard.writeText(t),window.dispatchEvent(new CustomEvent(`extension-notification`,{detail:{type:`success`,title:a.copied,...e?.includeWorkspaceLockHint?{message:a.selectionOnlyAtNewChat,duration:4500}:{duration:2500}}}))}catch{}},[s,a.copied,a.selectionOnlyAtNewChat]),b=(0,T.useCallback)(()=>{window.dispatchEvent(new CustomEvent(`extension-notification`,{detail:{type:`info`,title:a.lockedTapTitle,message:a.lockedTapBody,duration:6500}}))},[a.lockedTapBody,a.lockedTapTitle]);if(!e)return null;let S=s.trim(),w=!!S,E=w?$(S):a.notSet,D=l(`inline-flex min-h-8 max-w-[min(12rem,40vw)] min-w-0 shrink-0 items-center gap-1 rounded-lg px-2 py-1 text-xs`,`border border-edge-subtle/80 bg-surface-hover/40 dark:border-edge-subtle`,x.transition,x.focusRingPanel),O=e=>(0,Y.jsxs)(`button`,{type:`button`,className:l(D,`cursor-pointer text-left hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50`,!w&&`text-fg-muted`),title:e,"aria-label":e,onClick:e=>{e.stopPropagation(),b()},children:[(0,Y.jsx)(C,{className:`size-3.5 shrink-0 text-fg-muted`,"aria-hidden":!0}),(0,Y.jsx)(`span`,{className:`min-w-0 truncate text-left font-medium text-fg`,children:E})]}),k=e=>(0,Y.jsxs)(`button`,{type:`button`,disabled:t||u||!w,className:l(D,w&&`cursor-pointer hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50`,(t||u||!w)&&`cursor-not-allowed opacity-60`),title:e,"aria-label":e,onClick:e=>{e.stopPropagation(),w&&!t&&!u&&y({includeWorkspaceLockHint:!0})},children:[(0,Y.jsx)(C,{className:`size-3.5 shrink-0 text-fg-muted`,"aria-hidden":!0}),(0,Y.jsx)(`span`,{className:`min-w-0 truncate text-left font-medium text-fg`,children:E})]});if(!n)return w?(0,Y.jsx)(`div`,{className:`inline-flex items-center`,children:k(`${S}\n${a.clickToCopyFullPath}`)}):(0,Y.jsx)(`div`,{className:`inline-flex items-center`,children:O(`${a.notSet}\n${a.selectionOnlyAtNewChat}`)});let A=w?`${S}\n${a.chooseFolder}`:a.selectWorkingDirectory;return(0,Y.jsxs)(Y.Fragment,{children:[(0,Y.jsx)(`div`,{className:`inline-flex items-center`,children:(0,Y.jsxs)(`button`,{type:`button`,disabled:t||u,className:l(D,`cursor-pointer hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50`,(t||u)&&`cursor-not-allowed opacity-60`),title:A,"aria-label":A,onClick:e=>{e.stopPropagation(),!t&&!u&&v()},children:[(0,Y.jsx)(C,{className:`size-3.5 shrink-0 text-fg-muted`,"aria-hidden":!0}),(0,Y.jsx)(`span`,{className:`min-w-0 truncate text-left font-medium text-fg`,children:E})]})}),_?null:(0,Y.jsx)(Z,{open:f,onOpenChange:p,initialAbsolutePath:S||void 0,onConfirm:h,wd:a})]})});export{K as a,C as c,le as i,S as l,$ as n,P as o,Z as r,w as s,ge as t};
6
- //# sourceMappingURL=session-working-directory-control-B6dHLvbr.js.map
6
+ //# sourceMappingURL=session-working-directory-control-DKOtWs3-.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-working-directory-control-B6dHLvbr.js","names":["__iconNode","__iconNode"],"sources":["../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/chevron-up.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/folder-input.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/layout-template.js","../../../../../web/src/features/chat/agent-messages.ts","../../../../../web/src/features/chat/session-manager.ts","../../../../../web/src/features/chat/host-fs-api.ts","../../../../../web/src/features/chat/working-directory-picker-modal.tsx","../../../../../web/src/features/chat/session-working-directory-control.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [[\"path\", { d: \"m18 15-6-6-6 6\", key: \"153udz\" }]];\nconst ChevronUp = createLucideIcon(\"chevron-up\", __iconNode);\n\nexport { __iconNode, ChevronUp as default };\n//# sourceMappingURL=chevron-up.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2 9V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-1\",\n key: \"fm4g5t\"\n }\n ],\n [\"path\", { d: \"M2 13h10\", key: \"pgb2dq\" }],\n [\"path\", { d: \"m9 16 3-3-3-3\", key: \"6m91ic\" }]\n];\nconst FolderInput = createLucideIcon(\"folder-input\", __iconNode);\n\nexport { __iconNode, FolderInput as default };\n//# sourceMappingURL=folder-input.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"rect\", { width: \"18\", height: \"7\", x: \"3\", y: \"3\", rx: \"1\", key: \"f1a2em\" }],\n [\"rect\", { width: \"9\", height: \"7\", x: \"3\", y: \"14\", rx: \"1\", key: \"jqznyg\" }],\n [\"rect\", { width: \"5\", height: \"7\", x: \"16\", y: \"14\", rx: \"1\", key: \"q5h2i8\" }]\n];\nconst LayoutTemplate = createLucideIcon(\"layout-template\", __iconNode);\n\nexport { __iconNode, LayoutTemplate as default };\n//# sourceMappingURL=layout-template.js.map\n","import { inferMimeTypeFromFileName } from '@/features/chat/attachment-utils-core';\nimport type { Message, MessageAttachment, MessageContent, ThinkingContent, ToolUseContent } from '@/features/chat/messages.types';\n\n// =============================================================================\n// Type definitions for safe type narrowing (replaces Record<string, unknown> casts)\n// =============================================================================\n\ninterface WireContentBlock {\n type?: string;\n text?: string;\n name?: string;\n args?: Record<string, unknown>;\n input?: unknown;\n function?: { name?: string; arguments?: string | unknown };\n result?: string;\n source?: { data?: string; media_type?: string };\n /** Pi / gateway user turns: `{ type: 'image', data: base64, mimeType }` (no `source`). */\n data?: string;\n mimeType?: string;\n id?: string;\n}\n\ninterface WireMessage {\n role?: string;\n content?: unknown;\n tool_calls?: Array<{ id: string; function: { name: string; arguments: string } }>;\n toolCalls?: Array<{ id?: string; name: string; args?: Record<string, unknown> }>;\n attachments?: unknown;\n usage?: unknown;\n timestamp?: string | number;\n tool_call_id?: string;\n toolCallId?: string;\n isError?: boolean;\n}\n\n/** Tool-related blocks in session wire format (tool_use / OpenAI / pi toolCall). */\ninterface ToolCallBlock extends WireContentBlock {\n type?: string;\n name?: string;\n args?: Record<string, unknown>;\n /** Session/pi format often uses `arguments` (same role as `args`). */\n arguments?: unknown;\n input?: unknown;\n function?: { name?: string; arguments?: string | unknown };\n result?: string;\n status?: string;\n}\n\nfunction isWireSessionMessage(item: unknown): item is WireMessage {\n return typeof item === 'object' && item !== null && 'role' in item;\n}\n\nfunction isToolCallBlock(item: unknown): item is ToolCallBlock {\n if (!item || typeof item !== 'object') return false;\n const t = (item as Record<string, unknown>).type;\n return t === 'tool_use' || t === 'tool_call' || t === 'toolCall';\n}\n\nfunction extractToolBlockId(block: ToolCallBlock): string {\n if (typeof block.id === 'string' && block.id.length > 0) return block.id;\n return crypto.randomUUID();\n}\n\nfunction parseMaybeJsonString(raw: unknown): unknown {\n if (typeof raw !== 'string') return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/** Prefer pi `args`, then session `arguments`, then `input` / OpenAI `function.arguments`. */\nfunction extractToolCallBlockInput(block: ToolCallBlock): unknown {\n const raw = block.args ?? block.arguments ?? block.input ?? block.function?.arguments;\n const parsed = parseMaybeJsonString(raw);\n return parsed !== undefined && parsed !== null ? parsed : {};\n}\n\n// =============================================================================\n// Type guards for wire format blocks\n// =============================================================================\n\nfunction isWireContentBlock(item: unknown): item is WireContentBlock {\n return typeof item === 'object' && item !== null;\n}\n\n/** Plain text for search / previews over wire-format or UI message content. */\nexport function messageWireSearchText(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n const parts: string[] = [];\n for (const item of content) {\n if (!isWireContentBlock(item)) continue;\n const t = item.type;\n if (t === 'text' && typeof item.text === 'string') {\n parts.push(item.text);\n } else if (t === 'thinking') {\n const th =\n typeof (item as WireContentBlock & { thinking?: string }).thinking === 'string'\n ? (item as { thinking: string }).thinking\n : typeof item.text === 'string'\n ? item.text\n : '';\n if (th) parts.push(th);\n } else if (t === 'tool_use' || t === 'toolCall' || t === 'tool_call') {\n parts.push(String(item.name ?? 'tool'));\n }\n }\n return parts.join(' ');\n}\n\n/**\n * Maps pi-agent-core (or similar) message payloads into the web UI message model.\n */\nexport function normalizeAgentMessages(raw: readonly unknown[]): Message[] {\n return sessionWireToUiMessages(raw);\n}\n\n/**\n * Merge consecutive assistant bubbles into one (same as a single live streaming turn).\n * Persisted sessions often store one wire `assistant` row per thinking/tool fragment; without this,\n * the chat shows repeated \"execution\" lines after refresh.\n */\nfunction thinkingBlockComparableText(b: MessageContent): string {\n if (b.type !== 'thinking') {\n return '';\n }\n const x = b as ThinkingContent & { thinking?: string };\n return typeof x.text === 'string' ? x.text : typeof x.thinking === 'string' ? x.thinking : '';\n}\n\n/**\n * Merge two assistant content arrays when consecutive wire rows represent one turn.\n * - tool_use with the same id: keep the later block (e.g. completed tool after a fragment).\n * - adjacent thinking blocks with identical text: drop duplicate (streaming + persist overlap).\n */\nfunction mergeAssistantContentFragments(left: MessageContent[], right: MessageContent[]): MessageContent[] {\n const out: MessageContent[] = left.map((b) => ({ ...b }));\n const toolIndexById = new Map<string, number>();\n for (let i = 0; i < out.length; i++) {\n const b = out[i];\n if (b.type === 'tool_use') {\n toolIndexById.set(b.id, i);\n }\n }\n\n for (const b of right) {\n if (b.type === 'tool_use' && toolIndexById.has(b.id)) {\n const idx = toolIndexById.get(b.id)!;\n out[idx] = { ...b };\n continue;\n }\n if (b.type === 'thinking' && out.length > 0) {\n const last = out[out.length - 1];\n if (last.type === 'thinking' && thinkingBlockComparableText(last) === thinkingBlockComparableText(b)) {\n continue;\n }\n }\n if (b.type === 'tool_use') {\n toolIndexById.set(b.id, out.length);\n }\n out.push({ ...b });\n }\n return out;\n}\n\nexport function mergeConsecutiveAssistantMessages(messages: Message[]): Message[] {\n if (messages.length < 2) return messages;\n const out: Message[] = [];\n for (const m of messages) {\n if (m.role !== 'assistant') {\n out.push(m);\n continue;\n }\n const prev = out[out.length - 1];\n if (prev?.role === 'assistant') {\n prev.content = mergeAssistantContentFragments(prev.content, m.content);\n if (m.timestamp != null) prev.timestamp = m.timestamp;\n if (m.usage) prev.usage = m.usage;\n if (m.attachments?.length) {\n prev.attachments = dedupeAttachments([...(prev.attachments ?? []), ...m.attachments]);\n }\n } else {\n out.push({\n ...m,\n content: [...m.content],\n });\n }\n }\n return out;\n}\n\n/**\n * Convert session/API wire format (including toolResult rows) into chat UI messages.\n */\nexport function sessionWireToUiMessages(raw: readonly unknown[]): Message[] {\n const out: Message[] = [];\n\n for (const item of raw) {\n if (!isWireSessionMessage(item)) continue;\n const m = item;\n const role = String(m.role ?? '');\n\n if (role === 'system') {\n continue;\n }\n\n if (role === 'toolResult' || role === 'tool') {\n applyToolResultToLastAssistant(out, m);\n continue;\n }\n\n if (role === 'user' || role === 'user-with-attachments') {\n out.push(buildUserMessage(m));\n continue;\n }\n\n if (role === 'assistant') {\n out.push(buildAssistantMessage(m));\n continue;\n }\n }\n\n return mergeConsecutiveAssistantMessages(out);\n}\n\nfunction normalizeWireAttachments(raw: unknown): Message['attachments'] {\n if (!Array.isArray(raw)) return undefined;\n return raw.map((item) => normalizeOneAttachment(item));\n}\n\nfunction normalizeOneAttachment(item: unknown): MessageAttachment {\n if (!item || typeof item !== 'object') {\n return { name: 'file', mimeType: 'application/octet-stream' };\n }\n const a = item as Record<string, unknown>;\n const data = typeof a.data === 'string' ? a.data : undefined;\n const content =\n typeof a.content === 'string' && a.content.length > 0 ? a.content : data;\n const name = typeof a.name === 'string' && a.name.length > 0 ? a.name : 'file';\n let mimeType = typeof a.mimeType === 'string' && a.mimeType.length > 0 ? a.mimeType : '';\n if (!mimeType && typeof a.type === 'string' && a.type.includes('/')) {\n mimeType = a.type;\n }\n if (!mimeType) {\n mimeType = 'application/octet-stream';\n }\n const baseMime = mimeType.split(';')[0]?.trim().toLowerCase() ?? '';\n if (baseMime === 'application/octet-stream' || baseMime === '') {\n const inferred = inferMimeTypeFromFileName(name);\n if (inferred) {\n mimeType = inferred;\n }\n }\n const preview =\n typeof a.preview === 'string' && a.preview.length > 0\n ? a.preview\n : mimeType.startsWith('image/') && content\n ? content\n : undefined;\n\n return {\n id: typeof a.id === 'string' ? a.id : undefined,\n name,\n mimeType,\n type: typeof a.type === 'string' ? a.type : undefined,\n size: typeof a.size === 'number' ? a.size : undefined,\n content,\n data: data ?? content,\n preview,\n extractedText: typeof a.extractedText === 'string' ? a.extractedText : undefined,\n workspaceRelativePath:\n typeof a.workspaceRelativePath === 'string' && a.workspaceRelativePath.length > 0\n ? a.workspaceRelativePath\n : undefined,\n };\n}\n\n/**\n * Session stores the server-expanded skill body (see SkillManager.buildSkillBlock).\n * Collapse back to wire form for UI: `/skill:name` and optional trailing args from `**Arguments**:`.\n */\nexport function collapseExpandedSkillBlockForDisplay(text: string): string {\n if (typeof text !== 'string' || !text.includes('## Skill:')) {\n return text;\n }\n const nameMatch = text.match(/## Skill:\\s*([^\\s\\r\\n]+)/);\n if (!nameMatch) {\n return text;\n }\n const name = nameMatch[1] ?? '';\n if (!name) {\n return text;\n }\n const argMatches = [...text.matchAll(/\\*\\*Arguments\\*\\*:\\s*([^\\r\\n]+)/g)];\n const args =\n argMatches.length > 0 ? (argMatches[argMatches.length - 1]?.[1] ?? '').trim() : '';\n return args ? `/skill:${name} ${args}` : `/skill:${name}`;\n}\n\n/** Remove persisted inbound machine lines from bubble text (attachments show separately). */\nexport function stripInboundFileMachineText(text: string): string {\n if (!text.includes('xopc-path:')) return text;\n let out = text;\n // Multiline (canonical persist format)\n out = out.replace(\n /\\s*\\[File:[^\\]]+\\]\\s*\\r?\\nxopc-path:rel:[^\\r\\n]+\\r?\\n\\s*xopc-path:abs:[^\\r\\n]+/g,\n '',\n );\n // Single line (e.g. markdown collapsed whitespace)\n out = out.replace(/\\s*\\[File:[^\\]]+\\]\\s+xopc-path:rel:\\S+\\s+xopc-path:abs:\\S+/g, '');\n out = out.replace(/\\s*\\[File:[^\\]]+\\]\\s*xopc-path:rel:\\S+\\s*xopc-path:abs:\\S+/g, '');\n return out.replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n\nfunction parseFileLineMeta(fileMeta: string): { name: string; mimeType: string; size: number } {\n const nameMatch = fileMeta.match(/^([^(]+?)\\s*\\(/);\n const name = nameMatch ? nameMatch[1].trim() : 'file';\n const mimeMatch = fileMeta.match(/\\(\\s*([^,]+)\\s*,\\s*(\\d+)\\s*bytes\\s*\\)/i);\n const mimeType = mimeMatch ? mimeMatch[1].trim() : 'application/octet-stream';\n const size = mimeMatch ? parseInt(mimeMatch[2], 10) : 0;\n return { name, mimeType, size };\n}\n\nfunction extractAttachmentsFromUserContent(raw: unknown): Message['attachments'] | undefined {\n const chunks: string[] = [];\n if (typeof raw === 'string') {\n chunks.push(raw);\n } else if (Array.isArray(raw)) {\n for (const item of raw) {\n if (item && typeof item === 'object' && (item as { type?: string }).type === 'text') {\n const t = (item as { text?: string }).text;\n if (typeof t === 'string') chunks.push(t);\n }\n }\n }\n const text = chunks.join('\\n');\n if (!text.includes('xopc-path:rel:')) return undefined;\n\n const out: NonNullable<Message['attachments']> = [];\n const seen = new Set<string>();\n\n // Single line: rel is \\S+ so it stops before \" xopc-path:abs:\" (fixes greedy [^\\n]+ bug)\n const reSingle = /\\[File: ([^\\]]+)\\]\\s*xopc-path:rel:(\\S+)\\s*xopc-path:abs:\\S+/g;\n let m: RegExpExecArray | null;\n while ((m = reSingle.exec(text)) !== null) {\n const rel = m[2].trim();\n if (seen.has(rel)) continue;\n seen.add(rel);\n const { name, mimeType, size } = parseFileLineMeta(m[1]);\n out.push({\n name,\n mimeType,\n size,\n type: 'document',\n workspaceRelativePath: rel,\n });\n }\n\n const reMulti =\n /\\[File: ([^\\]]+)\\]\\s*\\r?\\nxopc-path:rel:([^\\r\\n]+)\\r?\\n\\s*xopc-path:abs:[^\\r\\n]+/g;\n while ((m = reMulti.exec(text)) !== null) {\n const rel = m[2].trim();\n if (seen.has(rel)) continue;\n seen.add(rel);\n const { name, mimeType, size } = parseFileLineMeta(m[1]);\n out.push({\n name,\n mimeType,\n size,\n type: 'document',\n workspaceRelativePath: rel,\n });\n }\n\n return out.length ? out : undefined;\n}\n\nfunction applyStripToUserContent(\n role: Message['role'],\n blocks: MessageContent[],\n): MessageContent[] {\n if (role !== 'user' && role !== 'user-with-attachments') return blocks;\n const mapped = blocks.map((b) => {\n if (b.type === 'text' && typeof b.text === 'string') {\n const stripped = stripInboundFileMachineText(b.text);\n return { ...b, text: collapseExpandedSkillBlockForDisplay(stripped) };\n }\n return b;\n });\n return mapped.filter((b) => {\n if (b.type === 'text' && (!b.text || !b.text.trim())) return false;\n return true;\n });\n}\n\n/** Deduplicate attachments that refer to the same workspace file (wire + parsed content often disagree on `name`). */\nfunction attachmentStableKey(a: MessageAttachment): string {\n const rel = a.workspaceRelativePath?.replace(/\\\\/g, '/').trim();\n if (rel) return `rel:${rel}`;\n if (a.id) return `id:${a.id}`;\n return `name:${a.name ?? 'file'}|${a.mimeType ?? ''}`;\n}\n\nfunction dedupeAttachments(list: Message['attachments'] | undefined): Message['attachments'] | undefined {\n if (!list?.length) return undefined;\n const out: NonNullable<Message['attachments']> = [];\n const seen = new Set<string>();\n for (const a of list) {\n const k = attachmentStableKey(a);\n if (seen.has(k)) continue;\n seen.add(k);\n out.push(a);\n }\n return out.length ? out : undefined;\n}\n\nfunction mergeUserAttachments(\n wire: Message['attachments'] | undefined,\n fromContent: Message['attachments'] | undefined,\n): Message['attachments'] | undefined {\n return dedupeAttachments([...(wire ?? []), ...(fromContent ?? [])]);\n}\n\nfunction buildUserMessage(m: WireMessage): Message {\n const roleRaw = String(m.role ?? 'user');\n const role: Message['role'] =\n roleRaw === 'user' || roleRaw === 'user-with-attachments'\n ? (roleRaw as Message['role'])\n : 'assistant';\n\n const fromContent = extractAttachmentsFromUserContent(m.content);\n\n return {\n role,\n content: applyStripToUserContent(role, normalizeContentBlocks(m.content)),\n attachments: mergeUserAttachments(normalizeWireAttachments(m.attachments), fromContent),\n timestamp: typeof m.timestamp === 'number' ? m.timestamp : parseTs(m.timestamp),\n usage: m.usage as Message['usage'],\n };\n}\n\nfunction buildAssistantMessage(m: WireMessage): Message {\n const content = mergeAssistantContent(m);\n return {\n role: 'assistant',\n content,\n attachments: dedupeAttachments(normalizeWireAttachments(m.attachments)),\n timestamp: typeof m.timestamp === 'number' ? m.timestamp : parseTs(m.timestamp),\n usage: m.usage as Message['usage'],\n };\n}\n\nfunction parseTs(raw: unknown): number {\n if (typeof raw === 'string') {\n const t = Date.parse(raw);\n return Number.isNaN(t) ? Date.now() : t;\n }\n return Date.now();\n}\n\nfunction mergeAssistantContent(m: WireMessage): MessageContent[] {\n const blocks = normalizeContentBlocks(m.content);\n\n const tc = m.tool_calls;\n if (Array.isArray(tc)) {\n for (const call of tc) {\n if (!call?.id || blocks.some((b) => b.type === 'tool_use' && b.id === call.id)) {\n continue;\n }\n let input: unknown = call.function?.arguments;\n if (typeof input === 'string') {\n try {\n input = JSON.parse(input);\n } catch {\n /* keep string */\n }\n }\n blocks.push({\n type: 'tool_use',\n id: call.id,\n name: call.function?.name || 'tool',\n input,\n status: 'running',\n });\n }\n }\n\n const piTcs = m.toolCalls;\n if (Array.isArray(piTcs)) {\n for (const call of piTcs) {\n const id = call.id ?? crypto.randomUUID();\n if (blocks.some((b) => b.type === 'tool_use' && b.id === id)) {\n continue;\n }\n blocks.push({\n type: 'tool_use',\n id,\n name: call.name || 'tool',\n input: call.args,\n status: 'running',\n });\n }\n }\n\n return blocks;\n}\n\nfunction applyToolResultToLastAssistant(out: Message[], m: WireMessage): void {\n const lastAssistant = findLastAssistant(out);\n if (!lastAssistant) return;\n\n const id = String(m.tool_call_id ?? m.toolCallId ?? '');\n const text = extractToolResultText(m.content);\n const isError = Boolean(m.isError);\n\n const block = id\n ? lastAssistant.content.find(\n (b): b is ToolUseContent => b.type === 'tool_use' && b.id === id,\n )\n : undefined;\n\n if (block) {\n block.status = isError ? 'error' : 'done';\n block.result = text;\n return;\n }\n\n const running = lastAssistant.content.filter(\n (b): b is ToolUseContent => b.type === 'tool_use' && b.status === 'running',\n );\n if (running.length === 1) {\n running[0].status = isError ? 'error' : 'done';\n running[0].result = text;\n }\n}\n\nfunction findLastAssistant(messages: Message[]): Message | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === 'assistant') {\n return messages[i];\n }\n }\n return null;\n}\n\nfunction extractToolResultText(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content\n .filter((c): c is WireContentBlock => isWireContentBlock(c) && c.type === 'text')\n .map((c) => String(c.text ?? ''))\n .join('\\n');\n }\n return String(content ?? '');\n}\n\n/** Map session/API image blocks to UI `ImageContent` (`source.data` is a usable `img` src). */\nfunction wireImageBlockToContent(item: WireContentBlock): MessageContent | null {\n const fromSource = item.source?.data;\n if (typeof fromSource === 'string' && fromSource.length > 0) {\n return { type: 'image', source: { data: fromSource } };\n }\n const raw = item.data;\n if (typeof raw !== 'string' || raw.length === 0) {\n return null;\n }\n const trimmed = raw.trim();\n if (trimmed.startsWith('data:')) {\n return { type: 'image', source: { data: trimmed } };\n }\n const mime =\n typeof item.mimeType === 'string' && item.mimeType.includes('/')\n ? item.mimeType\n : 'image/png';\n const compact = trimmed.replace(/\\s/g, '');\n return { type: 'image', source: { data: `data:${mime};base64,${compact}` } };\n}\n\nfunction normalizeContentBlocks(raw: unknown): MessageContent[] {\n if (raw == null) return [];\n if (typeof raw === 'string') {\n return raw.trim() ? [{ type: 'text', text: raw }] : [];\n }\n if (!Array.isArray(raw)) {\n return [{ type: 'text', text: String(raw) }];\n }\n\n const out: MessageContent[] = [];\n for (const item of raw) {\n if (!isWireContentBlock(item)) continue;\n \n const t = item.type;\n if (t === 'text' && typeof item.text === 'string') {\n out.push({ type: 'text', text: item.text });\n } else if (t === 'thinking') {\n const th =\n typeof (item as WireContentBlock & { thinking?: string }).thinking === 'string'\n ? (item as { thinking: string }).thinking\n : typeof item.text === 'string'\n ? item.text\n : '';\n out.push({ type: 'thinking', text: th, streaming: false });\n } else if (t === 'image') {\n const img = wireImageBlockToContent(item);\n if (img) {\n out.push(img);\n }\n } else if (t === 'tool_use' || t === 'tool_call') {\n if (!isToolCallBlock(item)) continue;\n const id = extractToolBlockId(item);\n const name = String(item.name ?? item.function?.name ?? 'tool');\n const input = item.input ?? item.function?.arguments;\n out.push({\n type: 'tool_use',\n id,\n name,\n input,\n status: 'done',\n result: typeof item.result === 'string' ? item.result : undefined,\n });\n } else if (t === 'toolCall') {\n if (!isToolCallBlock(item)) continue;\n const id = extractToolBlockId(item);\n const name = String(item.name ?? item.function?.name ?? 'tool');\n out.push({\n type: 'tool_use',\n id,\n name,\n input: extractToolCallBlockInput(item),\n status: 'done',\n result: typeof item.result === 'string' ? item.result : undefined,\n });\n }\n }\n return out;\n}\n","import type { Message } from '@/features/chat/messages.types';\nimport type { SessionInfo } from '@/features/chat/chat.types';\nimport { sessionWireToUiMessages } from '@/features/chat/agent-messages';\nimport { listSessions } from '@/features/sessions/session-api';\nimport { apiFetch } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\n/** Web UI chat sessions use segment `webchat` (same as `ui`). */\nexport function isWebUiSessionKey(key: string): boolean {\n return key.startsWith('gateway:') || key.includes(':gateway:') || key.includes(':webchat:');\n}\n\ntype SessionAgentConfig = {\n thinkingLevel: string;\n model: string;\n reasoningLevel: string;\n effectiveWorkspacePath: string;\n workingDirectoryLocked: boolean;\n};\n\nconst _agentConfigInflight = new Map<string, Promise<SessionAgentConfig>>();\n\n/** Session list + history via REST; auth from `apiFetch` (gateway token store). */\nexport class SessionManager {\n /** Same first page as sidebar (`limit=20&offset=0`) so `listSessions` in-flight dedupe applies. */\n async loadSessions(): Promise<SessionInfo[]> {\n const data = await listSessions({ limit: 20, offset: 0 });\n return data.items\n .filter((s) => isWebUiSessionKey(s.key))\n .map((s) => ({\n key: s.key,\n name: s.name,\n updatedAt: s.updatedAt,\n messageCount: s.messageCount,\n }))\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());\n }\n\n async loadSessionAgentConfig(sessionKey: string): Promise<SessionAgentConfig> {\n const existing = _agentConfigInflight.get(sessionKey);\n if (existing) return existing;\n\n const pending = (async () => {\n const res = await apiFetch(\n apiUrl(`/api/sessions/${encodeURIComponent(sessionKey)}/agent-config`),\n );\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as {\n payload?: {\n thinkingLevel?: string;\n model?: string;\n reasoningLevel?: string;\n effectiveWorkspacePath?: string;\n workingDirectoryLocked?: boolean;\n };\n };\n const thinkingLevel = data.payload?.thinkingLevel ?? 'medium';\n const model = typeof data.payload?.model === 'string' ? data.payload.model : '';\n const reasoningLevel = data.payload?.reasoningLevel ?? 'off';\n const effectiveWorkspacePath =\n typeof data.payload?.effectiveWorkspacePath === 'string'\n ? data.payload.effectiveWorkspacePath\n : '';\n const workingDirectoryLocked = Boolean(data.payload?.workingDirectoryLocked);\n return {\n thinkingLevel,\n model,\n reasoningLevel,\n effectiveWorkspacePath,\n workingDirectoryLocked,\n };\n })().finally(() => {\n _agentConfigInflight.delete(sessionKey);\n });\n\n _agentConfigInflight.set(sessionKey, pending);\n return pending;\n }\n\n async patchSessionAgentConfig(\n sessionKey: string,\n patch: { thinkingLevel?: string; model?: string | null; reasoningLevel?: string; workingDirectory?: string },\n ): Promise<void> {\n const res = await apiFetch(apiUrl(`/api/sessions/${encodeURIComponent(sessionKey)}/agent-config`), {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(patch),\n });\n if (!res.ok) {\n const j = (await res.json().catch(() => ({}))) as { error?: string };\n throw new Error(j.error ?? `HTTP ${res.status}`);\n }\n }\n\n async loadSession(\n sessionKey: string,\n offset = 0,\n ): Promise<{ messages: Message[]; hasMore: boolean; name?: string }> {\n const res = await apiFetch(\n apiUrl(`/api/sessions/${encodeURIComponent(sessionKey)}?offset=${offset}&limit=50`),\n );\n if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);\n const data = (await res.json()) as {\n session?: { messages?: unknown[]; name?: string };\n };\n const raw = data.session?.messages || [];\n const messages = sessionWireToUiMessages(raw);\n const name =\n typeof data.session?.name === 'string' && data.session.name.trim()\n ? data.session.name.trim()\n : undefined;\n return { messages, hasMore: raw.length >= 50, name };\n }\n\n async createSession(options?: { agentId?: string }): Promise<SessionInfo> {\n const body: Record<string, unknown> = { channel: 'webchat' };\n const raw = options?.agentId?.trim();\n if (raw) body.agentId = raw.toLowerCase();\n const res = await apiFetch(apiUrl('/api/sessions'), {\n method: 'POST',\n body: JSON.stringify(body),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as { session: SessionInfo };\n return data.session;\n }\n\n /** Lightweight name read after auto-title (matches `ui` SessionManager). */\n async fetchSessionName(sessionKey: string): Promise<string | undefined> {\n const res = await apiFetch(\n apiUrl(`/api/sessions/${encodeURIComponent(sessionKey)}?offset=0&limit=1`),\n );\n if (!res.ok) return undefined;\n const data = (await res.json()) as { session?: { name?: string } };\n const n = data.session?.name;\n return typeof n === 'string' && n.trim() ? n.trim() : undefined;\n }\n\n updateUrl(sessionKey: string): void {\n const newHash = `#/chat/${encodeURIComponent(sessionKey)}`;\n if (location.hash !== newHash) history.replaceState(null, '', newHash);\n }\n\n parseSessionFromHash(): string | null {\n const hash = location.hash.slice(1);\n const m = hash.match(/^\\/chat\\/(.+)$/) || hash.match(/^chat\\/(.+)$/);\n const key = m ? decodeURIComponent(m[1]) : null;\n return key && key !== 'new' ? key : null;\n }\n}\n","import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nexport type HostFsEntry = {\n name: string;\n absolutePath: string;\n isDirectory: boolean;\n};\n\nexport type HostFsListPayload = {\n currentPath: string;\n parentPath: string | null;\n entries: HostFsEntry[];\n};\n\nexport type HostFsMetaPayload = {\n hostname: string;\n platform: string;\n pathSeparator: string;\n};\n\ninterface ListResponse {\n ok: boolean;\n payload: HostFsListPayload;\n}\n\ninterface MetaResponse {\n ok: boolean;\n payload: HostFsMetaPayload;\n}\n\n/** List one directory level on the gateway host. Omit `path` for OS root (POSIX `/` or Windows drives). */\nexport async function listHostFs(path?: string): Promise<HostFsListPayload> {\n const params = new URLSearchParams();\n if (path != null && path !== '') {\n params.set('path', path);\n }\n const qs = params.toString();\n const res = await fetchJson<ListResponse>(apiUrl(`/api/host/fs/list${qs ? `?${qs}` : ''}`));\n return res.payload;\n}\n\nexport async function getHostFsMeta(): Promise<HostFsMetaPayload> {\n const res = await fetchJson<MetaResponse>(apiUrl('/api/host/fs/meta'));\n return res.payload;\n}\n","import * as Dialog from '@radix-ui/react-dialog';\nimport { ChevronUp, FolderInput, Loader2 } from 'lucide-react';\nimport { useCallback, useEffect, useId, useState } from 'react';\n\nimport { Button } from '@/components/ui/button';\nimport { getHostFsMeta, listHostFs, type HostFsEntry, type HostFsListPayload } from '@/features/chat/host-fs-api';\nimport { cn } from '@/lib/cn';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport { interaction } from '@/lib/interaction';\nimport type { MessageBundle } from '@/i18n/messages';\n\nfunction inputClassName(): string {\n return cn(\n 'w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 font-mono text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n 'dark:border-edge',\n );\n}\n\n/** Windows drive root like `C:\\` — parent is null but \"Up\" goes to drive list. */\nfunction isWindowsDriveRootPath(p: string): boolean {\n return /^[A-Za-z]:\\\\?$/.test(p.replace(/\\\\$/, '\\\\'));\n}\n\nfunction canGoUp(state: HostFsListPayload | null): boolean {\n if (!state || state.currentPath === '') return false;\n if (state.parentPath !== null) return true;\n if (state.currentPath === '/') return false;\n if (isWindowsDriveRootPath(state.currentPath)) return true;\n return false;\n}\n\ntype Props = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** When set, open this directory on first load (falls back to root on error). */\n initialAbsolutePath?: string;\n onConfirm: (absolutePath: string) => void | Promise<void>;\n wd: MessageBundle['chat']['workingDirectory'];\n};\n\nexport function WorkingDirectoryPickerModal({\n open,\n onOpenChange,\n initialAbsolutePath,\n onConfirm,\n wd,\n}: Props) {\n const manualId = useId();\n const [metaHostname, setMetaHostname] = useState<string | null>(null);\n const [listState, setListState] = useState<HostFsListPayload | null>(null);\n const [listLoading, setListLoading] = useState(false);\n const [listError, setListError] = useState<string | null>(null);\n const [manualPath, setManualPath] = useState('');\n\n const refreshFromPath = useCallback(async (pathArg?: string) => {\n setListLoading(true);\n setListError(null);\n try {\n const payload = await listHostFs(pathArg);\n setListState(payload);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n setListError(msg);\n setListState(null);\n } finally {\n setListLoading(false);\n }\n }, []);\n\n useEffect(() => {\n if (!open) return;\n let cancelled = false;\n void (async () => {\n try {\n const m = await getHostFsMeta();\n if (!cancelled) setMetaHostname(m.hostname);\n } catch {\n if (!cancelled) setMetaHostname(null);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n const initial = initialAbsolutePath?.trim() ?? '';\n setManualPath(initial);\n let cancelled = false;\n void (async () => {\n setListLoading(true);\n setListError(null);\n try {\n if (initial) {\n try {\n const payload = await listHostFs(initial);\n if (!cancelled) setListState(payload);\n } catch {\n const payload = await listHostFs();\n if (!cancelled) setListState(payload);\n }\n } else {\n const payload = await listHostFs();\n if (!cancelled) setListState(payload);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (!cancelled) {\n setListError(msg);\n setListState(null);\n }\n } finally {\n if (!cancelled) setListLoading(false);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [open, initialAbsolutePath]);\n\n const enterDir = (entry: HostFsEntry) => {\n if (!entry.isDirectory) return;\n void refreshFromPath(entry.absolutePath);\n };\n\n const goUp = () => {\n if (!listState) return;\n const { parentPath, currentPath } = listState;\n if (parentPath !== null) {\n void refreshFromPath(parentPath);\n return;\n }\n if (currentPath === '/' || currentPath === '') return;\n if (isWindowsDriveRootPath(currentPath)) {\n void refreshFromPath(undefined);\n }\n };\n\n const currentDisplayPath =\n listState?.currentPath === '' ? wd.pickerDrives : (listState?.currentPath ?? '');\n\n const canUseCurrentFolder =\n Boolean(listState) && listState!.currentPath !== '' && !listLoading && !listError;\n\n const onUseFolder = async () => {\n if (!listState || listState.currentPath === '') return;\n try {\n await onConfirm(listState.currentPath);\n onOpenChange(false);\n } catch {\n /* PATCH failed; stay open */\n }\n };\n\n const onApplyManual = async () => {\n const t = manualPath.trim();\n if (!t) return;\n try {\n await onConfirm(t);\n onOpenChange(false);\n } catch {\n /* PATCH failed; stay open */\n }\n };\n\n const showLoadingOverlay = listLoading && listState;\n\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Portal>\n <Dialog.Overlay className=\"fixed inset-0 z-[80] bg-scrim backdrop-blur-[2px]\" />\n <Dialog.Content\n className={cn(\n 'fixed left-1/2 top-1/2 z-[81] flex max-h-[min(90vh,32rem)] w-[min(100%-2rem,28rem)] -translate-x-1/2 -translate-y-1/2 flex-col rounded-xl border border-edge bg-surface-panel p-4 shadow-popover',\n 'dark:border-edge',\n )}\n onOpenAutoFocus={(e) => e.preventDefault()}\n >\n <Dialog.Title className=\"text-base font-semibold text-fg\">{wd.pathModalTitle}</Dialog.Title>\n <Dialog.Description className=\"mt-1 text-sm leading-relaxed text-fg-muted\">\n {wd.pathModalDescription}\n </Dialog.Description>\n {metaHostname ? (\n <p className=\"mt-2 text-xs text-fg-muted\">{wd.pickerHostHint.replace('{{hostname}}', metaHostname)}</p>\n ) : null}\n\n <div className=\"mt-3 flex min-h-0 flex-1 flex-col gap-2\">\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n className=\"shrink-0 gap-1 px-2 py-1.5 text-xs\"\n disabled={listLoading || !canGoUp(listState) || Boolean(listError)}\n onClick={() => goUp()}\n title={wd.pickerUp}\n >\n <ChevronUp className=\"size-4\" aria-hidden />\n {wd.pickerUp}\n </Button>\n <div\n className=\"min-w-0 flex-1 truncate rounded-md border border-edge-subtle/80 bg-surface-hover/30 px-2 py-1.5 font-mono text-xs text-fg\"\n title={currentDisplayPath}\n >\n {listLoading && !listState ? wd.pickerLoading : currentDisplayPath || '—'}\n </div>\n </div>\n\n <div\n className={cn(\n 'relative min-h-[12rem] overflow-y-auto rounded-lg border border-edge-subtle/80 bg-surface-hover/20 p-1 dark:border-edge-subtle',\n interaction.focusRingPanel,\n )}\n role=\"listbox\"\n aria-label={wd.pathModalTitle}\n >\n {showLoadingOverlay ? (\n <div\n className=\"absolute inset-0 z-[1] flex items-center justify-center rounded-lg bg-surface-panel/60\"\n aria-hidden\n >\n <Loader2 className=\"size-6 animate-spin text-fg-muted\" />\n </div>\n ) : null}\n {listLoading && !listState ? (\n <div className=\"flex items-center justify-center gap-2 py-12 text-sm text-fg-muted\">\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n {wd.pickerLoading}\n </div>\n ) : null}\n {listError ? (\n <p className=\"px-2 py-8 text-center text-sm text-fg-muted\">{wd.pickerListError}</p>\n ) : null}\n {!listLoading && listState && !listError && listState.entries.length === 0 ? (\n <p className=\"px-2 py-8 text-center text-sm text-fg-muted\">{wd.pickerEmptyFolder}</p>\n ) : null}\n {listState?.entries.map((e) => (\n <button\n key={e.absolutePath}\n type=\"button\"\n role=\"option\"\n disabled={!e.isDirectory}\n className={cn(\n 'flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm',\n e.isDirectory\n ? 'cursor-pointer text-fg hover:bg-surface-hover'\n : 'cursor-default text-fg-muted opacity-60',\n )}\n onClick={() => enterDir(e)}\n >\n <FolderInput className=\"size-4 shrink-0 text-fg-muted\" aria-hidden />\n <span className=\"min-w-0 truncate\">{e.name}</span>\n </button>\n ))}\n </div>\n\n <div className=\"space-y-1.5\">\n <label htmlFor={manualId} className=\"text-xs text-fg-muted\">\n {wd.pickerManualPath}\n </label>\n <input\n id={manualId}\n type=\"text\"\n value={manualPath}\n onChange={(e) => setManualPath(e.target.value)}\n placeholder={wd.pathInputPlaceholder}\n className={inputClassName()}\n autoComplete=\"off\"\n spellCheck={false}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && manualPath.trim()) {\n e.preventDefault();\n void onApplyManual();\n }\n }}\n />\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-end gap-2 border-t border-edge-subtle/60 pt-3\">\n <Button type=\"button\" variant=\"ghost\" onClick={() => onOpenChange(false)}>\n {wd.pathModalCancel}\n </Button>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={!manualPath.trim()}\n onClick={() => void onApplyManual()}\n >\n {wd.pickerApplyManual}\n </Button>\n <Button type=\"button\" disabled={!canUseCurrentFolder} onClick={() => void onUseFolder()}>\n {wd.pickerUseThisFolder}\n </Button>\n </div>\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n","import { FolderInput } from 'lucide-react';\nimport { memo, useCallback, useEffect, useState } from 'react';\n\nimport { SessionManager } from '@/features/chat/session-manager';\nimport { WorkingDirectoryPickerModal } from '@/features/chat/working-directory-picker-modal';\nimport { cn } from '@/lib/cn';\nimport { interaction } from '@/lib/interaction';\nimport { messages } from '@/i18n/messages';\nimport { useLocaleStore } from '@/stores/locale-store';\n\nconst RECENT_DIRS_KEY = 'xopc.recentWorkspaceDirs.v1';\nconst MAX_RECENT = 10;\n\nfunction readRecentDirs(): string[] {\n try {\n const raw = localStorage.getItem(RECENT_DIRS_KEY);\n if (!raw) return [];\n const parsed = JSON.parse(raw) as unknown;\n return Array.isArray(parsed)\n ? parsed.filter((x): x is string => typeof x === 'string' && x.trim().length > 0)\n : [];\n } catch {\n return [];\n }\n}\n\nfunction pushRecentDir(path: string): void {\n const t = path.trim();\n if (!t) return;\n try {\n const prev = readRecentDirs();\n const next = [t, ...prev.filter((p) => p !== t)].slice(0, MAX_RECENT);\n localStorage.setItem(RECENT_DIRS_KEY, JSON.stringify(next));\n } catch {\n /* ignore */\n }\n}\n\n/** Last path segment for display (folder name only). */\nexport function folderDisplayName(absPath: string): string {\n const t = absPath.trim().replace(/[/\\\\]+$/, '');\n if (!t) return absPath;\n const parts = t.split(/[/\\\\]/);\n return parts[parts.length - 1] || t;\n}\n\ntype Props = {\n sessionKey: string | null;\n disabled: boolean;\n /** Only while the conversation has no messages yet (new chat start). */\n canSelectWorkingDirectory: boolean;\n sessionMgr: SessionManager;\n};\n\nexport const SessionWorkingDirectoryControl = memo(function SessionWorkingDirectoryControl({\n sessionKey,\n disabled,\n canSelectWorkingDirectory,\n sessionMgr,\n}: Props) {\n const language = useLocaleStore((s) => s.language);\n const m = messages(language);\n const wd = m.chat.workingDirectory;\n\n const [effectivePath, setEffectivePath] = useState('');\n const [loading, setLoading] = useState(false);\n const [pathModalOpen, setPathModalOpen] = useState(false);\n\n const refresh = useCallback(async () => {\n if (!sessionKey) {\n setEffectivePath('');\n return;\n }\n setLoading(true);\n try {\n const cfg = await sessionMgr.loadSessionAgentConfig(sessionKey);\n setEffectivePath(cfg.effectiveWorkspacePath);\n } catch {\n setEffectivePath('');\n } finally {\n setLoading(false);\n }\n }, [sessionKey, sessionMgr]);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const applyPath = useCallback(\n async (path: string) => {\n if (!sessionKey?.trim() || !path.trim()) return;\n try {\n await sessionMgr.patchSessionAgentConfig(sessionKey, { workingDirectory: path.trim() });\n pushRecentDir(path.trim());\n await refresh();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n window.alert(msg);\n throw e;\n }\n },\n [sessionKey, sessionMgr, refresh],\n );\n\n /** Native folder dialog — only available in Electron desktop build. */\n const openNativeFolderPicker = useCallback(async (): Promise<string | null> => {\n const api = typeof window !== 'undefined' ? window.electronAPI?.file?.openDirectory : undefined;\n if (api) return api();\n return null;\n }, []);\n\n const hasElectronFolderPicker =\n typeof window !== 'undefined' && Boolean(window.electronAPI?.file?.openDirectory);\n\n const onSelectWorkingDirectoryClick = useCallback(() => {\n if (hasElectronFolderPicker) {\n void (async () => {\n const picked = await openNativeFolderPicker();\n if (picked) await applyPath(picked);\n })();\n } else {\n setPathModalOpen(true);\n }\n }, [applyPath, hasElectronFolderPicker, openNativeFolderPicker]);\n\n const copyFullPath = useCallback(\n async (opts?: { includeWorkspaceLockHint?: boolean }) => {\n const full = effectivePath.trim();\n if (!full) return;\n try {\n await navigator.clipboard.writeText(full);\n window.dispatchEvent(\n new CustomEvent('extension-notification', {\n detail: {\n type: 'success' as const,\n title: wd.copied,\n ...(opts?.includeWorkspaceLockHint ? { message: wd.selectionOnlyAtNewChat, duration: 4500 } : { duration: 2500 }),\n },\n }),\n );\n } catch {\n /* ignore */\n }\n },\n [effectivePath, wd.copied, wd.selectionOnlyAtNewChat],\n );\n\n const showWorkspaceLockedReminder = useCallback(() => {\n window.dispatchEvent(\n new CustomEvent('extension-notification', {\n detail: {\n type: 'info' as const,\n title: wd.lockedTapTitle,\n message: wd.lockedTapBody,\n duration: 6500,\n },\n }),\n );\n }, [wd.lockedTapBody, wd.lockedTapTitle]);\n\n if (!sessionKey) {\n return null;\n }\n\n const fullPath = effectivePath.trim();\n const hasPath = Boolean(fullPath);\n const label = hasPath ? folderDisplayName(fullPath) : wd.notSet;\n\n const chipClass = cn(\n 'inline-flex min-h-8 max-w-[min(12rem,40vw)] min-w-0 shrink-0 items-center gap-1 rounded-lg px-2 py-1 text-xs',\n 'border border-edge-subtle/80 bg-surface-hover/40 dark:border-edge-subtle',\n interaction.transition,\n interaction.focusRingPanel,\n );\n\n const readOnlyChip = (title: string) => (\n <button\n type=\"button\"\n className={cn(\n chipClass,\n 'cursor-pointer text-left hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50',\n !hasPath && 'text-fg-muted',\n )}\n title={title}\n aria-label={title}\n onClick={(e) => {\n e.stopPropagation();\n showWorkspaceLockedReminder();\n }}\n >\n <FolderInput className=\"size-3.5 shrink-0 text-fg-muted\" aria-hidden />\n <span className=\"min-w-0 truncate text-left font-medium text-fg\">{label}</span>\n </button>\n );\n\n const copyPathChip = (title: string) => (\n <button\n type=\"button\"\n disabled={disabled || loading || !hasPath}\n className={cn(\n chipClass,\n hasPath && 'cursor-pointer hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50',\n (disabled || loading || !hasPath) && 'cursor-not-allowed opacity-60',\n )}\n title={title}\n aria-label={title}\n onClick={(e) => {\n e.stopPropagation();\n if (hasPath && !disabled && !loading) void copyFullPath({ includeWorkspaceLockHint: true });\n }}\n >\n <FolderInput className=\"size-3.5 shrink-0 text-fg-muted\" aria-hidden />\n <span className=\"min-w-0 truncate text-left font-medium text-fg\">{label}</span>\n </button>\n );\n\n if (!canSelectWorkingDirectory) {\n if (!hasPath) {\n return (\n <div className=\"inline-flex items-center\">\n {readOnlyChip(`${wd.notSet}\\n${wd.selectionOnlyAtNewChat}`)}\n </div>\n );\n }\n return (\n <div className=\"inline-flex items-center\">\n {copyPathChip(`${fullPath}\\n${wd.clickToCopyFullPath}`)}\n </div>\n );\n }\n\n const titleSelectable = hasPath ? `${fullPath}\\n${wd.chooseFolder}` : wd.selectWorkingDirectory;\n\n return (\n <>\n <div className=\"inline-flex items-center\">\n <button\n type=\"button\"\n disabled={disabled || loading}\n className={cn(\n chipClass,\n 'cursor-pointer hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50',\n (disabled || loading) && 'cursor-not-allowed opacity-60',\n )}\n title={titleSelectable}\n aria-label={titleSelectable}\n onClick={(e) => {\n e.stopPropagation();\n if (!disabled && !loading) onSelectWorkingDirectoryClick();\n }}\n >\n <FolderInput className=\"size-3.5 shrink-0 text-fg-muted\" aria-hidden />\n <span className=\"min-w-0 truncate text-left font-medium text-fg\">{label}</span>\n </button>\n </div>\n\n {!hasElectronFolderPicker ? (\n <WorkingDirectoryPickerModal\n open={pathModalOpen}\n onOpenChange={setPathModalOpen}\n initialAbsolutePath={fullPath || undefined}\n onConfirm={applyPath}\n wd={wd}\n />\n ) : null}\n </>\n );\n});\n"],"x_google_ignoreList":[0,1,2],"mappings":"ikBAUA,IAAM,EAAY,EAAiB,aADhB,CAAC,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,SAAU,CAAC,CAAC,CACT,CCUtD,EAAc,EAAiB,eAXlB,CACjB,CACE,OACA,CACE,EAAG,0HACH,IAAK,SACN,CACF,CACD,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,SAAU,CAAC,CAC1C,CAAC,OAAQ,CAAE,EAAG,gBAAiB,IAAK,SAAU,CAAC,CAChD,CAC+D,CCN1D,EAAiB,EAAiB,kBALrB,CACjB,CAAC,OAAQ,CAAE,MAAO,KAAM,OAAQ,IAAK,EAAG,IAAK,EAAG,IAAK,GAAI,IAAK,IAAK,SAAU,CAAC,CAC9E,CAAC,OAAQ,CAAE,MAAO,IAAK,OAAQ,IAAK,EAAG,IAAK,EAAG,KAAM,GAAI,IAAK,IAAK,SAAU,CAAC,CAC9E,CAAC,OAAQ,CAAE,MAAO,IAAK,OAAQ,IAAK,EAAG,KAAM,EAAG,KAAM,GAAI,IAAK,IAAK,SAAU,CAAC,CAChF,CACqE,YCkCtE,SAAA,EAAA,EAAA,CACE,OAAA,OAAA,GAAA,YAAA,GAAA,SAAA,EAGF,SAAA,EAAA,EAAA,CACE,GAAA,CAAA,GAAA,OAAA,GAAA,SAAA,MAAA,gBAEA,OAAA,IAAA,YAAA,IAAA,aAAA,IAAA,WAGF,SAAA,EAAA,EAAA,CAEE,OADA,OAAA,EAAA,IAAA,UAAA,EAAA,GAAA,OAAA,EAAA,EAAA,GACA,OAAA,YAAA,CAGF,SAAA,EAAA,EAAA,CACE,GAAA,OAAA,GAAA,SAAA,OAAA,EACA,GAAA,CACE,OAAA,KAAA,MAAA,EAAA,OAEA,OAAA,GAKJ,SAAA,EAAA,EAAA,CAGE,+DAAA,EAAA,CAOF,SAAA,EAAA,EAAA,CACE,OAAA,OAAA,GAAA,YAAA,EA4CF,SAAA,EAAA,EAAA,CACE,GAAA,EAAA,OAAA,WAAA,MAAA,WAIA,OAAA,OAAA,EAAA,MAAA,SAAA,EAAA,KAAA,OAAA,EAAA,UAAA,SAAA,EAAA,SAAA,GAQF,SAAA,EAAA,EAAA,EAAA,oCAGE,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,OAAA,IAAA,YAEE,EAAA,OAAA,YAAA,EAAA,IAAA,EAAA,GAAA,EAAA,CAKF,IAAA,IAAA,KAAA,EAAA,CACE,GAAA,EAAA,OAAA,YAAA,EAAA,IAAA,EAAA,GAAA,CAAA,mBAEE,EAAA,GAAA,CAAA,GAAA,EAAA,CACA,SAEF,GAAA,EAAA,OAAA,YAAA,EAAA,OAAA,EAAA,qBAEE,GAAA,EAAA,OAAA,YAAA,EAAA,EAAA,GAAA,EAAA,EAAA,CAAA,SAIF,EAAA,OAAA,YAAA,EAAA,IAAA,EAAA,GAAA,EAAA,OAAA,CAGA,EAAA,KAAA,CAAA,GAAA,EAAA,CAAA,CAEF,OAAA,EAGF,SAAA,EAAA,EAAA,CACE,GAAA,EAAA,OAAA,EAAA,OAAA,WAEA,IAAA,IAAA,KAAA,EAAA,CACE,GAAA,EAAA,OAAA,YAAA,CACE,EAAA,KAAA,EAAA,CACA,6BAGF,GAAA,OAAA,aACE,EAAA,QAAA,EAAA,EAAA,QAAA,EAAA,QAAA,CACA,EAAA,WAAA,OAAA,EAAA,UAAA,EAAA,WACA,EAAA,QAAA,EAAA,MAAA,EAAA,OACA,EAAA,aAAA,SAAA,EAAA,YAAA,EAAA,CAAA,GAAA,EAAA,aAAA,EAAA,CAAA,GAAA,EAAA,YAAA,CAAA,yCAUJ,OAAA,EAMF,SAAA,EAAA,EAAA,UAGE,IAAA,IAAA,KAAA,EAAA,CACE,GAAA,CAAA,EAAA,EAAA,CAAA,sCAIA,OAAA,SAIA,IAAA,IAAA,cAAA,IAAA,OAAA,CACE,GAAA,EAAA,EAAA,CACA,SAGF,GAAA,IAAA,QAAA,IAAA,wBAAA,CACE,EAAA,KAAA,EAAA,EAAA,CAAA,CACA,SAGF,GAAA,IAAA,YAAA,CACE,EAAA,KAAA,GAAA,EAAA,CAAA,CACA,WAIJ,OAAA,EAAA,EAAA,CAGF,SAAA,EAAA,EAAA,CACE,SAAA,QAAA,EAAA,CACA,OAAA,EAAA,IAAA,GAAA,GAAA,EAAA,CAAA,CAGF,SAAA,GAAA,EAAA,CACE,GAAA,CAAA,GAAA,OAAA,GAAA,SAAA,MAAA,yRASA,CAAA,GAAA,OAAA,EAAA,MAAA,UAAA,EAAA,KAAA,SAAA,IAAA,GAAA,EAAA,EAAA,MAGA,AAAA,IAAA,2EAIA,GAAA,IAAA,4BAAA,IAAA,GAAA,YAEE,IAAA,EAAA,qGAWF,MAAA,mXAqBF,SAAA,GAAA,EAAA,CACE,GAAA,OAAA,GAAA,UAAA,CAAA,EAAA,SAAA,YAAA,CAAA,OAAA,4CAIA,GAAA,CAAA,EAAA,OAAA,iBAIA,GAAA,CAAA,EAAA,OAAA,4GAMA,OAAA,EAAA,UAAA,EAAA,GAAA,IAAA,UAAA,IAIF,SAAA,GAAA,EAAA,CACE,GAAA,CAAA,EAAA,SAAA,aAAA,CAAA,OAAA,UAUA,MAPA,GAAA,EAAA,QAAA,kFAAA,GAAA,CAKA,EAAA,EAAA,QAAA,8DAAA,GAAA,CACA,EAAA,EAAA,QAAA,8DAAA,GAAA,CACA,EAAA,QAAA,UAAA;;EAAA,CAAA,MAAA,CAGF,SAAA,EAAA,EAAA,4GAME,MAAA,sFAGF,SAAA,EAAA,EAAA,UAEE,GAAA,OAAA,GAAA,SAAA,EAAA,KAAA,EAAA,kGAMM,OAAA,GAAA,UAAA,EAAA,KAAA,EAAA;GAKN,GAAA,CAAA,EAAA,SAAA,iBAAA,CAAA,8FAQA,MAAA,EAAA,EAAA,KAAA,EAAA,IAAA,MAAA,mBAEE,GAAA,EAAA,IAAA,EAAA,CAAA,SACA,EAAA,IAAA,EAAA,uCAEA,EAAA,KAAA,8JAWF,MAAA,EAAA,EAAA,KAAA,EAAA,IAAA,MAAA,mBAEE,GAAA,EAAA,IAAA,EAAA,CAAA,SACA,EAAA,IAAA,EAAA,uCAEA,EAAA,KAAA,oEASF,OAAA,EAAA,OAAA,EAAA,IAAA,GAGF,SAAA,EAAA,EAAA,EAAA,CAYE,OARA,IAAA,QAAA,IAAA,wBAAA,EAQA,EAAA,IAAA,GAAA,CANE,GAAA,EAAA,OAAA,QAAA,OAAA,EAAA,MAAA,SAAA,kBAEE,MAAA,kBAEF,OAAA,cAGA,EAAA,EAAA,OAAA,SAAA,CAAA,EAAA,MAAA,CAAA,EAAA,KAAA,MAAA,IAMJ,SAAA,EAAA,EAAA,0DAIE,OAFA,EAAA,OAAA,IACA,EAAA,GAAA,MAAA,EAAA,KACA,QAAA,EAAA,MAAA,OAAA,GAAA,EAAA,UAAA,KAGF,SAAA,EAAA,EAAA,CACE,GAAA,CAAA,GAAA,OAAA,0BAGA,IAAA,IAAA,KAAA,EAAA,YAEE,EAAA,IAAA,EAAA,GACA,EAAA,IAAA,EAAA,CACA,EAAA,KAAA,EAAA,EAEF,OAAA,EAAA,OAAA,EAAA,IAAA,GAGF,SAAA,EAAA,EAAA,EAAA,CAIE,OAAA,EAAA,CAAA,GAAA,GAAA,EAAA,CAAA,GAAA,GAAA,EAAA,CAAA,CAAA,CAGF,SAAA,EAAA,EAAA,qGASE,MAAA,qJASF,SAAA,GAAA,EAAA,CAEE,MAAA,iJASF,SAAA,EAAA,EAAA,CACE,GAAA,OAAA,GAAA,SAAA,qBAEE,OAAA,OAAA,MAAA,EAAA,CAAA,KAAA,KAAA,CAAA,EAEF,OAAA,KAAA,KAAA,CAGF,SAAA,GAAA,EAAA,mCAIE,GAAA,MAAA,QAAA,EAAA,CAAA,IAAA,IAAA,KAAA,EAAA,CAEI,GAAA,CAAA,GAAA,IAAA,EAAA,KAAA,GAAA,EAAA,OAAA,YAAA,EAAA,KAAA,EAAA,GAAA,CAAA,qCAIA,GAAA,OAAA,GAAA,SAAA,GAAA,CAEI,EAAA,KAAA,MAAA,EAAA,QAKJ,EAAA,KAAA,oGAWJ,GAAA,MAAA,QAAA,EAAA,CAAA,IAAA,IAAA,KAAA,EAAA,iCAGI,EAAA,KAAA,GAAA,EAAA,OAAA,YAAA,EAAA,KAAA,EAAA,EAGA,EAAA,KAAA,0EAUJ,OAAA,EAGF,SAAA,GAAA,EAAA,EAAA,aAEE,GAAA,CAAA,EAAA,gJAYA,GAAA,EAAA,CACE,EAAA,OAAA,EAAA,QAAA,OACA,EAAA,OAAA,EACA,4EAMF,EAAA,SAAA,IACE,EAAA,GAAA,OAAA,EAAA,QAAA,OACA,EAAA,GAAA,OAAA,GAIJ,SAAA,GAAA,EAAA,CACE,IAAA,IAAA,EAAA,EAAA,OAAA,EAAA,GAAA,EAAA,IAAA,GAAA,EAAA,GAAA,OAAA,YAAA,OAAA,EAAA,GAKA,OAAA,KAGF,SAAA,GAAA,EAAA,CAUE,OATA,OAAA,GAAA,SAAA,EAGA,MAAA,QAAA,EAAA,CAAA,EAAA,OAAA,GAAA,EAAA,EAAA,EAAA,EAAA,OAAA,OAAA,CAAA,IAAA,GAAA,OAAA,EAAA,MAAA,GAAA,CAAA,CAAA,KAAA;EAAA,CAMA,OAAA,GAAA,GAAA,CAIF,SAAA,GAAA,EAAA,sBAEE,GAAA,OAAA,GAAA,UAAA,EAAA,OAAA,EAAA,MAAA,4CAIA,GAAA,OAAA,GAAA,UAAA,EAAA,SAAA,EAAA,OAAA,oBAYA,OARA,EAAA,WAAA,QAAA,CAAA,+BAQA,kJAGF,SAAA,EAAA,EAAA,CACE,GAAA,GAAA,KAAA,MAAA,EAAA,CACA,GAAA,OAAA,GAAA,SAAA,OAAA,EAAA,MAAA,CAAA,CAAA,yBAGA,GAAA,CAAA,MAAA,QAAA,EAAA,CAAA,MAAA,CAAA,uCAKA,IAAA,IAAA,KAAA,EAAA,CACE,GAAA,CAAA,EAAA,EAAA,CAAA,sBAGA,GAAA,IAAA,QAAA,OAAA,EAAA,MAAA,SAAA,EAAA,KAAA,kIASE,EAAA,KAAA,wEAGA,GAAA,EAAA,KAAA,EAAA,0CAIA,GAAA,CAAA,EAAA,EAAA,CAAA,gGAIA,EAAA,KAAA,8HASA,GAAA,CAAA,EAAA,EAAA,CAAA,+DAGA,EAAA,KAAA,0GAUJ,OAAA,EC1nBF,SAAgB,EAAkB,EAAsB,CACtD,OAAO,EAAI,WAAW,WAAW,EAAI,EAAI,SAAS,YAAY,EAAI,EAAI,SAAS,YAAY,CAW7F,IAAM,EAAuB,IAAI,IAGpB,GAAb,KAA4B,CAE1B,MAAM,cAAuC,CAE3C,OADa,MAAM,EAAa,CAAE,MAAO,GAAI,OAAQ,EAAG,CAAC,EAC7C,MACT,OAAQ,GAAM,EAAkB,EAAE,IAAI,CAAC,CACvC,IAAK,IAAO,CACX,IAAK,EAAE,IACP,KAAM,EAAE,KACR,UAAW,EAAE,UACb,aAAc,EAAE,aACjB,EAAE,CACF,MAAM,EAAG,IAAM,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,CAAG,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,CAGtF,MAAM,uBAAuB,EAAiD,CAC5E,IAAM,EAAW,EAAqB,IAAI,EAAW,CACrD,GAAI,EAAU,OAAO,EAErB,IAAM,GAAW,SAAY,CAC3B,IAAM,EAAM,MAAM,EAChB,EAAO,iBAAiB,mBAAmB,EAAW,CAAC,eAAe,CACvE,CACD,GAAI,CAAC,EAAI,GAAI,MAAU,MAAM,QAAQ,EAAI,SAAS,CAClD,IAAM,EAAQ,MAAM,EAAI,MAAM,CAiB9B,MAAO,CACL,cAToB,EAAK,SAAS,eAAiB,SAUnD,MATY,OAAO,EAAK,SAAS,OAAU,SAAW,EAAK,QAAQ,MAAQ,GAU3E,eATqB,EAAK,SAAS,gBAAkB,MAUrD,uBARA,OAAO,EAAK,SAAS,wBAA2B,SAC5C,EAAK,QAAQ,uBACb,GAOJ,uBAN6B,EAAQ,EAAK,SAAS,uBAOpD,IACC,CAAC,YAAc,CACjB,EAAqB,OAAO,EAAW,EACvC,CAGF,OADA,EAAqB,IAAI,EAAY,EAAQ,CACtC,EAGT,MAAM,wBACJ,EACA,EACe,CACf,IAAM,EAAM,MAAM,EAAS,EAAO,iBAAiB,mBAAmB,EAAW,CAAC,eAAe,CAAE,CACjG,OAAQ,QACR,QAAS,CAAE,eAAgB,mBAAoB,CAC/C,KAAM,KAAK,UAAU,EAAM,CAC5B,CAAC,CACF,GAAI,CAAC,EAAI,GAAI,CACX,IAAM,EAAK,MAAM,EAAI,MAAM,CAAC,WAAa,EAAE,EAAE,CAC7C,MAAU,MAAM,EAAE,OAAS,QAAQ,EAAI,SAAS,EAIpD,MAAM,YACJ,EACA,EAAS,EAC0D,CACnE,IAAM,EAAM,MAAM,EAChB,EAAO,iBAAiB,mBAAmB,EAAW,CAAC,UAAU,EAAO,WAAW,CACpF,CACD,GAAI,CAAC,EAAI,GAAI,MAAU,MAAM,QAAQ,EAAI,OAAO,IAAI,EAAI,aAAa,CACrE,IAAM,EAAQ,MAAM,EAAI,MAAM,CAGxB,EAAM,EAAK,SAAS,UAAY,EAAE,CAClC,EAAW,EAAwB,EAAI,CACvC,EACJ,OAAO,EAAK,SAAS,MAAS,UAAY,EAAK,QAAQ,KAAK,MAAM,CAC9D,EAAK,QAAQ,KAAK,MAAM,CACxB,IAAA,GACN,MAAO,CAAE,WAAU,QAAS,EAAI,QAAU,GAAI,OAAM,CAGtD,MAAM,cAAc,EAAsD,CACxE,IAAM,EAAgC,CAAE,QAAS,UAAW,CACtD,EAAM,GAAS,SAAS,MAAM,CAChC,IAAK,EAAK,QAAU,EAAI,aAAa,EACzC,IAAM,EAAM,MAAM,EAAS,EAAO,gBAAgB,CAAE,CAClD,OAAQ,OACR,KAAM,KAAK,UAAU,EAAK,CAC3B,CAAC,CACF,GAAI,CAAC,EAAI,GAAI,MAAU,MAAM,QAAQ,EAAI,SAAS,CAElD,OADc,MAAM,EAAI,MAAM,EAClB,QAId,MAAM,iBAAiB,EAAiD,CACtE,IAAM,EAAM,MAAM,EAChB,EAAO,iBAAiB,mBAAmB,EAAW,CAAC,mBAAmB,CAC3E,CACD,GAAI,CAAC,EAAI,GAAI,OAEb,IAAM,GADQ,MAAM,EAAI,MAAM,EACf,SAAS,KACxB,OAAO,OAAO,GAAM,UAAY,EAAE,MAAM,CAAG,EAAE,MAAM,CAAG,IAAA,GAGxD,UAAU,EAA0B,CAClC,IAAM,EAAU,UAAU,mBAAmB,EAAW,GACpD,SAAS,OAAS,GAAS,QAAQ,aAAa,KAAM,GAAI,EAAQ,CAGxE,sBAAsC,CACpC,IAAM,EAAO,SAAS,KAAK,MAAM,EAAE,CAC7B,EAAI,EAAK,MAAM,iBAAiB,EAAI,EAAK,MAAM,eAAe,CAC9D,EAAM,EAAI,mBAAmB,EAAE,GAAG,CAAG,KAC3C,OAAO,GAAO,IAAQ,MAAQ,EAAM,OCnHxC,eAAsB,EAAW,EAA2C,CAC1E,IAAM,EAAS,IAAI,gBACf,GAAQ,MAAQ,IAAS,IAC3B,EAAO,IAAI,OAAQ,EAAK,CAE1B,IAAM,EAAK,EAAO,UAAU,CAE5B,OADY,MAAM,EAAwB,EAAO,oBAAoB,EAAK,IAAI,IAAO,KAAK,CAAC,EAChF,QAGb,eAAsB,IAA4C,CAEhE,OADY,MAAM,EAAwB,EAAO,oBAAoB,CAAC,EAC3D,kBCjCb,SAAS,IAAyB,CAChC,OAAO,EACL,4FACA,6BACA,EACA,mBACD,CAIH,SAAS,EAAuB,EAAoB,CAClD,MAAO,iBAAiB,KAAK,EAAE,QAAQ,MAAO,KAAK,CAAC,CAGtD,SAAS,GAAQ,EAA0C,CAKzD,MAJI,CAAC,GAAS,EAAM,cAAgB,GAAW,GAC3C,EAAM,aAAe,KACrB,EAAM,cAAgB,IAAY,GACtC,EAAI,EAAuB,EAAM,YAAY,CAFP,GAexC,SAAgB,EAA4B,CAC1C,OACA,eACA,sBACA,YACA,MACQ,CACR,IAAM,GAAA,EAAA,EAAA,QAAkB,CAClB,CAAC,EAAc,IAAA,EAAA,EAAA,UAA2C,KAAK,CAC/D,CAAC,EAAW,IAAA,EAAA,EAAA,UAAmD,KAAK,CACpE,CAAC,EAAa,IAAA,EAAA,EAAA,UAA2B,GAAM,CAC/C,CAAC,EAAW,IAAA,EAAA,EAAA,UAAwC,KAAK,CACzD,CAAC,EAAY,IAAA,EAAA,EAAA,UAA0B,GAAG,CAE1C,GAAA,EAAA,EAAA,aAA8B,KAAO,IAAqB,CAC9D,EAAe,GAAK,CACpB,EAAa,KAAK,CAClB,GAAI,CAEF,EADgB,MAAM,EAAW,EAAQ,CACpB,OACd,EAAG,CAEV,EADY,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CACrC,CACjB,EAAa,KAAK,QACV,CACR,EAAe,GAAM,GAEtB,EAAE,CAAC,EAEN,EAAA,EAAA,eAAgB,CACd,GAAI,CAAC,EAAM,OACX,IAAI,EAAY,GAShB,OARM,SAAY,CAChB,GAAI,CACF,IAAM,EAAI,MAAM,IAAe,CAC1B,GAAW,EAAgB,EAAE,SAAS,MACrC,CACD,GAAW,EAAgB,KAAK,KAErC,KACS,CACX,EAAY,KAEb,CAAC,EAAK,CAAC,EAEV,EAAA,EAAA,eAAgB,CACd,GAAI,CAAC,EAAM,OACX,IAAM,EAAU,GAAqB,MAAM,EAAI,GAC/C,EAAc,EAAQ,CACtB,IAAI,EAAY,GA2BhB,OA1BM,SAAY,CAChB,EAAe,GAAK,CACpB,EAAa,KAAK,CAClB,GAAI,CACF,GAAI,EACF,GAAI,CACF,IAAM,EAAU,MAAM,EAAW,EAAQ,CACpC,GAAW,EAAa,EAAQ,MAC/B,CACN,IAAM,EAAU,MAAM,GAAY,CAC7B,GAAW,EAAa,EAAQ,KAElC,CACL,IAAM,EAAU,MAAM,GAAY,CAC7B,GAAW,EAAa,EAAQ,QAEhC,EAAG,CACV,IAAM,EAAM,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CACjD,IACH,EAAa,EAAI,CACjB,EAAa,KAAK,SAEZ,CACH,GAAW,EAAe,GAAM,KAErC,KACS,CACX,EAAY,KAEb,CAAC,EAAM,EAAoB,CAAC,CAE/B,IAAM,EAAY,GAAuB,CAClC,EAAM,aACN,EAAgB,EAAM,aAAa,EAGpC,MAAa,CACjB,GAAI,CAAC,EAAW,OAChB,GAAM,CAAE,aAAY,eAAgB,EACpC,GAAI,IAAe,KAAM,CAClB,EAAgB,EAAW,CAChC,OAEE,IAAgB,KAAO,IAAgB,IACvC,EAAuB,EAAY,EAChC,EAAgB,IAAA,GAAU,EAI7B,EACJ,GAAW,cAAgB,GAAK,EAAG,aAAgB,GAAW,aAAe,GAEzE,EACJ,EAAQ,GAAc,EAAW,cAAgB,IAAM,CAAC,GAAe,CAAC,EAEpE,EAAc,SAAY,CAC1B,MAAC,GAAa,EAAU,cAAgB,IAC5C,GAAI,CACF,MAAM,EAAU,EAAU,YAAY,CACtC,EAAa,GAAM,MACb,IAKJ,EAAgB,SAAY,CAChC,IAAM,EAAI,EAAW,MAAM,CACtB,KACL,GAAI,CACF,MAAM,EAAU,EAAE,CAClB,EAAa,GAAM,MACb,IAKJ,EAAqB,GAAe,EAE1C,OACE,EAAA,EAAA,KAAC,EAAD,CAAmB,OAAoB,yBACrC,EAAA,EAAA,MAAC,EAAD,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,EAAD,CAAgB,UAAU,oDAAsD,CAAA,EAChF,EAAA,EAAA,MAAC,EAAD,CACE,UAAW,EACT,mMACA,mBACD,CACD,gBAAkB,GAAM,EAAE,gBAAgB,UAL5C,EAOE,EAAA,EAAA,KAAC,EAAD,CAAc,UAAU,2CAAmC,EAAG,eAA8B,CAAA,EAC5F,EAAA,EAAA,KAAC,EAAD,CAAoB,UAAU,sDAC3B,EAAG,qBACe,CAAA,CACpB,GACC,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,sCAA8B,EAAG,eAAe,QAAQ,eAAgB,EAAa,CAAK,CAAA,CACrG,MAEJ,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mDAAf,EACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,mCAAf,EACE,EAAA,EAAA,MAAC,EAAD,CACE,KAAK,SACL,QAAQ,YACR,UAAU,qCACV,SAAU,GAAe,CAAC,GAAQ,EAAU,EAAI,EAAQ,EACxD,YAAe,GAAM,CACrB,MAAO,EAAG,kBANZ,EAQE,EAAA,EAAA,KAAC,EAAD,CAAW,UAAU,SAAS,cAAA,GAAc,CAAA,CAC3C,EAAG,SACG,IACT,EAAA,EAAA,KAAC,MAAD,CACE,UAAU,4HACV,MAAO,WAEN,GAAe,CAAC,EAAY,EAAG,cAAgB,GAAsB,IAClE,CAAA,CACF,IAEN,EAAA,EAAA,MAAC,MAAD,CACE,UAAW,EACT,iIACA,EAAY,eACb,CACD,KAAK,UACL,aAAY,EAAG,wBANjB,CAQG,GACC,EAAA,EAAA,KAAC,MAAD,CACE,UAAU,yFACV,cAAA,aAEA,EAAA,EAAA,KAAC,EAAD,CAAS,UAAU,oCAAsC,CAAA,CACrD,CAAA,CACJ,KACH,GAAe,CAAC,GACf,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,8EAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAS,UAAU,sBAAsB,cAAA,GAAc,CAAA,CACtD,EAAG,cACA,GACJ,KACH,GACC,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,uDAA+C,EAAG,gBAAoB,CAAA,CACjF,KACH,CAAC,GAAe,GAAa,CAAC,GAAa,EAAU,QAAQ,SAAW,GACvE,EAAA,EAAA,KAAC,IAAD,CAAG,UAAU,uDAA+C,EAAG,kBAAsB,CAAA,CACnF,KACH,GAAW,QAAQ,IAAK,IACvB,EAAA,EAAA,MAAC,SAAD,CAEE,KAAK,SACL,KAAK,SACL,SAAU,CAAC,EAAE,YACb,UAAW,EACT,0EACA,EAAE,YACE,gDACA,0CACL,CACD,YAAe,EAAS,EAAE,UAX5B,EAaE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,gCAAgC,cAAA,GAAc,CAAA,EACrE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,4BAAoB,EAAE,KAAY,CAAA,CAC3C,EAdF,EAAE,aAcA,CACT,CACE,IAEN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uBAAf,EACE,EAAA,EAAA,KAAC,QAAD,CAAO,QAAS,EAAU,UAAU,iCACjC,EAAG,iBACE,CAAA,EACR,EAAA,EAAA,KAAC,QAAD,CACE,GAAI,EACJ,KAAK,OACL,MAAO,EACP,SAAW,GAAM,EAAc,EAAE,OAAO,MAAM,CAC9C,YAAa,EAAG,qBAChB,UAAW,IAAgB,CAC3B,aAAa,MACb,WAAY,GACZ,UAAY,GAAM,CACZ,EAAE,MAAQ,SAAW,EAAW,MAAM,GACxC,EAAE,gBAAgB,CACb,GAAe,GAGxB,CAAA,CACE,GACF,IAEN,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,kGAAf,EACE,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,SAAS,QAAQ,QAAQ,YAAe,EAAa,GAAM,UACrE,EAAG,gBACG,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CACE,KAAK,SACL,QAAQ,YACR,SAAU,CAAC,EAAW,MAAM,CAC5B,YAAe,KAAK,GAAe,UAElC,EAAG,kBACG,CAAA,EACT,EAAA,EAAA,KAAC,EAAD,CAAQ,KAAK,SAAS,SAAU,CAAC,EAAqB,YAAe,KAAK,GAAa,UACpF,EAAG,oBACG,CAAA,CACL,GACS,GACH,CAAA,CAAA,CACJ,CAAA,CCjSlB,IAAM,EAAkB,8BAClB,GAAa,GAEnB,SAAS,IAA2B,CAClC,GAAI,CACF,IAAM,EAAM,aAAa,QAAQ,EAAgB,CACjD,GAAI,CAAC,EAAK,MAAO,EAAE,CACnB,IAAM,EAAS,KAAK,MAAM,EAAI,CAC9B,OAAO,MAAM,QAAQ,EAAO,CACxB,EAAO,OAAQ,GAAmB,OAAO,GAAM,UAAY,EAAE,MAAM,CAAC,OAAS,EAAE,CAC/E,EAAE,MACA,CACN,MAAO,EAAE,EAIb,SAAS,GAAc,EAAoB,CACzC,IAAM,EAAI,EAAK,MAAM,CAChB,KACL,GAAI,CAEF,IAAM,EAAO,CAAC,EAAG,GADJ,IAAgB,CACJ,OAAQ,GAAM,IAAM,EAAE,CAAC,CAAC,MAAM,EAAG,GAAW,CACrE,aAAa,QAAQ,EAAiB,KAAK,UAAU,EAAK,CAAC,MACrD,GAMV,SAAgB,EAAkB,EAAyB,CACzD,IAAM,EAAI,EAAQ,MAAM,CAAC,QAAQ,UAAW,GAAG,CAC/C,GAAI,CAAC,EAAG,OAAO,EACf,IAAM,EAAQ,EAAE,MAAM,QAAQ,CAC9B,OAAO,EAAM,EAAM,OAAS,IAAM,EAWpC,IAAa,IAAA,EAAA,EAAA,MAAsC,SAAwC,CACzF,aACA,WACA,4BACA,cACQ,CAGR,IAAM,EADI,EADO,EAAgB,GAAM,EAAE,SAAS,CACtB,CACf,KAAK,iBAEZ,CAAC,EAAe,IAAA,EAAA,EAAA,UAA6B,GAAG,CAChD,CAAC,EAAS,IAAA,EAAA,EAAA,UAAuB,GAAM,CACvC,CAAC,EAAe,IAAA,EAAA,EAAA,UAA6B,GAAM,CAEnD,GAAA,EAAA,EAAA,aAAsB,SAAY,CACtC,GAAI,CAAC,EAAY,CACf,EAAiB,GAAG,CACpB,OAEF,EAAW,GAAK,CAChB,GAAI,CAEF,GADY,MAAM,EAAW,uBAAuB,EAAW,EAC1C,uBAAuB,MACtC,CACN,EAAiB,GAAG,QACZ,CACR,EAAW,GAAM,GAElB,CAAC,EAAY,EAAW,CAAC,EAE5B,EAAA,EAAA,eAAgB,CACT,GAAS,EACb,CAAC,EAAQ,CAAC,CAEb,IAAM,GAAA,EAAA,EAAA,aACJ,KAAO,IAAiB,CAClB,MAAC,GAAY,MAAM,EAAI,CAAC,EAAK,MAAM,EACvC,GAAI,CACF,MAAM,EAAW,wBAAwB,EAAY,CAAE,iBAAkB,EAAK,MAAM,CAAE,CAAC,CACvF,GAAc,EAAK,MAAM,CAAC,CAC1B,MAAM,GAAS,OACR,EAAG,CACV,IAAM,EAAM,aAAa,MAAQ,EAAE,QAAU,OAAO,EAAE,CAEtD,MADA,OAAO,MAAM,EAAI,CACX,IAGV,CAAC,EAAY,EAAY,EAAQ,CAClC,CAGK,GAAA,EAAA,EAAA,aAAqC,SAAoC,CAC7E,IAAM,EAAM,OAAO,OAAW,IAAc,OAAO,aAAa,MAAM,cAAgB,IAAA,GAEtF,OADI,EAAY,GAAK,CACd,MACN,EAAE,CAAC,CAEA,EACJ,OAAO,OAAW,KAAe,EAAQ,OAAO,aAAa,MAAM,cAE/D,GAAA,EAAA,EAAA,iBAAkD,CAClD,GACI,SAAY,CAChB,IAAM,EAAS,MAAM,GAAwB,CACzC,GAAQ,MAAM,EAAU,EAAO,IACjC,CAEJ,EAAiB,GAAK,EAEvB,CAAC,EAAW,EAAyB,EAAuB,CAAC,CAE1D,GAAA,EAAA,EAAA,aACJ,KAAO,IAAkD,CACvD,IAAM,EAAO,EAAc,MAAM,CAC5B,KACL,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,EAAK,CACzC,OAAO,cACL,IAAI,YAAY,yBAA0B,CACxC,OAAQ,CACN,KAAM,UACN,MAAO,EAAG,OACV,GAAI,GAAM,yBAA2B,CAAE,QAAS,EAAG,uBAAwB,SAAU,KAAM,CAAG,CAAE,SAAU,KAAM,CACjH,CACF,CAAC,CACH,MACK,IAIV,CAAC,EAAe,EAAG,OAAQ,EAAG,uBAAuB,CACtD,CAEK,GAAA,EAAA,EAAA,iBAAgD,CACpD,OAAO,cACL,IAAI,YAAY,yBAA0B,CACxC,OAAQ,CACN,KAAM,OACN,MAAO,EAAG,eACV,QAAS,EAAG,cACZ,SAAU,KACX,CACF,CAAC,CACH,EACA,CAAC,EAAG,cAAe,EAAG,eAAe,CAAC,CAEzC,GAAI,CAAC,EACH,OAAO,KAGT,IAAM,EAAW,EAAc,MAAM,CAC/B,EAAU,EAAQ,EAClB,EAAQ,EAAU,EAAkB,EAAS,CAAG,EAAG,OAEnD,EAAY,EAChB,+GACA,2EACA,EAAY,WACZ,EAAY,eACb,CAEK,EAAgB,IACpB,EAAA,EAAA,MAAC,SAAD,CACE,KAAK,SACL,UAAW,EACT,EACA,oFACA,CAAC,GAAW,gBACb,CACM,QACP,aAAY,EACZ,QAAU,GAAM,CACd,EAAE,iBAAiB,CACnB,GAA6B,WAXjC,EAcE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,kCAAkC,cAAA,GAAc,CAAA,EACvE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0DAAkD,EAAa,CAAA,CACxE,GAGL,EAAgB,IACpB,EAAA,EAAA,MAAC,SAAD,CACE,KAAK,SACL,SAAU,GAAY,GAAW,CAAC,EAClC,UAAW,EACT,EACA,GAAW,2EACV,GAAY,GAAW,CAAC,IAAY,gCACtC,CACM,QACP,aAAY,EACZ,QAAU,GAAM,CACd,EAAE,iBAAiB,CACf,GAAW,CAAC,GAAY,CAAC,GAAc,EAAa,CAAE,yBAA0B,GAAM,CAAC,WAZ/F,EAeE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,kCAAkC,cAAA,GAAc,CAAA,EACvE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0DAAkD,EAAa,CAAA,CACxE,GAGX,GAAI,CAAC,EAQH,OAPK,GAQH,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,oCACZ,EAAa,GAAG,EAAS,IAAI,EAAG,sBAAsB,CACnD,CAAA,EARJ,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,oCACZ,EAAa,GAAG,EAAG,OAAO,IAAI,EAAG,yBAAyB,CACvD,CAAA,CAUZ,IAAM,EAAkB,EAAU,GAAG,EAAS,IAAI,EAAG,eAAiB,EAAG,uBAEzE,OACE,EAAA,EAAA,MAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,KAAC,MAAD,CAAK,UAAU,qCACb,EAAA,EAAA,MAAC,SAAD,CACE,KAAK,SACL,SAAU,GAAY,EACtB,UAAW,EACT,EACA,2EACC,GAAY,IAAY,gCAC1B,CACD,MAAO,EACP,aAAY,EACZ,QAAU,GAAM,CACd,EAAE,iBAAiB,CACf,CAAC,GAAY,CAAC,GAAS,GAA+B,WAZ9D,EAeE,EAAA,EAAA,KAAC,EAAD,CAAa,UAAU,kCAAkC,cAAA,GAAc,CAAA,EACvE,EAAA,EAAA,KAAC,OAAD,CAAM,UAAU,0DAAkD,EAAa,CAAA,CACxE,GACL,CAAA,CAEJ,EAQE,MAPF,EAAA,EAAA,KAAC,EAAD,CACE,KAAM,EACN,aAAc,EACd,oBAAqB,GAAY,IAAA,GACjC,UAAW,EACP,KACJ,CAAA,CAEH,CAAA,CAAA,EAEL"}
1
+ {"version":3,"file":"session-working-directory-control-DKOtWs3-.js","names":["__iconNode","__iconNode"],"sources":["../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/chevron-up.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/folder-input.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/layout-template.js","../../../../../web/src/features/chat/agent-messages.ts","../../../../../web/src/features/chat/session-manager.ts","../../../../../web/src/features/chat/host-fs-api.ts","../../../../../web/src/features/chat/working-directory-picker-modal.tsx","../../../../../web/src/features/chat/session-working-directory-control.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [[\"path\", { d: \"m18 15-6-6-6 6\", key: \"153udz\" }]];\nconst ChevronUp = createLucideIcon(\"chevron-up\", __iconNode);\n\nexport { __iconNode, ChevronUp as default };\n//# sourceMappingURL=chevron-up.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2 9V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-1\",\n key: \"fm4g5t\"\n }\n ],\n [\"path\", { d: \"M2 13h10\", key: \"pgb2dq\" }],\n [\"path\", { d: \"m9 16 3-3-3-3\", key: \"6m91ic\" }]\n];\nconst FolderInput = createLucideIcon(\"folder-input\", __iconNode);\n\nexport { __iconNode, FolderInput as default };\n//# sourceMappingURL=folder-input.js.map\n","/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"rect\", { width: \"18\", height: \"7\", x: \"3\", y: \"3\", rx: \"1\", key: \"f1a2em\" }],\n [\"rect\", { width: \"9\", height: \"7\", x: \"3\", y: \"14\", rx: \"1\", key: \"jqznyg\" }],\n [\"rect\", { width: \"5\", height: \"7\", x: \"16\", y: \"14\", rx: \"1\", key: \"q5h2i8\" }]\n];\nconst LayoutTemplate = createLucideIcon(\"layout-template\", __iconNode);\n\nexport { __iconNode, LayoutTemplate as default };\n//# sourceMappingURL=layout-template.js.map\n","import { inferMimeTypeFromFileName } from '@/features/chat/attachment-utils-core';\nimport type { Message, MessageAttachment, MessageContent, ThinkingContent, ToolUseContent } from '@/features/chat/messages.types';\n\n// =============================================================================\n// Type definitions for safe type narrowing (replaces Record<string, unknown> casts)\n// =============================================================================\n\ninterface WireContentBlock {\n type?: string;\n text?: string;\n name?: string;\n args?: Record<string, unknown>;\n input?: unknown;\n function?: { name?: string; arguments?: string | unknown };\n result?: string;\n source?: { data?: string; media_type?: string };\n /** Pi / gateway user turns: `{ type: 'image', data: base64, mimeType }` (no `source`). */\n data?: string;\n mimeType?: string;\n id?: string;\n}\n\ninterface WireMessage {\n role?: string;\n content?: unknown;\n tool_calls?: Array<{ id: string; function: { name: string; arguments: string } }>;\n toolCalls?: Array<{ id?: string; name: string; args?: Record<string, unknown> }>;\n attachments?: unknown;\n usage?: unknown;\n timestamp?: string | number;\n tool_call_id?: string;\n toolCallId?: string;\n isError?: boolean;\n}\n\n/** Tool-related blocks in session wire format (tool_use / OpenAI / pi toolCall). */\ninterface ToolCallBlock extends WireContentBlock {\n type?: string;\n name?: string;\n args?: Record<string, unknown>;\n /** Session/pi format often uses `arguments` (same role as `args`). */\n arguments?: unknown;\n input?: unknown;\n function?: { name?: string; arguments?: string | unknown };\n result?: string;\n status?: string;\n}\n\nfunction isWireSessionMessage(item: unknown): item is WireMessage {\n return typeof item === 'object' && item !== null && 'role' in item;\n}\n\nfunction isToolCallBlock(item: unknown): item is ToolCallBlock {\n if (!item || typeof item !== 'object') return false;\n const t = (item as Record<string, unknown>).type;\n return t === 'tool_use' || t === 'tool_call' || t === 'toolCall';\n}\n\nfunction extractToolBlockId(block: ToolCallBlock): string {\n if (typeof block.id === 'string' && block.id.length > 0) return block.id;\n return crypto.randomUUID();\n}\n\nfunction parseMaybeJsonString(raw: unknown): unknown {\n if (typeof raw !== 'string') return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return raw;\n }\n}\n\n/** Prefer pi `args`, then session `arguments`, then `input` / OpenAI `function.arguments`. */\nfunction extractToolCallBlockInput(block: ToolCallBlock): unknown {\n const raw = block.args ?? block.arguments ?? block.input ?? block.function?.arguments;\n const parsed = parseMaybeJsonString(raw);\n return parsed !== undefined && parsed !== null ? parsed : {};\n}\n\n// =============================================================================\n// Type guards for wire format blocks\n// =============================================================================\n\nfunction isWireContentBlock(item: unknown): item is WireContentBlock {\n return typeof item === 'object' && item !== null;\n}\n\n/** Plain text for search / previews over wire-format or UI message content. */\nexport function messageWireSearchText(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n const parts: string[] = [];\n for (const item of content) {\n if (!isWireContentBlock(item)) continue;\n const t = item.type;\n if (t === 'text' && typeof item.text === 'string') {\n parts.push(item.text);\n } else if (t === 'thinking') {\n const th =\n typeof (item as WireContentBlock & { thinking?: string }).thinking === 'string'\n ? (item as { thinking: string }).thinking\n : typeof item.text === 'string'\n ? item.text\n : '';\n if (th) parts.push(th);\n } else if (t === 'tool_use' || t === 'toolCall' || t === 'tool_call') {\n parts.push(String(item.name ?? 'tool'));\n }\n }\n return parts.join(' ');\n}\n\n/**\n * Maps pi-agent-core (or similar) message payloads into the web UI message model.\n */\nexport function normalizeAgentMessages(raw: readonly unknown[]): Message[] {\n return sessionWireToUiMessages(raw);\n}\n\n/**\n * Merge consecutive assistant bubbles into one (same as a single live streaming turn).\n * Persisted sessions often store one wire `assistant` row per thinking/tool fragment; without this,\n * the chat shows repeated \"execution\" lines after refresh.\n */\nfunction thinkingBlockComparableText(b: MessageContent): string {\n if (b.type !== 'thinking') {\n return '';\n }\n const x = b as ThinkingContent & { thinking?: string };\n return typeof x.text === 'string' ? x.text : typeof x.thinking === 'string' ? x.thinking : '';\n}\n\n/**\n * Merge two assistant content arrays when consecutive wire rows represent one turn.\n * - tool_use with the same id: keep the later block (e.g. completed tool after a fragment).\n * - adjacent thinking blocks with identical text: drop duplicate (streaming + persist overlap).\n */\nfunction mergeAssistantContentFragments(left: MessageContent[], right: MessageContent[]): MessageContent[] {\n const out: MessageContent[] = left.map((b) => ({ ...b }));\n const toolIndexById = new Map<string, number>();\n for (let i = 0; i < out.length; i++) {\n const b = out[i];\n if (b.type === 'tool_use') {\n toolIndexById.set(b.id, i);\n }\n }\n\n for (const b of right) {\n if (b.type === 'tool_use' && toolIndexById.has(b.id)) {\n const idx = toolIndexById.get(b.id)!;\n out[idx] = { ...b };\n continue;\n }\n if (b.type === 'thinking' && out.length > 0) {\n const last = out[out.length - 1];\n if (last.type === 'thinking' && thinkingBlockComparableText(last) === thinkingBlockComparableText(b)) {\n continue;\n }\n }\n if (b.type === 'tool_use') {\n toolIndexById.set(b.id, out.length);\n }\n out.push({ ...b });\n }\n return out;\n}\n\nexport function mergeConsecutiveAssistantMessages(messages: Message[]): Message[] {\n if (messages.length < 2) return messages;\n const out: Message[] = [];\n for (const m of messages) {\n if (m.role !== 'assistant') {\n out.push(m);\n continue;\n }\n const prev = out[out.length - 1];\n if (prev?.role === 'assistant') {\n prev.content = mergeAssistantContentFragments(prev.content, m.content);\n if (m.timestamp != null) prev.timestamp = m.timestamp;\n if (m.usage) prev.usage = m.usage;\n if (m.attachments?.length) {\n prev.attachments = dedupeAttachments([...(prev.attachments ?? []), ...m.attachments]);\n }\n } else {\n out.push({\n ...m,\n content: [...m.content],\n });\n }\n }\n return out;\n}\n\n/**\n * Convert session/API wire format (including toolResult rows) into chat UI messages.\n */\nexport function sessionWireToUiMessages(raw: readonly unknown[]): Message[] {\n const out: Message[] = [];\n\n for (const item of raw) {\n if (!isWireSessionMessage(item)) continue;\n const m = item;\n const role = String(m.role ?? '');\n\n if (role === 'system') {\n continue;\n }\n\n if (role === 'toolResult' || role === 'tool') {\n applyToolResultToLastAssistant(out, m);\n continue;\n }\n\n if (role === 'user' || role === 'user-with-attachments') {\n out.push(buildUserMessage(m));\n continue;\n }\n\n if (role === 'assistant') {\n out.push(buildAssistantMessage(m));\n continue;\n }\n }\n\n return mergeConsecutiveAssistantMessages(out);\n}\n\nfunction normalizeWireAttachments(raw: unknown): Message['attachments'] {\n if (!Array.isArray(raw)) return undefined;\n return raw.map((item) => normalizeOneAttachment(item));\n}\n\nfunction normalizeOneAttachment(item: unknown): MessageAttachment {\n if (!item || typeof item !== 'object') {\n return { name: 'file', mimeType: 'application/octet-stream' };\n }\n const a = item as Record<string, unknown>;\n const data = typeof a.data === 'string' ? a.data : undefined;\n const content =\n typeof a.content === 'string' && a.content.length > 0 ? a.content : data;\n const name = typeof a.name === 'string' && a.name.length > 0 ? a.name : 'file';\n let mimeType = typeof a.mimeType === 'string' && a.mimeType.length > 0 ? a.mimeType : '';\n if (!mimeType && typeof a.type === 'string' && a.type.includes('/')) {\n mimeType = a.type;\n }\n if (!mimeType) {\n mimeType = 'application/octet-stream';\n }\n const baseMime = mimeType.split(';')[0]?.trim().toLowerCase() ?? '';\n if (baseMime === 'application/octet-stream' || baseMime === '') {\n const inferred = inferMimeTypeFromFileName(name);\n if (inferred) {\n mimeType = inferred;\n }\n }\n const preview =\n typeof a.preview === 'string' && a.preview.length > 0\n ? a.preview\n : mimeType.startsWith('image/') && content\n ? content\n : undefined;\n\n return {\n id: typeof a.id === 'string' ? a.id : undefined,\n name,\n mimeType,\n type: typeof a.type === 'string' ? a.type : undefined,\n size: typeof a.size === 'number' ? a.size : undefined,\n content,\n data: data ?? content,\n preview,\n extractedText: typeof a.extractedText === 'string' ? a.extractedText : undefined,\n workspaceRelativePath:\n typeof a.workspaceRelativePath === 'string' && a.workspaceRelativePath.length > 0\n ? a.workspaceRelativePath\n : undefined,\n };\n}\n\n/**\n * Session stores the server-expanded skill body (see SkillManager.buildSkillBlock).\n * Collapse back to wire form for UI: `/skill:name` and optional trailing args from `**Arguments**:`.\n */\nexport function collapseExpandedSkillBlockForDisplay(text: string): string {\n if (typeof text !== 'string' || !text.includes('## Skill:')) {\n return text;\n }\n const nameMatch = text.match(/## Skill:\\s*([^\\s\\r\\n]+)/);\n if (!nameMatch) {\n return text;\n }\n const name = nameMatch[1] ?? '';\n if (!name) {\n return text;\n }\n const argMatches = [...text.matchAll(/\\*\\*Arguments\\*\\*:\\s*([^\\r\\n]+)/g)];\n const args =\n argMatches.length > 0 ? (argMatches[argMatches.length - 1]?.[1] ?? '').trim() : '';\n return args ? `/skill:${name} ${args}` : `/skill:${name}`;\n}\n\n/** Remove persisted inbound machine lines from bubble text (attachments show separately). */\nexport function stripInboundFileMachineText(text: string): string {\n if (!text.includes('xopc-path:')) return text;\n let out = text;\n // Multiline (canonical persist format)\n out = out.replace(\n /\\s*\\[File:[^\\]]+\\]\\s*\\r?\\nxopc-path:rel:[^\\r\\n]+\\r?\\n\\s*xopc-path:abs:[^\\r\\n]+/g,\n '',\n );\n // Single line (e.g. markdown collapsed whitespace)\n out = out.replace(/\\s*\\[File:[^\\]]+\\]\\s+xopc-path:rel:\\S+\\s+xopc-path:abs:\\S+/g, '');\n out = out.replace(/\\s*\\[File:[^\\]]+\\]\\s*xopc-path:rel:\\S+\\s*xopc-path:abs:\\S+/g, '');\n return out.replace(/\\n{3,}/g, '\\n\\n').trim();\n}\n\nfunction parseFileLineMeta(fileMeta: string): { name: string; mimeType: string; size: number } {\n const nameMatch = fileMeta.match(/^([^(]+?)\\s*\\(/);\n const name = nameMatch ? nameMatch[1].trim() : 'file';\n const mimeMatch = fileMeta.match(/\\(\\s*([^,]+)\\s*,\\s*(\\d+)\\s*bytes\\s*\\)/i);\n const mimeType = mimeMatch ? mimeMatch[1].trim() : 'application/octet-stream';\n const size = mimeMatch ? parseInt(mimeMatch[2], 10) : 0;\n return { name, mimeType, size };\n}\n\nfunction extractAttachmentsFromUserContent(raw: unknown): Message['attachments'] | undefined {\n const chunks: string[] = [];\n if (typeof raw === 'string') {\n chunks.push(raw);\n } else if (Array.isArray(raw)) {\n for (const item of raw) {\n if (item && typeof item === 'object' && (item as { type?: string }).type === 'text') {\n const t = (item as { text?: string }).text;\n if (typeof t === 'string') chunks.push(t);\n }\n }\n }\n const text = chunks.join('\\n');\n if (!text.includes('xopc-path:rel:')) return undefined;\n\n const out: NonNullable<Message['attachments']> = [];\n const seen = new Set<string>();\n\n // Single line: rel is \\S+ so it stops before \" xopc-path:abs:\" (fixes greedy [^\\n]+ bug)\n const reSingle = /\\[File: ([^\\]]+)\\]\\s*xopc-path:rel:(\\S+)\\s*xopc-path:abs:\\S+/g;\n let m: RegExpExecArray | null;\n while ((m = reSingle.exec(text)) !== null) {\n const rel = m[2].trim();\n if (seen.has(rel)) continue;\n seen.add(rel);\n const { name, mimeType, size } = parseFileLineMeta(m[1]);\n out.push({\n name,\n mimeType,\n size,\n type: 'document',\n workspaceRelativePath: rel,\n });\n }\n\n const reMulti =\n /\\[File: ([^\\]]+)\\]\\s*\\r?\\nxopc-path:rel:([^\\r\\n]+)\\r?\\n\\s*xopc-path:abs:[^\\r\\n]+/g;\n while ((m = reMulti.exec(text)) !== null) {\n const rel = m[2].trim();\n if (seen.has(rel)) continue;\n seen.add(rel);\n const { name, mimeType, size } = parseFileLineMeta(m[1]);\n out.push({\n name,\n mimeType,\n size,\n type: 'document',\n workspaceRelativePath: rel,\n });\n }\n\n return out.length ? out : undefined;\n}\n\nfunction applyStripToUserContent(\n role: Message['role'],\n blocks: MessageContent[],\n): MessageContent[] {\n if (role !== 'user' && role !== 'user-with-attachments') return blocks;\n const mapped = blocks.map((b) => {\n if (b.type === 'text' && typeof b.text === 'string') {\n const stripped = stripInboundFileMachineText(b.text);\n return { ...b, text: collapseExpandedSkillBlockForDisplay(stripped) };\n }\n return b;\n });\n return mapped.filter((b) => {\n if (b.type === 'text' && (!b.text || !b.text.trim())) return false;\n return true;\n });\n}\n\n/** Deduplicate attachments that refer to the same workspace file (wire + parsed content often disagree on `name`). */\nfunction attachmentStableKey(a: MessageAttachment): string {\n const rel = a.workspaceRelativePath?.replace(/\\\\/g, '/').trim();\n if (rel) return `rel:${rel}`;\n if (a.id) return `id:${a.id}`;\n return `name:${a.name ?? 'file'}|${a.mimeType ?? ''}`;\n}\n\nfunction dedupeAttachments(list: Message['attachments'] | undefined): Message['attachments'] | undefined {\n if (!list?.length) return undefined;\n const out: NonNullable<Message['attachments']> = [];\n const seen = new Set<string>();\n for (const a of list) {\n const k = attachmentStableKey(a);\n if (seen.has(k)) continue;\n seen.add(k);\n out.push(a);\n }\n return out.length ? out : undefined;\n}\n\nfunction mergeUserAttachments(\n wire: Message['attachments'] | undefined,\n fromContent: Message['attachments'] | undefined,\n): Message['attachments'] | undefined {\n return dedupeAttachments([...(wire ?? []), ...(fromContent ?? [])]);\n}\n\nfunction buildUserMessage(m: WireMessage): Message {\n const roleRaw = String(m.role ?? 'user');\n const role: Message['role'] =\n roleRaw === 'user' || roleRaw === 'user-with-attachments'\n ? (roleRaw as Message['role'])\n : 'assistant';\n\n const fromContent = extractAttachmentsFromUserContent(m.content);\n\n return {\n role,\n content: applyStripToUserContent(role, normalizeContentBlocks(m.content)),\n attachments: mergeUserAttachments(normalizeWireAttachments(m.attachments), fromContent),\n timestamp: typeof m.timestamp === 'number' ? m.timestamp : parseTs(m.timestamp),\n usage: m.usage as Message['usage'],\n };\n}\n\nfunction buildAssistantMessage(m: WireMessage): Message {\n const content = mergeAssistantContent(m);\n return {\n role: 'assistant',\n content,\n attachments: dedupeAttachments(normalizeWireAttachments(m.attachments)),\n timestamp: typeof m.timestamp === 'number' ? m.timestamp : parseTs(m.timestamp),\n usage: m.usage as Message['usage'],\n };\n}\n\nfunction parseTs(raw: unknown): number {\n if (typeof raw === 'string') {\n const t = Date.parse(raw);\n return Number.isNaN(t) ? Date.now() : t;\n }\n return Date.now();\n}\n\nfunction mergeAssistantContent(m: WireMessage): MessageContent[] {\n const blocks = normalizeContentBlocks(m.content);\n\n const tc = m.tool_calls;\n if (Array.isArray(tc)) {\n for (const call of tc) {\n if (!call?.id || blocks.some((b) => b.type === 'tool_use' && b.id === call.id)) {\n continue;\n }\n let input: unknown = call.function?.arguments;\n if (typeof input === 'string') {\n try {\n input = JSON.parse(input);\n } catch {\n /* keep string */\n }\n }\n blocks.push({\n type: 'tool_use',\n id: call.id,\n name: call.function?.name || 'tool',\n input,\n status: 'running',\n });\n }\n }\n\n const piTcs = m.toolCalls;\n if (Array.isArray(piTcs)) {\n for (const call of piTcs) {\n const id = call.id ?? crypto.randomUUID();\n if (blocks.some((b) => b.type === 'tool_use' && b.id === id)) {\n continue;\n }\n blocks.push({\n type: 'tool_use',\n id,\n name: call.name || 'tool',\n input: call.args,\n status: 'running',\n });\n }\n }\n\n return blocks;\n}\n\nfunction applyToolResultToLastAssistant(out: Message[], m: WireMessage): void {\n const lastAssistant = findLastAssistant(out);\n if (!lastAssistant) return;\n\n const id = String(m.tool_call_id ?? m.toolCallId ?? '');\n const text = extractToolResultText(m.content);\n const isError = Boolean(m.isError);\n\n const block = id\n ? lastAssistant.content.find(\n (b): b is ToolUseContent => b.type === 'tool_use' && b.id === id,\n )\n : undefined;\n\n if (block) {\n block.status = isError ? 'error' : 'done';\n block.result = text;\n return;\n }\n\n const running = lastAssistant.content.filter(\n (b): b is ToolUseContent => b.type === 'tool_use' && b.status === 'running',\n );\n if (running.length === 1) {\n running[0].status = isError ? 'error' : 'done';\n running[0].result = text;\n }\n}\n\nfunction findLastAssistant(messages: Message[]): Message | null {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === 'assistant') {\n return messages[i];\n }\n }\n return null;\n}\n\nfunction extractToolResultText(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n return content\n .filter((c): c is WireContentBlock => isWireContentBlock(c) && c.type === 'text')\n .map((c) => String(c.text ?? ''))\n .join('\\n');\n }\n return String(content ?? '');\n}\n\n/** Map session/API image blocks to UI `ImageContent` (`source.data` is a usable `img` src). */\nfunction wireImageBlockToContent(item: WireContentBlock): MessageContent | null {\n const fromSource = item.source?.data;\n if (typeof fromSource === 'string' && fromSource.length > 0) {\n return { type: 'image', source: { data: fromSource } };\n }\n const raw = item.data;\n if (typeof raw !== 'string' || raw.length === 0) {\n return null;\n }\n const trimmed = raw.trim();\n if (trimmed.startsWith('data:')) {\n return { type: 'image', source: { data: trimmed } };\n }\n const mime =\n typeof item.mimeType === 'string' && item.mimeType.includes('/')\n ? item.mimeType\n : 'image/png';\n const compact = trimmed.replace(/\\s/g, '');\n return { type: 'image', source: { data: `data:${mime};base64,${compact}` } };\n}\n\nfunction normalizeContentBlocks(raw: unknown): MessageContent[] {\n if (raw == null) return [];\n if (typeof raw === 'string') {\n return raw.trim() ? [{ type: 'text', text: raw }] : [];\n }\n if (!Array.isArray(raw)) {\n return [{ type: 'text', text: String(raw) }];\n }\n\n const out: MessageContent[] = [];\n for (const item of raw) {\n if (!isWireContentBlock(item)) continue;\n \n const t = item.type;\n if (t === 'text' && typeof item.text === 'string') {\n out.push({ type: 'text', text: item.text });\n } else if (t === 'thinking') {\n const th =\n typeof (item as WireContentBlock & { thinking?: string }).thinking === 'string'\n ? (item as { thinking: string }).thinking\n : typeof item.text === 'string'\n ? item.text\n : '';\n out.push({ type: 'thinking', text: th, streaming: false });\n } else if (t === 'image') {\n const img = wireImageBlockToContent(item);\n if (img) {\n out.push(img);\n }\n } else if (t === 'tool_use' || t === 'tool_call') {\n if (!isToolCallBlock(item)) continue;\n const id = extractToolBlockId(item);\n const name = String(item.name ?? item.function?.name ?? 'tool');\n const input = item.input ?? item.function?.arguments;\n out.push({\n type: 'tool_use',\n id,\n name,\n input,\n status: 'done',\n result: typeof item.result === 'string' ? item.result : undefined,\n });\n } else if (t === 'toolCall') {\n if (!isToolCallBlock(item)) continue;\n const id = extractToolBlockId(item);\n const name = String(item.name ?? item.function?.name ?? 'tool');\n out.push({\n type: 'tool_use',\n id,\n name,\n input: extractToolCallBlockInput(item),\n status: 'done',\n result: typeof item.result === 'string' ? item.result : undefined,\n });\n }\n }\n return out;\n}\n","import type { Message } from '@/features/chat/messages.types';\nimport type { SessionInfo } from '@/features/chat/chat.types';\nimport { sessionWireToUiMessages } from '@/features/chat/agent-messages';\nimport { listSessions } from '@/features/sessions/session-api';\nimport { apiFetch } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\n/** Web UI chat sessions use segment `webchat` (same as `ui`). */\nexport function isWebUiSessionKey(key: string): boolean {\n return key.startsWith('gateway:') || key.includes(':gateway:') || key.includes(':webchat:');\n}\n\ntype SessionAgentConfig = {\n thinkingLevel: string;\n model: string;\n reasoningLevel: string;\n effectiveWorkspacePath: string;\n workingDirectoryLocked: boolean;\n};\n\nconst _agentConfigInflight = new Map<string, Promise<SessionAgentConfig>>();\n\n/** Session list + history via REST; auth from `apiFetch` (gateway token store). */\nexport class SessionManager {\n /** Same first page as sidebar (`limit=20&offset=0`) so `listSessions` in-flight dedupe applies. */\n async loadSessions(): Promise<SessionInfo[]> {\n const data = await listSessions({ limit: 20, offset: 0 });\n return data.items\n .filter((s) => isWebUiSessionKey(s.key))\n .map((s) => ({\n key: s.key,\n name: s.name,\n updatedAt: s.updatedAt,\n messageCount: s.messageCount,\n }))\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());\n }\n\n async loadSessionAgentConfig(sessionKey: string): Promise<SessionAgentConfig> {\n const existing = _agentConfigInflight.get(sessionKey);\n if (existing) return existing;\n\n const pending = (async () => {\n const res = await apiFetch(\n apiUrl(`/api/sessions/${encodeURIComponent(sessionKey)}/agent-config`),\n );\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as {\n payload?: {\n thinkingLevel?: string;\n model?: string;\n reasoningLevel?: string;\n effectiveWorkspacePath?: string;\n workingDirectoryLocked?: boolean;\n };\n };\n const thinkingLevel = data.payload?.thinkingLevel ?? 'medium';\n const model = typeof data.payload?.model === 'string' ? data.payload.model : '';\n const reasoningLevel = data.payload?.reasoningLevel ?? 'off';\n const effectiveWorkspacePath =\n typeof data.payload?.effectiveWorkspacePath === 'string'\n ? data.payload.effectiveWorkspacePath\n : '';\n const workingDirectoryLocked = Boolean(data.payload?.workingDirectoryLocked);\n return {\n thinkingLevel,\n model,\n reasoningLevel,\n effectiveWorkspacePath,\n workingDirectoryLocked,\n };\n })().finally(() => {\n _agentConfigInflight.delete(sessionKey);\n });\n\n _agentConfigInflight.set(sessionKey, pending);\n return pending;\n }\n\n async patchSessionAgentConfig(\n sessionKey: string,\n patch: { thinkingLevel?: string; model?: string | null; reasoningLevel?: string; workingDirectory?: string },\n ): Promise<void> {\n const res = await apiFetch(apiUrl(`/api/sessions/${encodeURIComponent(sessionKey)}/agent-config`), {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(patch),\n });\n if (!res.ok) {\n const j = (await res.json().catch(() => ({}))) as { error?: string };\n throw new Error(j.error ?? `HTTP ${res.status}`);\n }\n }\n\n async loadSession(\n sessionKey: string,\n offset = 0,\n ): Promise<{ messages: Message[]; hasMore: boolean; name?: string }> {\n const res = await apiFetch(\n apiUrl(`/api/sessions/${encodeURIComponent(sessionKey)}?offset=${offset}&limit=50`),\n );\n if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);\n const data = (await res.json()) as {\n session?: { messages?: unknown[]; name?: string };\n };\n const raw = data.session?.messages || [];\n const messages = sessionWireToUiMessages(raw);\n const name =\n typeof data.session?.name === 'string' && data.session.name.trim()\n ? data.session.name.trim()\n : undefined;\n return { messages, hasMore: raw.length >= 50, name };\n }\n\n async createSession(options?: { agentId?: string }): Promise<SessionInfo> {\n const body: Record<string, unknown> = { channel: 'webchat' };\n const raw = options?.agentId?.trim();\n if (raw) body.agentId = raw.toLowerCase();\n const res = await apiFetch(apiUrl('/api/sessions'), {\n method: 'POST',\n body: JSON.stringify(body),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const data = (await res.json()) as { session: SessionInfo };\n return data.session;\n }\n\n /** Lightweight name read after auto-title (matches `ui` SessionManager). */\n async fetchSessionName(sessionKey: string): Promise<string | undefined> {\n const res = await apiFetch(\n apiUrl(`/api/sessions/${encodeURIComponent(sessionKey)}?offset=0&limit=1`),\n );\n if (!res.ok) return undefined;\n const data = (await res.json()) as { session?: { name?: string } };\n const n = data.session?.name;\n return typeof n === 'string' && n.trim() ? n.trim() : undefined;\n }\n\n updateUrl(sessionKey: string): void {\n const newHash = `#/chat/${encodeURIComponent(sessionKey)}`;\n if (location.hash !== newHash) history.replaceState(null, '', newHash);\n }\n\n parseSessionFromHash(): string | null {\n const hash = location.hash.slice(1);\n const m = hash.match(/^\\/chat\\/(.+)$/) || hash.match(/^chat\\/(.+)$/);\n const key = m ? decodeURIComponent(m[1]) : null;\n return key && key !== 'new' ? key : null;\n }\n}\n","import { fetchJson } from '@/lib/fetch';\nimport { apiUrl } from '@/lib/url';\n\nexport type HostFsEntry = {\n name: string;\n absolutePath: string;\n isDirectory: boolean;\n};\n\nexport type HostFsListPayload = {\n currentPath: string;\n parentPath: string | null;\n entries: HostFsEntry[];\n};\n\nexport type HostFsMetaPayload = {\n hostname: string;\n platform: string;\n pathSeparator: string;\n};\n\ninterface ListResponse {\n ok: boolean;\n payload: HostFsListPayload;\n}\n\ninterface MetaResponse {\n ok: boolean;\n payload: HostFsMetaPayload;\n}\n\n/** List one directory level on the gateway host. Omit `path` for OS root (POSIX `/` or Windows drives). */\nexport async function listHostFs(path?: string): Promise<HostFsListPayload> {\n const params = new URLSearchParams();\n if (path != null && path !== '') {\n params.set('path', path);\n }\n const qs = params.toString();\n const res = await fetchJson<ListResponse>(apiUrl(`/api/host/fs/list${qs ? `?${qs}` : ''}`));\n return res.payload;\n}\n\nexport async function getHostFsMeta(): Promise<HostFsMetaPayload> {\n const res = await fetchJson<MetaResponse>(apiUrl('/api/host/fs/meta'));\n return res.payload;\n}\n","import * as Dialog from '@radix-ui/react-dialog';\nimport { ChevronUp, FolderInput, Loader2 } from 'lucide-react';\nimport { useCallback, useEffect, useId, useState } from 'react';\n\nimport { Button } from '@/components/ui/button';\nimport { getHostFsMeta, listHostFs, type HostFsEntry, type HostFsListPayload } from '@/features/chat/host-fs-api';\nimport { cn } from '@/lib/cn';\nimport { settingsInputFocusClass } from '@/lib/form-field-width';\nimport { interaction } from '@/lib/interaction';\nimport type { MessageBundle } from '@/i18n/messages';\n\nfunction inputClassName(): string {\n return cn(\n 'w-full rounded-lg border border-edge bg-surface-panel px-3 py-2 font-mono text-sm text-fg',\n 'placeholder:text-fg-subtle',\n settingsInputFocusClass,\n 'dark:border-edge',\n );\n}\n\n/** Windows drive root like `C:\\` — parent is null but \"Up\" goes to drive list. */\nfunction isWindowsDriveRootPath(p: string): boolean {\n return /^[A-Za-z]:\\\\?$/.test(p.replace(/\\\\$/, '\\\\'));\n}\n\nfunction canGoUp(state: HostFsListPayload | null): boolean {\n if (!state || state.currentPath === '') return false;\n if (state.parentPath !== null) return true;\n if (state.currentPath === '/') return false;\n if (isWindowsDriveRootPath(state.currentPath)) return true;\n return false;\n}\n\ntype Props = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n /** When set, open this directory on first load (falls back to root on error). */\n initialAbsolutePath?: string;\n onConfirm: (absolutePath: string) => void | Promise<void>;\n wd: MessageBundle['chat']['workingDirectory'];\n};\n\nexport function WorkingDirectoryPickerModal({\n open,\n onOpenChange,\n initialAbsolutePath,\n onConfirm,\n wd,\n}: Props) {\n const manualId = useId();\n const [metaHostname, setMetaHostname] = useState<string | null>(null);\n const [listState, setListState] = useState<HostFsListPayload | null>(null);\n const [listLoading, setListLoading] = useState(false);\n const [listError, setListError] = useState<string | null>(null);\n const [manualPath, setManualPath] = useState('');\n\n const refreshFromPath = useCallback(async (pathArg?: string) => {\n setListLoading(true);\n setListError(null);\n try {\n const payload = await listHostFs(pathArg);\n setListState(payload);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n setListError(msg);\n setListState(null);\n } finally {\n setListLoading(false);\n }\n }, []);\n\n useEffect(() => {\n if (!open) return;\n let cancelled = false;\n void (async () => {\n try {\n const m = await getHostFsMeta();\n if (!cancelled) setMetaHostname(m.hostname);\n } catch {\n if (!cancelled) setMetaHostname(null);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n const initial = initialAbsolutePath?.trim() ?? '';\n setManualPath(initial);\n let cancelled = false;\n void (async () => {\n setListLoading(true);\n setListError(null);\n try {\n if (initial) {\n try {\n const payload = await listHostFs(initial);\n if (!cancelled) setListState(payload);\n } catch {\n const payload = await listHostFs();\n if (!cancelled) setListState(payload);\n }\n } else {\n const payload = await listHostFs();\n if (!cancelled) setListState(payload);\n }\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n if (!cancelled) {\n setListError(msg);\n setListState(null);\n }\n } finally {\n if (!cancelled) setListLoading(false);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [open, initialAbsolutePath]);\n\n const enterDir = (entry: HostFsEntry) => {\n if (!entry.isDirectory) return;\n void refreshFromPath(entry.absolutePath);\n };\n\n const goUp = () => {\n if (!listState) return;\n const { parentPath, currentPath } = listState;\n if (parentPath !== null) {\n void refreshFromPath(parentPath);\n return;\n }\n if (currentPath === '/' || currentPath === '') return;\n if (isWindowsDriveRootPath(currentPath)) {\n void refreshFromPath(undefined);\n }\n };\n\n const currentDisplayPath =\n listState?.currentPath === '' ? wd.pickerDrives : (listState?.currentPath ?? '');\n\n const canUseCurrentFolder =\n Boolean(listState) && listState!.currentPath !== '' && !listLoading && !listError;\n\n const onUseFolder = async () => {\n if (!listState || listState.currentPath === '') return;\n try {\n await onConfirm(listState.currentPath);\n onOpenChange(false);\n } catch {\n /* PATCH failed; stay open */\n }\n };\n\n const onApplyManual = async () => {\n const t = manualPath.trim();\n if (!t) return;\n try {\n await onConfirm(t);\n onOpenChange(false);\n } catch {\n /* PATCH failed; stay open */\n }\n };\n\n const showLoadingOverlay = listLoading && listState;\n\n return (\n <Dialog.Root open={open} onOpenChange={onOpenChange}>\n <Dialog.Portal>\n <Dialog.Overlay className=\"fixed inset-0 z-[80] bg-scrim backdrop-blur-[2px]\" />\n <Dialog.Content\n className={cn(\n 'fixed left-1/2 top-1/2 z-[81] flex max-h-[min(90vh,32rem)] w-[min(100%-2rem,28rem)] -translate-x-1/2 -translate-y-1/2 flex-col rounded-xl border border-edge bg-surface-panel p-4 shadow-popover',\n 'dark:border-edge',\n )}\n onOpenAutoFocus={(e) => e.preventDefault()}\n >\n <Dialog.Title className=\"text-base font-semibold text-fg\">{wd.pathModalTitle}</Dialog.Title>\n <Dialog.Description className=\"mt-1 text-sm leading-relaxed text-fg-muted\">\n {wd.pathModalDescription}\n </Dialog.Description>\n {metaHostname ? (\n <p className=\"mt-2 text-xs text-fg-muted\">{wd.pickerHostHint.replace('{{hostname}}', metaHostname)}</p>\n ) : null}\n\n <div className=\"mt-3 flex min-h-0 flex-1 flex-col gap-2\">\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"secondary\"\n className=\"shrink-0 gap-1 px-2 py-1.5 text-xs\"\n disabled={listLoading || !canGoUp(listState) || Boolean(listError)}\n onClick={() => goUp()}\n title={wd.pickerUp}\n >\n <ChevronUp className=\"size-4\" aria-hidden />\n {wd.pickerUp}\n </Button>\n <div\n className=\"min-w-0 flex-1 truncate rounded-md border border-edge-subtle/80 bg-surface-hover/30 px-2 py-1.5 font-mono text-xs text-fg\"\n title={currentDisplayPath}\n >\n {listLoading && !listState ? wd.pickerLoading : currentDisplayPath || '—'}\n </div>\n </div>\n\n <div\n className={cn(\n 'relative min-h-[12rem] overflow-y-auto rounded-lg border border-edge-subtle/80 bg-surface-hover/20 p-1 dark:border-edge-subtle',\n interaction.focusRingPanel,\n )}\n role=\"listbox\"\n aria-label={wd.pathModalTitle}\n >\n {showLoadingOverlay ? (\n <div\n className=\"absolute inset-0 z-[1] flex items-center justify-center rounded-lg bg-surface-panel/60\"\n aria-hidden\n >\n <Loader2 className=\"size-6 animate-spin text-fg-muted\" />\n </div>\n ) : null}\n {listLoading && !listState ? (\n <div className=\"flex items-center justify-center gap-2 py-12 text-sm text-fg-muted\">\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n {wd.pickerLoading}\n </div>\n ) : null}\n {listError ? (\n <p className=\"px-2 py-8 text-center text-sm text-fg-muted\">{wd.pickerListError}</p>\n ) : null}\n {!listLoading && listState && !listError && listState.entries.length === 0 ? (\n <p className=\"px-2 py-8 text-center text-sm text-fg-muted\">{wd.pickerEmptyFolder}</p>\n ) : null}\n {listState?.entries.map((e) => (\n <button\n key={e.absolutePath}\n type=\"button\"\n role=\"option\"\n disabled={!e.isDirectory}\n className={cn(\n 'flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm',\n e.isDirectory\n ? 'cursor-pointer text-fg hover:bg-surface-hover'\n : 'cursor-default text-fg-muted opacity-60',\n )}\n onClick={() => enterDir(e)}\n >\n <FolderInput className=\"size-4 shrink-0 text-fg-muted\" aria-hidden />\n <span className=\"min-w-0 truncate\">{e.name}</span>\n </button>\n ))}\n </div>\n\n <div className=\"space-y-1.5\">\n <label htmlFor={manualId} className=\"text-xs text-fg-muted\">\n {wd.pickerManualPath}\n </label>\n <input\n id={manualId}\n type=\"text\"\n value={manualPath}\n onChange={(e) => setManualPath(e.target.value)}\n placeholder={wd.pathInputPlaceholder}\n className={inputClassName()}\n autoComplete=\"off\"\n spellCheck={false}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && manualPath.trim()) {\n e.preventDefault();\n void onApplyManual();\n }\n }}\n />\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center justify-end gap-2 border-t border-edge-subtle/60 pt-3\">\n <Button type=\"button\" variant=\"ghost\" onClick={() => onOpenChange(false)}>\n {wd.pathModalCancel}\n </Button>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={!manualPath.trim()}\n onClick={() => void onApplyManual()}\n >\n {wd.pickerApplyManual}\n </Button>\n <Button type=\"button\" disabled={!canUseCurrentFolder} onClick={() => void onUseFolder()}>\n {wd.pickerUseThisFolder}\n </Button>\n </div>\n </Dialog.Content>\n </Dialog.Portal>\n </Dialog.Root>\n );\n}\n","import { FolderInput } from 'lucide-react';\nimport { memo, useCallback, useEffect, useState } from 'react';\n\nimport { SessionManager } from '@/features/chat/session-manager';\nimport { WorkingDirectoryPickerModal } from '@/features/chat/working-directory-picker-modal';\nimport { cn } from '@/lib/cn';\nimport { interaction } from '@/lib/interaction';\nimport { messages } from '@/i18n/messages';\nimport { useLocaleStore } from '@/stores/locale-store';\n\nconst RECENT_DIRS_KEY = 'xopc.recentWorkspaceDirs.v1';\nconst MAX_RECENT = 10;\n\nfunction readRecentDirs(): string[] {\n try {\n const raw = localStorage.getItem(RECENT_DIRS_KEY);\n if (!raw) return [];\n const parsed = JSON.parse(raw) as unknown;\n return Array.isArray(parsed)\n ? parsed.filter((x): x is string => typeof x === 'string' && x.trim().length > 0)\n : [];\n } catch {\n return [];\n }\n}\n\nfunction pushRecentDir(path: string): void {\n const t = path.trim();\n if (!t) return;\n try {\n const prev = readRecentDirs();\n const next = [t, ...prev.filter((p) => p !== t)].slice(0, MAX_RECENT);\n localStorage.setItem(RECENT_DIRS_KEY, JSON.stringify(next));\n } catch {\n /* ignore */\n }\n}\n\n/** Last path segment for display (folder name only). */\nexport function folderDisplayName(absPath: string): string {\n const t = absPath.trim().replace(/[/\\\\]+$/, '');\n if (!t) return absPath;\n const parts = t.split(/[/\\\\]/);\n return parts[parts.length - 1] || t;\n}\n\ntype Props = {\n sessionKey: string | null;\n disabled: boolean;\n /** Only while the conversation has no messages yet (new chat start). */\n canSelectWorkingDirectory: boolean;\n sessionMgr: SessionManager;\n};\n\nexport const SessionWorkingDirectoryControl = memo(function SessionWorkingDirectoryControl({\n sessionKey,\n disabled,\n canSelectWorkingDirectory,\n sessionMgr,\n}: Props) {\n const language = useLocaleStore((s) => s.language);\n const m = messages(language);\n const wd = m.chat.workingDirectory;\n\n const [effectivePath, setEffectivePath] = useState('');\n const [loading, setLoading] = useState(false);\n const [pathModalOpen, setPathModalOpen] = useState(false);\n\n const refresh = useCallback(async () => {\n if (!sessionKey) {\n setEffectivePath('');\n return;\n }\n setLoading(true);\n try {\n const cfg = await sessionMgr.loadSessionAgentConfig(sessionKey);\n setEffectivePath(cfg.effectiveWorkspacePath);\n } catch {\n setEffectivePath('');\n } finally {\n setLoading(false);\n }\n }, [sessionKey, sessionMgr]);\n\n useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const applyPath = useCallback(\n async (path: string) => {\n if (!sessionKey?.trim() || !path.trim()) return;\n try {\n await sessionMgr.patchSessionAgentConfig(sessionKey, { workingDirectory: path.trim() });\n pushRecentDir(path.trim());\n await refresh();\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n window.alert(msg);\n throw e;\n }\n },\n [sessionKey, sessionMgr, refresh],\n );\n\n /** Native folder dialog — only available in Electron desktop build. */\n const openNativeFolderPicker = useCallback(async (): Promise<string | null> => {\n const api = typeof window !== 'undefined' ? window.electronAPI?.file?.openDirectory : undefined;\n if (api) return api();\n return null;\n }, []);\n\n const hasElectronFolderPicker =\n typeof window !== 'undefined' && Boolean(window.electronAPI?.file?.openDirectory);\n\n const onSelectWorkingDirectoryClick = useCallback(() => {\n if (hasElectronFolderPicker) {\n void (async () => {\n const picked = await openNativeFolderPicker();\n if (picked) await applyPath(picked);\n })();\n } else {\n setPathModalOpen(true);\n }\n }, [applyPath, hasElectronFolderPicker, openNativeFolderPicker]);\n\n const copyFullPath = useCallback(\n async (opts?: { includeWorkspaceLockHint?: boolean }) => {\n const full = effectivePath.trim();\n if (!full) return;\n try {\n await navigator.clipboard.writeText(full);\n window.dispatchEvent(\n new CustomEvent('extension-notification', {\n detail: {\n type: 'success' as const,\n title: wd.copied,\n ...(opts?.includeWorkspaceLockHint ? { message: wd.selectionOnlyAtNewChat, duration: 4500 } : { duration: 2500 }),\n },\n }),\n );\n } catch {\n /* ignore */\n }\n },\n [effectivePath, wd.copied, wd.selectionOnlyAtNewChat],\n );\n\n const showWorkspaceLockedReminder = useCallback(() => {\n window.dispatchEvent(\n new CustomEvent('extension-notification', {\n detail: {\n type: 'info' as const,\n title: wd.lockedTapTitle,\n message: wd.lockedTapBody,\n duration: 6500,\n },\n }),\n );\n }, [wd.lockedTapBody, wd.lockedTapTitle]);\n\n if (!sessionKey) {\n return null;\n }\n\n const fullPath = effectivePath.trim();\n const hasPath = Boolean(fullPath);\n const label = hasPath ? folderDisplayName(fullPath) : wd.notSet;\n\n const chipClass = cn(\n 'inline-flex min-h-8 max-w-[min(12rem,40vw)] min-w-0 shrink-0 items-center gap-1 rounded-lg px-2 py-1 text-xs',\n 'border border-edge-subtle/80 bg-surface-hover/40 dark:border-edge-subtle',\n interaction.transition,\n interaction.focusRingPanel,\n );\n\n const readOnlyChip = (title: string) => (\n <button\n type=\"button\"\n className={cn(\n chipClass,\n 'cursor-pointer text-left hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50',\n !hasPath && 'text-fg-muted',\n )}\n title={title}\n aria-label={title}\n onClick={(e) => {\n e.stopPropagation();\n showWorkspaceLockedReminder();\n }}\n >\n <FolderInput className=\"size-3.5 shrink-0 text-fg-muted\" aria-hidden />\n <span className=\"min-w-0 truncate text-left font-medium text-fg\">{label}</span>\n </button>\n );\n\n const copyPathChip = (title: string) => (\n <button\n type=\"button\"\n disabled={disabled || loading || !hasPath}\n className={cn(\n chipClass,\n hasPath && 'cursor-pointer hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50',\n (disabled || loading || !hasPath) && 'cursor-not-allowed opacity-60',\n )}\n title={title}\n aria-label={title}\n onClick={(e) => {\n e.stopPropagation();\n if (hasPath && !disabled && !loading) void copyFullPath({ includeWorkspaceLockHint: true });\n }}\n >\n <FolderInput className=\"size-3.5 shrink-0 text-fg-muted\" aria-hidden />\n <span className=\"min-w-0 truncate text-left font-medium text-fg\">{label}</span>\n </button>\n );\n\n if (!canSelectWorkingDirectory) {\n if (!hasPath) {\n return (\n <div className=\"inline-flex items-center\">\n {readOnlyChip(`${wd.notSet}\\n${wd.selectionOnlyAtNewChat}`)}\n </div>\n );\n }\n return (\n <div className=\"inline-flex items-center\">\n {copyPathChip(`${fullPath}\\n${wd.clickToCopyFullPath}`)}\n </div>\n );\n }\n\n const titleSelectable = hasPath ? `${fullPath}\\n${wd.chooseFolder}` : wd.selectWorkingDirectory;\n\n return (\n <>\n <div className=\"inline-flex items-center\">\n <button\n type=\"button\"\n disabled={disabled || loading}\n className={cn(\n chipClass,\n 'cursor-pointer hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50',\n (disabled || loading) && 'cursor-not-allowed opacity-60',\n )}\n title={titleSelectable}\n aria-label={titleSelectable}\n onClick={(e) => {\n e.stopPropagation();\n if (!disabled && !loading) onSelectWorkingDirectoryClick();\n }}\n >\n <FolderInput className=\"size-3.5 shrink-0 text-fg-muted\" aria-hidden />\n <span className=\"min-w-0 truncate text-left font-medium text-fg\">{label}</span>\n </button>\n </div>\n\n {!hasElectronFolderPicker ? (\n <WorkingDirectoryPickerModal\n open={pathModalOpen}\n onOpenChange={setPathModalOpen}\n initialAbsolutePath={fullPath || undefined}\n onConfirm={applyPath}\n wd={wd}\n />\n ) : null}\n </>\n );\n});\n"],"x_google_ignoreList":[0,1,2],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{l as r,o as i,s as a}from"./url-CtSqjF9J.js";import{d as o}from"./chunk-QFMPRPBF-DOYp8d2p.js";import{n as s,t as c}from"./cn-DPF56z7S.js";import{a as l,c as u,i as ee,l as d,n as f,o as p,r as te,s as ne,t as m}from"./pin-DlJMazAN.js";import{n as re,t as h}from"./zap-C4SJDlFg.js";import{t as g}from"./message-square-Bj5SJ39I.js";import{t as ie}from"./search-DoMetBR0.js";import{t as ae}from"./trash-2-D9v5oysq.js";import{a as _,i as oe,n as v,o as y,s as b,t as x}from"./dist-Db1dcLr9.js";import{t as S}from"./button-CRQ9eQGX.js";import{n as se,r as ce}from"./segmented-styles-DEG4J-_P.js";import{a as C,i as le,l as ue,n as de,o as fe,r as pe,s as me,t as he,u as ge}from"./session-api-n-4O5d9U.js";import{n as _e,t as w}from"./interaction-C4gx-xSs.js";var T=e(t(),1),E=n();function D(e){let t=new Date(e),n=Math.floor((new Date().getTime()-t.getTime())/(1e3*60*60*24));return n===0?t.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`}):n===1?`Yesterday`:n<7?t.toLocaleDateString([],{weekday:`short`}):t.toLocaleDateString([],{month:`short`,day:`numeric`})}function O(e){return e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function ve({session:e,variant:t,labels:n,onOpen:r,onAction:i}){let a=e.name?.trim()||n.unnamedSession,o=!!e.name?.trim(),s=e.status===`archived`,l=e.status===`pinned`;return(0,E.jsxs)(`div`,{role:`button`,tabIndex:0,className:c(`group flex min-w-0 w-full max-w-full cursor-pointer flex-col rounded-xl bg-surface-base text-left transition-colors duration-150 ease-out`,`hover:bg-surface-hover active:scale-[0.99]`,`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-surface-panel`,t===`list`&&`sm:flex-row sm:items-center sm:gap-4`),onClick:r,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),r())},children:[(0,E.jsxs)(`div`,{className:c(`flex min-w-0 items-start justify-between gap-2 bg-surface-hover/35 px-3 py-2 dark:bg-surface-hover/25`,t===`list`&&`sm:py-3`),children:[(0,E.jsx)(`div`,{className:`flex min-w-0 items-center gap-2`,children:(0,E.jsx)(`span`,{className:`truncate text-[11px] font-medium uppercase tracking-wide text-fg-subtle`,children:e.sourceChannel})}),(0,E.jsxs)(`div`,{className:`flex shrink-0 items-center gap-1.5 text-xs text-fg-muted`,children:[l?(0,E.jsx)(m,{className:`size-3.5 text-accent-fg`,strokeWidth:1.75,"aria-hidden":!0}):null,(0,E.jsx)(`span`,{children:D(e.updatedAt)})]})]}),(0,E.jsxs)(`div`,{className:c(`min-w-0 flex-1 px-3 py-2`,t===`list`&&`sm:py-3`),children:[(0,E.jsx)(`div`,{className:`min-w-0 max-w-full truncate text-sm font-semibold text-fg`,title:a,children:a}),o?(0,E.jsx)(`div`,{className:`mt-0.5 min-w-0 max-w-full truncate font-mono text-[11px] text-fg-subtle`,title:e.key,children:e.key}):null,(0,E.jsxs)(`div`,{className:`mt-2 flex flex-wrap items-center gap-3 text-xs text-fg-muted`,children:[(0,E.jsxs)(`span`,{className:`inline-flex items-center gap-1`,children:[(0,E.jsx)(g,{className:`size-3.5`,strokeWidth:1.75,"aria-hidden":!0}),e.messageCount]}),(0,E.jsxs)(`span`,{className:`inline-flex items-center gap-1`,children:[(0,E.jsx)(h,{className:`size-3.5`,strokeWidth:1.75,"aria-hidden":!0}),O(e.estimatedTokens)]})]}),e.tags.length>0?(0,E.jsxs)(`div`,{className:`mt-2 flex flex-wrap gap-1`,children:[e.tags.slice(0,3).map(e=>(0,E.jsx)(`span`,{className:`max-w-full break-words rounded-md bg-surface-hover px-1.5 py-0.5 text-[11px] text-fg-muted`,children:e},e)),e.tags.length>3?(0,E.jsxs)(`span`,{className:`text-[11px] text-fg-disabled`,children:[`+`,e.tags.length-3]}):null]}):null]}),(0,E.jsxs)(`div`,{className:`flex flex-wrap items-center gap-0.5 border-t border-edge-subtle/80 bg-surface-hover/25 px-2 py-2 dark:border-edge-subtle`,onClick:e=>e.stopPropagation(),onKeyDown:e=>e.stopPropagation(),children:[(0,E.jsx)(`button`,{type:`button`,className:w,title:n.continueChat,"aria-label":n.continueChat,onClick:()=>i(`continue`),children:(0,E.jsx)(g,{className:`size-4`,strokeWidth:1.75})}),s?(0,E.jsx)(`button`,{type:`button`,className:w,title:n.unarchive,"aria-label":n.unarchive,onClick:()=>i(`unarchive`),children:(0,E.jsx)(d,{className:`size-4`,strokeWidth:1.75})}):(0,E.jsx)(`button`,{type:`button`,className:w,title:n.archive,"aria-label":n.archive,onClick:()=>i(`archive`),children:(0,E.jsx)(u,{className:`size-4`,strokeWidth:1.75})}),l?(0,E.jsx)(`button`,{type:`button`,className:w,title:n.unpin,"aria-label":n.unpin,onClick:()=>i(`unpin`),children:(0,E.jsx)(f,{className:`size-4`,strokeWidth:1.75})}):(0,E.jsx)(`button`,{type:`button`,className:w,title:n.pin,"aria-label":n.pin,onClick:()=>i(`pin`),children:(0,E.jsx)(m,{className:`size-4`,strokeWidth:1.75})}),(0,E.jsx)(`button`,{type:`button`,className:w,title:n.export,"aria-label":n.export,onClick:()=>i(`export`),children:(0,E.jsx)(p,{className:`size-4`,strokeWidth:1.75})}),(0,E.jsx)(`button`,{type:`button`,className:c(w,`text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/40`),title:n.delete,"aria-label":n.delete,onClick:()=>i(`delete`),children:(0,E.jsx)(ae,{className:`size-4`,strokeWidth:1.75})})]})]})}function k(e){if(typeof e==`string`)return e.length>2e3?`${e.slice(0,2e3)}…`:e;try{let t=JSON.stringify(e,null,2);return t.length>2e3?`${t.slice(0,2e3)}…`:t}catch{return String(e)}}function ye({open:e,loading:t,session:n,labels:r,onClose:i,onArchive:a,onUnarchive:o,onPin:l,onUnpin:u,onExport:ee,onDelete:d}){let f=n?.status===`archived`,p=n?.status===`pinned`;return(0,E.jsx)(y,{open:e,onOpenChange:e=>!e&&i(),children:(0,E.jsxs)(_,{children:[(0,E.jsx)(oe,{className:`xopc-dialog-overlay fixed inset-0 z-50 bg-scrim`}),(0,E.jsxs)(v,{className:c(`xopc-drawer-right fixed right-0 top-0 z-50 flex h-full w-full max-w-lg flex-col border-l border-edge bg-surface-panel shadow-popover outline-none`,`dark:border-edge`),"aria-describedby":void 0,children:[(0,E.jsxs)(`div`,{className:`flex min-w-0 shrink-0 items-center justify-between gap-2 border-b border-edge px-4 py-3 dark:border-edge`,children:[(0,E.jsx)(b,{className:`min-w-0 flex-1 truncate text-base font-semibold tracking-tight text-fg`,children:n?.name?.trim()||r.unnamedSession}),(0,E.jsx)(x,{asChild:!0,children:(0,E.jsx)(S,{type:`button`,variant:`ghost`,className:`h-9 w-9 shrink-0 p-0`,"aria-label":r.close,children:(0,E.jsx)(s,{className:`size-5`,strokeWidth:1.75})})})]}),(0,E.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-4 py-3`,children:t?(0,E.jsx)(`p`,{className:`text-sm text-fg-muted`,children:r.detailLoading}):n?(0,E.jsxs)(E.Fragment,{children:[(0,E.jsxs)(`dl`,{className:`mb-4 grid gap-2 text-xs text-fg-muted`,children:[(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`dt`,{className:`text-fg-disabled`,children:`Key`}),(0,E.jsx)(`dd`,{className:`mt-0.5 break-all font-mono text-fg`,children:n.key})]}),(0,E.jsx)(`div`,{className:`flex flex-wrap gap-4`,children:(0,E.jsxs)(`span`,{children:[n.messageCount,` msgs · `,n.estimatedTokens,` tok`]})})]}),(0,E.jsx)(`h3`,{className:`mb-2 text-xs font-medium uppercase tracking-wide text-fg-subtle`,children:r.detailMessages}),(0,E.jsx)(`ul`,{className:`space-y-3`,children:n.messages.map((e,t)=>(0,E.jsxs)(`li`,{className:`rounded-lg border border-edge-subtle bg-surface-hover/50 p-2 dark:border-edge`,children:[(0,E.jsx)(`div`,{className:`mb-1 text-[10px] font-medium uppercase text-fg-subtle`,children:e.role}),(0,E.jsx)(`pre`,{className:`max-h-40 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed text-fg-muted`,children:k(e.content)})]},`${e.timestamp??t}-${t}`))})]}):null}),(0,E.jsx)(`div`,{className:`shrink-0 border-t border-edge px-4 py-3 dark:border-edge`,children:(0,E.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:ee,children:r.detailExport}),f?(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:o,children:r.unarchive}):(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:a,children:r.archive}),p?(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:u,children:r.unpin}):(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:l,children:r.pin}),(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/40`,onClick:d,children:r.delete})]})})]})]})})}var A=20;function j(e,t){return e.replace(/\{\{(\w+)\}\}/g,(e,n)=>String(t[n]??``))}var M=new Set([`all`,`active`,`pinned`,`archived`]),N=new Set([`grid`,`list`]);function P(){let e=r(a(e=>e.language)),t=e.sessions,n=!!i(e=>e.token),[s,d]=o(),f=s.get(`q`)??``,p=s.get(`status`),h=s.get(`view`),g=M.has(p)?p:`all`,ae=N.has(h)?h:`grid`,[x,w]=(0,T.useState)(f),[D,O]=(0,T.useState)(f.trim()),[k,P]=(0,T.useState)(g),[F,I]=(0,T.useState)(ae),[L,R]=(0,T.useState)([]),[z,B]=(0,T.useState)(!1),[be,V]=(0,T.useState)(null),[H,xe]=(0,T.useState)(!1),[U,W]=(0,T.useState)(null),[Se,G]=(0,T.useState)(!1),[Ce,K]=(0,T.useState)(!1),[q,J]=(0,T.useState)(null),[we,Y]=(0,T.useState)(!1),[X,Z]=(0,T.useState)(null);(0,T.useEffect)(()=>{let e=setTimeout(()=>O(x.trim()),300);return()=>clearTimeout(e)},[x]),(0,T.useEffect)(()=>{let e=s.get(`q`)??``,t=s.get(`status`),n=s.get(`view`),r=M.has(t)?t:`all`,i=N.has(n)?n:`grid`,a=e.trim();w(t=>t===e?t:e),O(e=>e===a?e:a),P(e=>e===r?e:r),I(e=>e===i?e:i)},[s]),(0,T.useEffect)(()=>{let e=new URLSearchParams(s),t=D.trim();t?e.set(`q`,t):e.delete(`q`),k===`all`?e.delete(`status`):e.set(`status`,k),F===`grid`?e.delete(`view`):e.set(`view`,F),e.toString()!==s.toString()&&d(e,{replace:!0})},[D,s,d,k,F]),(0,T.useEffect)(()=>{if(!n)return;let e=!1;return(async()=>{B(!0),V(null);try{let t=await fe({limit:A,offset:0,...D?{search:D}:{},...k===`all`?{}:{status:k}});if(e)return;R(t.items),xe(t.hasMore)}catch(n){e||V(n instanceof Error?n.message:t.loadError)}finally{e||B(!1)}})(),()=>{e=!0}},[n,D,k,t.loadError]),(0,T.useEffect)(()=>{n&&C().then(W).catch(()=>{})},[n]),(0,T.useEffect)(()=>{if(!n)return;let e=e=>{let t=e.detail;!t?.key||t.name===void 0||(R(e=>e.map(e=>e.key===t.key?{...e,name:t.name}:e)),J(e=>e&&e.key===t.key?{...e,name:t.name}:e))};return window.addEventListener(`session-updated`,e),()=>{window.removeEventListener(`session-updated`,e)}},[n]);let Te=(0,T.useCallback)(async()=>{if(!(!n||z||!H)){B(!0),V(null);try{let e=await fe({limit:A,offset:L.length,...D?{search:D}:{},...k===`all`?{}:{status:k}});R(t=>[...t,...e.items]),xe(e.hasMore)}catch(e){V(e instanceof Error?e.message:t.loadError)}finally{B(!1)}}},[n,z,H,L.length,D,k,t.loadError]),Q=(0,T.useCallback)((e,t)=>{R(n=>n.map(n=>n.key===e?{...n,status:t}:n)),J(n=>n&&n.key===e?{...n,status:t}:n)},[]),Ee=(0,T.useCallback)(async e=>{G(!0),K(!0),J(null);try{J(await le(e))}catch{G(!1)}finally{K(!1)}},[]),De=e=>{Ee(e)},$=async(e,t)=>{if(t===`continue`){window.dispatchEvent(new CustomEvent(`navigate-to-chat`,{detail:{sessionKey:e},bubbles:!0}));return}if(t===`delete`){Z(e),Y(!0);return}try{switch(t){case`archive`:await he(e),Q(e,`archived`);break;case`unarchive`:await ue(e),Q(e,`active`);break;case`pin`:await me(e),Q(e,`pinned`);break;case`unpin`:await ge(e),Q(e,`active`);break;case`export`:{let t=await pe(e),n=new Blob([t],{type:`application/json`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=`session-${e.replace(/[^a-z0-9]/gi,`_`)}.json`,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(r);break}default:break}C().then(W).catch(()=>{})}catch{}},Oe=async e=>{try{await de(e),R(t=>t.filter(t=>t.key!==e)),J(t=>t?.key===e?null:t),q?.key===e&&G(!1),C().then(W).catch(()=>{})}catch{}},ke={continueChat:t.continueChat,archive:t.archive,unarchive:t.unarchive,pin:t.pin,unpin:t.unpin,export:t.export,delete:t.delete,unnamedSession:e.chat.newSession},Ae={close:t.close,detailLoading:t.detailLoading,detailMessages:t.detailMessages,detailExport:t.detailExport,archive:t.archive,unarchive:t.unarchive,pin:t.pin,unpin:t.unpin,delete:t.delete,unnamedSession:e.chat.newSession},je=[{key:`all`,label:t.filterAll,icon:re},{key:`active`,label:t.filterActive,icon:ne},{key:`pinned`,label:t.filterPinned,icon:m},{key:`archived`,label:t.filterArchived,icon:u}];return n?(0,E.jsxs)(`div`,{className:`flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto overflow-x-hidden bg-surface-panel`,children:[(0,E.jsxs)(`div`,{className:`mx-auto flex w-full min-w-0 max-w-2xl flex-col gap-4 px-4 py-6 sm:px-6 lg:max-w-app-main lg:px-8`,children:[(0,E.jsxs)(`header`,{className:`flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between`,children:[(0,E.jsxs)(`h1`,{className:`flex items-center gap-2 text-xl font-semibold tracking-tight text-fg`,children:[(0,E.jsx)(l,{className:`size-5 shrink-0 text-fg-muted`,strokeWidth:1.75,"aria-hidden":!0}),t.title]}),(0,E.jsxs)(`div`,{className:`flex w-full min-w-0 items-center gap-2 rounded-xl bg-surface-base px-3 py-2 transition-colors sm:max-w-md dark:bg-surface-hover/40`,children:[(0,E.jsx)(ie,{className:`size-4 shrink-0 text-fg-disabled`,strokeWidth:1.75,"aria-hidden":!0}),(0,E.jsx)(`input`,{type:`search`,value:x,onChange:e=>w(e.target.value),placeholder:t.searchPlaceholder,className:`min-w-0 flex-1 border-0 bg-transparent text-sm text-fg placeholder:text-fg-disabled focus:outline-none focus:ring-0`})]})]}),(0,E.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:je.map(({key:e,label:t,icon:n})=>(0,E.jsxs)(`button`,{type:`button`,"aria-pressed":k===e,onClick:()=>P(e),className:c(`inline-flex items-center gap-1.5 rounded-xl px-3 py-2 text-sm font-medium`,_e.transition,_e.focusRingPanel,k===e?`bg-accent-soft text-accent-fg`:`bg-surface-base text-fg-muted hover:bg-surface-hover hover:text-fg dark:bg-surface-hover/35`),children:[(0,E.jsx)(n,{className:`size-4`,strokeWidth:1.75,"aria-hidden":!0}),t]},e))}),U?(0,E.jsx)(`div`,{className:`grid grid-cols-2 gap-3 sm:grid-cols-4`,children:[[U.totalSessions,t.totalSessions],[U.activeSessions,t.activeSessions],[U.pinnedSessions,t.pinnedSessions],[U.archivedSessions,t.archivedSessions]].map(([e,t])=>(0,E.jsxs)(`div`,{className:`rounded-xl bg-surface-base px-3 py-3 dark:bg-surface-hover/30`,children:[(0,E.jsx)(`div`,{className:`text-lg font-semibold tabular-nums text-fg`,children:e}),(0,E.jsx)(`div`,{className:`text-xs text-fg-muted`,children:t})]},t))}):null,be?(0,E.jsx)(`div`,{className:`rounded-lg border border-edge bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-edge dark:bg-red-950/40 dark:text-red-300`,children:be}):null,(0,E.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,E.jsx)(`p`,{className:`text-xs text-fg-muted`,children:j(t.sessionCount,{count:L.length})}),(0,E.jsxs)(`div`,{className:ce,role:`group`,"aria-label":t.layoutToggleGroup,children:[(0,E.jsx)(S,{type:`button`,variant:`segmented`,title:t.gridView,"aria-pressed":F===`grid`,onClick:()=>I(`grid`),className:c(se,`size-7 p-0`,F===`grid`&&`bg-surface-panel shadow-sm dark:bg-surface-panel dark:shadow-sm dark:ring-1 dark:ring-edge-strong/40`,F===`grid`&&`text-accent-fg`),children:(0,E.jsx)(ee,{className:`size-3.5`,strokeWidth:1.5})}),(0,E.jsx)(S,{type:`button`,variant:`segmented`,title:t.listView,"aria-pressed":F===`list`,onClick:()=>I(`list`),className:c(se,`size-9 p-0`,F===`list`&&`bg-surface-panel shadow-sm dark:bg-surface-panel dark:shadow-sm dark:ring-1 dark:ring-edge-strong/40`,F===`list`&&`text-accent-fg`),children:(0,E.jsx)(te,{className:`size-3.5`,strokeWidth:1.5})})]})]}),z&&L.length===0?(0,E.jsx)(`div`,{className:`grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3`,children:Array.from({length:6}).map((e,t)=>(0,E.jsx)(`div`,{className:`h-40 animate-pulse rounded-xl bg-surface-hover/60 dark:bg-surface-hover/40`},t))}):L.length===0?(0,E.jsxs)(`div`,{className:`flex flex-col items-center justify-center rounded-2xl bg-surface-base py-16 text-center dark:bg-surface-hover/25`,children:[(0,E.jsx)(l,{className:`mb-3 size-12 text-fg-disabled`,strokeWidth:1.25,"aria-hidden":!0}),(0,E.jsx)(`p`,{className:`text-base font-semibold text-fg`,children:t.noSessions}),(0,E.jsx)(`p`,{className:`mt-1 max-w-sm text-sm text-fg-muted`,children:t.noSessionsDescription}),(0,E.jsx)(S,{variant:`primary`,className:`mt-6`,onClick:()=>{window.dispatchEvent(new CustomEvent(`navigate-to-chat`,{detail:{sessionKey:``},bubbles:!0}))},children:t.startNewChat})]}):(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(`div`,{className:c(`grid min-w-0 gap-3`,F===`grid`?`sm:grid-cols-2 lg:grid-cols-3`:`grid-cols-1`),children:L.map(e=>(0,E.jsx)(ve,{session:e,variant:F,labels:ke,onOpen:()=>De(e.key),onAction:t=>void $(e.key,t)},e.key))}),H?(0,E.jsx)(`div`,{className:`flex justify-center pt-2`,children:(0,E.jsx)(S,{type:`button`,variant:`secondary`,disabled:z,onClick:()=>void Te(),children:t.loadMore})}):null]})]}),(0,E.jsx)(ye,{open:Se,loading:Ce,session:q,labels:Ae,onClose:()=>{G(!1),J(null)},onArchive:()=>q&&void $(q.key,`archive`),onUnarchive:()=>q&&void $(q.key,`unarchive`),onPin:()=>q&&void $(q.key,`pin`),onUnpin:()=>q&&void $(q.key,`unpin`),onExport:()=>q&&void $(q.key,`export`),onDelete:()=>q&&(Z(q.key),Y(!0))}),(0,E.jsx)(y,{open:we,onOpenChange:Y,children:(0,E.jsxs)(_,{children:[(0,E.jsx)(oe,{className:`xopc-dialog-overlay fixed inset-0 z-[60] bg-scrim`}),(0,E.jsxs)(v,{className:`xopc-dialog-content fixed left-1/2 top-1/2 z-[60] w-[min(100%-2rem,24rem)] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-edge bg-surface-panel p-4 shadow-popover dark:border-edge`,children:[(0,E.jsx)(b,{className:`text-base font-semibold text-fg`,children:t.deleteSessionTitle}),(0,E.jsx)(`p`,{className:`mt-2 text-sm text-fg-muted`,children:X?j(t.deleteSessionMessage,{name:L.find(e=>e.key===X)?.name?.trim()||e.chat.newSession}):``}),(0,E.jsxs)(`div`,{className:`mt-4 flex justify-end gap-2`,children:[(0,E.jsx)(S,{type:`button`,variant:`secondary`,onClick:()=>Y(!1),children:t.cancel}),(0,E.jsx)(S,{type:`button`,variant:`primary`,className:`bg-red-600 hover:bg-red-700`,onClick:()=>{X&&Oe(X),Y(!1),Z(null)},children:t.delete})]})]})]})})]}):(0,E.jsx)(`div`,{className:`mx-auto w-full max-w-2xl px-4 py-16 text-center text-sm text-fg-muted sm:px-8 lg:max-w-app-main`,children:t.needToken})}export{P as SessionsPage};
2
- //# sourceMappingURL=sessions-page-rBUfTdm3.js.map
1
+ import{i as e}from"./rolldown-runtime-B1FJdls4.js";import{i as t,t as n}from"./vendor-react-QAsRxa6t.js";import{l as r,o as i,s as a}from"./url-D7yWllI8.js";import{d as o}from"./chunk-QFMPRPBF-DOYp8d2p.js";import{n as s,t as c}from"./cn-DPF56z7S.js";import{a as l,c as u,i as ee,l as d,n as f,o as p,r as te,s as ne,t as m}from"./pin-DlJMazAN.js";import{n as re,t as h}from"./zap-C4SJDlFg.js";import{t as g}from"./message-square-Bj5SJ39I.js";import{t as ie}from"./search-DoMetBR0.js";import{t as ae}from"./trash-2-D9v5oysq.js";import{a as _,i as oe,n as v,o as y,s as b,t as x}from"./dist-Db1dcLr9.js";import{t as S}from"./button-CRQ9eQGX.js";import{n as se,r as ce}from"./segmented-styles-DEG4J-_P.js";import{a as C,i as le,l as ue,n as de,o as fe,r as pe,s as me,t as he,u as ge}from"./session-api-DEhQXWJg.js";import{n as _e,t as w}from"./interaction-C4gx-xSs.js";var T=e(t(),1),E=n();function D(e){let t=new Date(e),n=Math.floor((new Date().getTime()-t.getTime())/(1e3*60*60*24));return n===0?t.toLocaleTimeString([],{hour:`2-digit`,minute:`2-digit`}):n===1?`Yesterday`:n<7?t.toLocaleDateString([],{weekday:`short`}):t.toLocaleDateString([],{month:`short`,day:`numeric`})}function O(e){return e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function ve({session:e,variant:t,labels:n,onOpen:r,onAction:i}){let a=e.name?.trim()||n.unnamedSession,o=!!e.name?.trim(),s=e.status===`archived`,l=e.status===`pinned`;return(0,E.jsxs)(`div`,{role:`button`,tabIndex:0,className:c(`group flex min-w-0 w-full max-w-full cursor-pointer flex-col rounded-xl bg-surface-base text-left transition-colors duration-150 ease-out`,`hover:bg-surface-hover active:scale-[0.99]`,`focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-2 focus-visible:ring-offset-surface-panel`,t===`list`&&`sm:flex-row sm:items-center sm:gap-4`),onClick:r,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),r())},children:[(0,E.jsxs)(`div`,{className:c(`flex min-w-0 items-start justify-between gap-2 bg-surface-hover/35 px-3 py-2 dark:bg-surface-hover/25`,t===`list`&&`sm:py-3`),children:[(0,E.jsx)(`div`,{className:`flex min-w-0 items-center gap-2`,children:(0,E.jsx)(`span`,{className:`truncate text-[11px] font-medium uppercase tracking-wide text-fg-subtle`,children:e.sourceChannel})}),(0,E.jsxs)(`div`,{className:`flex shrink-0 items-center gap-1.5 text-xs text-fg-muted`,children:[l?(0,E.jsx)(m,{className:`size-3.5 text-accent-fg`,strokeWidth:1.75,"aria-hidden":!0}):null,(0,E.jsx)(`span`,{children:D(e.updatedAt)})]})]}),(0,E.jsxs)(`div`,{className:c(`min-w-0 flex-1 px-3 py-2`,t===`list`&&`sm:py-3`),children:[(0,E.jsx)(`div`,{className:`min-w-0 max-w-full truncate text-sm font-semibold text-fg`,title:a,children:a}),o?(0,E.jsx)(`div`,{className:`mt-0.5 min-w-0 max-w-full truncate font-mono text-[11px] text-fg-subtle`,title:e.key,children:e.key}):null,(0,E.jsxs)(`div`,{className:`mt-2 flex flex-wrap items-center gap-3 text-xs text-fg-muted`,children:[(0,E.jsxs)(`span`,{className:`inline-flex items-center gap-1`,children:[(0,E.jsx)(g,{className:`size-3.5`,strokeWidth:1.75,"aria-hidden":!0}),e.messageCount]}),(0,E.jsxs)(`span`,{className:`inline-flex items-center gap-1`,children:[(0,E.jsx)(h,{className:`size-3.5`,strokeWidth:1.75,"aria-hidden":!0}),O(e.estimatedTokens)]})]}),e.tags.length>0?(0,E.jsxs)(`div`,{className:`mt-2 flex flex-wrap gap-1`,children:[e.tags.slice(0,3).map(e=>(0,E.jsx)(`span`,{className:`max-w-full break-words rounded-md bg-surface-hover px-1.5 py-0.5 text-[11px] text-fg-muted`,children:e},e)),e.tags.length>3?(0,E.jsxs)(`span`,{className:`text-[11px] text-fg-disabled`,children:[`+`,e.tags.length-3]}):null]}):null]}),(0,E.jsxs)(`div`,{className:`flex flex-wrap items-center gap-0.5 border-t border-edge-subtle/80 bg-surface-hover/25 px-2 py-2 dark:border-edge-subtle`,onClick:e=>e.stopPropagation(),onKeyDown:e=>e.stopPropagation(),children:[(0,E.jsx)(`button`,{type:`button`,className:w,title:n.continueChat,"aria-label":n.continueChat,onClick:()=>i(`continue`),children:(0,E.jsx)(g,{className:`size-4`,strokeWidth:1.75})}),s?(0,E.jsx)(`button`,{type:`button`,className:w,title:n.unarchive,"aria-label":n.unarchive,onClick:()=>i(`unarchive`),children:(0,E.jsx)(d,{className:`size-4`,strokeWidth:1.75})}):(0,E.jsx)(`button`,{type:`button`,className:w,title:n.archive,"aria-label":n.archive,onClick:()=>i(`archive`),children:(0,E.jsx)(u,{className:`size-4`,strokeWidth:1.75})}),l?(0,E.jsx)(`button`,{type:`button`,className:w,title:n.unpin,"aria-label":n.unpin,onClick:()=>i(`unpin`),children:(0,E.jsx)(f,{className:`size-4`,strokeWidth:1.75})}):(0,E.jsx)(`button`,{type:`button`,className:w,title:n.pin,"aria-label":n.pin,onClick:()=>i(`pin`),children:(0,E.jsx)(m,{className:`size-4`,strokeWidth:1.75})}),(0,E.jsx)(`button`,{type:`button`,className:w,title:n.export,"aria-label":n.export,onClick:()=>i(`export`),children:(0,E.jsx)(p,{className:`size-4`,strokeWidth:1.75})}),(0,E.jsx)(`button`,{type:`button`,className:c(w,`text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/40`),title:n.delete,"aria-label":n.delete,onClick:()=>i(`delete`),children:(0,E.jsx)(ae,{className:`size-4`,strokeWidth:1.75})})]})]})}function k(e){if(typeof e==`string`)return e.length>2e3?`${e.slice(0,2e3)}…`:e;try{let t=JSON.stringify(e,null,2);return t.length>2e3?`${t.slice(0,2e3)}…`:t}catch{return String(e)}}function ye({open:e,loading:t,session:n,labels:r,onClose:i,onArchive:a,onUnarchive:o,onPin:l,onUnpin:u,onExport:ee,onDelete:d}){let f=n?.status===`archived`,p=n?.status===`pinned`;return(0,E.jsx)(y,{open:e,onOpenChange:e=>!e&&i(),children:(0,E.jsxs)(_,{children:[(0,E.jsx)(oe,{className:`xopc-dialog-overlay fixed inset-0 z-50 bg-scrim`}),(0,E.jsxs)(v,{className:c(`xopc-drawer-right fixed right-0 top-0 z-50 flex h-full w-full max-w-lg flex-col border-l border-edge bg-surface-panel shadow-popover outline-none`,`dark:border-edge`),"aria-describedby":void 0,children:[(0,E.jsxs)(`div`,{className:`flex min-w-0 shrink-0 items-center justify-between gap-2 border-b border-edge px-4 py-3 dark:border-edge`,children:[(0,E.jsx)(b,{className:`min-w-0 flex-1 truncate text-base font-semibold tracking-tight text-fg`,children:n?.name?.trim()||r.unnamedSession}),(0,E.jsx)(x,{asChild:!0,children:(0,E.jsx)(S,{type:`button`,variant:`ghost`,className:`h-9 w-9 shrink-0 p-0`,"aria-label":r.close,children:(0,E.jsx)(s,{className:`size-5`,strokeWidth:1.75})})})]}),(0,E.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto px-4 py-3`,children:t?(0,E.jsx)(`p`,{className:`text-sm text-fg-muted`,children:r.detailLoading}):n?(0,E.jsxs)(E.Fragment,{children:[(0,E.jsxs)(`dl`,{className:`mb-4 grid gap-2 text-xs text-fg-muted`,children:[(0,E.jsxs)(`div`,{children:[(0,E.jsx)(`dt`,{className:`text-fg-disabled`,children:`Key`}),(0,E.jsx)(`dd`,{className:`mt-0.5 break-all font-mono text-fg`,children:n.key})]}),(0,E.jsx)(`div`,{className:`flex flex-wrap gap-4`,children:(0,E.jsxs)(`span`,{children:[n.messageCount,` msgs · `,n.estimatedTokens,` tok`]})})]}),(0,E.jsx)(`h3`,{className:`mb-2 text-xs font-medium uppercase tracking-wide text-fg-subtle`,children:r.detailMessages}),(0,E.jsx)(`ul`,{className:`space-y-3`,children:n.messages.map((e,t)=>(0,E.jsxs)(`li`,{className:`rounded-lg border border-edge-subtle bg-surface-hover/50 p-2 dark:border-edge`,children:[(0,E.jsx)(`div`,{className:`mb-1 text-[10px] font-medium uppercase text-fg-subtle`,children:e.role}),(0,E.jsx)(`pre`,{className:`max-h-40 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed text-fg-muted`,children:k(e.content)})]},`${e.timestamp??t}-${t}`))})]}):null}),(0,E.jsx)(`div`,{className:`shrink-0 border-t border-edge px-4 py-3 dark:border-edge`,children:(0,E.jsxs)(`div`,{className:`flex flex-wrap gap-2`,children:[(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:ee,children:r.detailExport}),f?(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:o,children:r.unarchive}):(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:a,children:r.archive}),p?(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:u,children:r.unpin}):(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm`,onClick:l,children:r.pin}),(0,E.jsx)(S,{type:`button`,variant:`secondary`,className:`text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/40`,onClick:d,children:r.delete})]})})]})]})})}var A=20;function j(e,t){return e.replace(/\{\{(\w+)\}\}/g,(e,n)=>String(t[n]??``))}var M=new Set([`all`,`active`,`pinned`,`archived`]),N=new Set([`grid`,`list`]);function P(){let e=r(a(e=>e.language)),t=e.sessions,n=!!i(e=>e.token),[s,d]=o(),f=s.get(`q`)??``,p=s.get(`status`),h=s.get(`view`),g=M.has(p)?p:`all`,ae=N.has(h)?h:`grid`,[x,w]=(0,T.useState)(f),[D,O]=(0,T.useState)(f.trim()),[k,P]=(0,T.useState)(g),[F,I]=(0,T.useState)(ae),[L,R]=(0,T.useState)([]),[z,B]=(0,T.useState)(!1),[be,V]=(0,T.useState)(null),[H,xe]=(0,T.useState)(!1),[U,W]=(0,T.useState)(null),[Se,G]=(0,T.useState)(!1),[Ce,K]=(0,T.useState)(!1),[q,J]=(0,T.useState)(null),[we,Y]=(0,T.useState)(!1),[X,Z]=(0,T.useState)(null);(0,T.useEffect)(()=>{let e=setTimeout(()=>O(x.trim()),300);return()=>clearTimeout(e)},[x]),(0,T.useEffect)(()=>{let e=s.get(`q`)??``,t=s.get(`status`),n=s.get(`view`),r=M.has(t)?t:`all`,i=N.has(n)?n:`grid`,a=e.trim();w(t=>t===e?t:e),O(e=>e===a?e:a),P(e=>e===r?e:r),I(e=>e===i?e:i)},[s]),(0,T.useEffect)(()=>{let e=new URLSearchParams(s),t=D.trim();t?e.set(`q`,t):e.delete(`q`),k===`all`?e.delete(`status`):e.set(`status`,k),F===`grid`?e.delete(`view`):e.set(`view`,F),e.toString()!==s.toString()&&d(e,{replace:!0})},[D,s,d,k,F]),(0,T.useEffect)(()=>{if(!n)return;let e=!1;return(async()=>{B(!0),V(null);try{let t=await fe({limit:A,offset:0,...D?{search:D}:{},...k===`all`?{}:{status:k}});if(e)return;R(t.items),xe(t.hasMore)}catch(n){e||V(n instanceof Error?n.message:t.loadError)}finally{e||B(!1)}})(),()=>{e=!0}},[n,D,k,t.loadError]),(0,T.useEffect)(()=>{n&&C().then(W).catch(()=>{})},[n]),(0,T.useEffect)(()=>{if(!n)return;let e=e=>{let t=e.detail;!t?.key||t.name===void 0||(R(e=>e.map(e=>e.key===t.key?{...e,name:t.name}:e)),J(e=>e&&e.key===t.key?{...e,name:t.name}:e))};return window.addEventListener(`session-updated`,e),()=>{window.removeEventListener(`session-updated`,e)}},[n]);let Te=(0,T.useCallback)(async()=>{if(!(!n||z||!H)){B(!0),V(null);try{let e=await fe({limit:A,offset:L.length,...D?{search:D}:{},...k===`all`?{}:{status:k}});R(t=>[...t,...e.items]),xe(e.hasMore)}catch(e){V(e instanceof Error?e.message:t.loadError)}finally{B(!1)}}},[n,z,H,L.length,D,k,t.loadError]),Q=(0,T.useCallback)((e,t)=>{R(n=>n.map(n=>n.key===e?{...n,status:t}:n)),J(n=>n&&n.key===e?{...n,status:t}:n)},[]),Ee=(0,T.useCallback)(async e=>{G(!0),K(!0),J(null);try{J(await le(e))}catch{G(!1)}finally{K(!1)}},[]),De=e=>{Ee(e)},$=async(e,t)=>{if(t===`continue`){window.dispatchEvent(new CustomEvent(`navigate-to-chat`,{detail:{sessionKey:e},bubbles:!0}));return}if(t===`delete`){Z(e),Y(!0);return}try{switch(t){case`archive`:await he(e),Q(e,`archived`);break;case`unarchive`:await ue(e),Q(e,`active`);break;case`pin`:await me(e),Q(e,`pinned`);break;case`unpin`:await ge(e),Q(e,`active`);break;case`export`:{let t=await pe(e),n=new Blob([t],{type:`application/json`}),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=`session-${e.replace(/[^a-z0-9]/gi,`_`)}.json`,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(r);break}default:break}C().then(W).catch(()=>{})}catch{}},Oe=async e=>{try{await de(e),R(t=>t.filter(t=>t.key!==e)),J(t=>t?.key===e?null:t),q?.key===e&&G(!1),C().then(W).catch(()=>{})}catch{}},ke={continueChat:t.continueChat,archive:t.archive,unarchive:t.unarchive,pin:t.pin,unpin:t.unpin,export:t.export,delete:t.delete,unnamedSession:e.chat.newSession},Ae={close:t.close,detailLoading:t.detailLoading,detailMessages:t.detailMessages,detailExport:t.detailExport,archive:t.archive,unarchive:t.unarchive,pin:t.pin,unpin:t.unpin,delete:t.delete,unnamedSession:e.chat.newSession},je=[{key:`all`,label:t.filterAll,icon:re},{key:`active`,label:t.filterActive,icon:ne},{key:`pinned`,label:t.filterPinned,icon:m},{key:`archived`,label:t.filterArchived,icon:u}];return n?(0,E.jsxs)(`div`,{className:`flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto overflow-x-hidden bg-surface-panel`,children:[(0,E.jsxs)(`div`,{className:`mx-auto flex w-full min-w-0 max-w-2xl flex-col gap-4 px-4 py-6 sm:px-6 lg:max-w-app-main lg:px-8`,children:[(0,E.jsxs)(`header`,{className:`flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between`,children:[(0,E.jsxs)(`h1`,{className:`flex items-center gap-2 text-xl font-semibold tracking-tight text-fg`,children:[(0,E.jsx)(l,{className:`size-5 shrink-0 text-fg-muted`,strokeWidth:1.75,"aria-hidden":!0}),t.title]}),(0,E.jsxs)(`div`,{className:`flex w-full min-w-0 items-center gap-2 rounded-xl bg-surface-base px-3 py-2 transition-colors sm:max-w-md dark:bg-surface-hover/40`,children:[(0,E.jsx)(ie,{className:`size-4 shrink-0 text-fg-disabled`,strokeWidth:1.75,"aria-hidden":!0}),(0,E.jsx)(`input`,{type:`search`,value:x,onChange:e=>w(e.target.value),placeholder:t.searchPlaceholder,className:`min-w-0 flex-1 border-0 bg-transparent text-sm text-fg placeholder:text-fg-disabled focus:outline-none focus:ring-0`})]})]}),(0,E.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:je.map(({key:e,label:t,icon:n})=>(0,E.jsxs)(`button`,{type:`button`,"aria-pressed":k===e,onClick:()=>P(e),className:c(`inline-flex items-center gap-1.5 rounded-xl px-3 py-2 text-sm font-medium`,_e.transition,_e.focusRingPanel,k===e?`bg-accent-soft text-accent-fg`:`bg-surface-base text-fg-muted hover:bg-surface-hover hover:text-fg dark:bg-surface-hover/35`),children:[(0,E.jsx)(n,{className:`size-4`,strokeWidth:1.75,"aria-hidden":!0}),t]},e))}),U?(0,E.jsx)(`div`,{className:`grid grid-cols-2 gap-3 sm:grid-cols-4`,children:[[U.totalSessions,t.totalSessions],[U.activeSessions,t.activeSessions],[U.pinnedSessions,t.pinnedSessions],[U.archivedSessions,t.archivedSessions]].map(([e,t])=>(0,E.jsxs)(`div`,{className:`rounded-xl bg-surface-base px-3 py-3 dark:bg-surface-hover/30`,children:[(0,E.jsx)(`div`,{className:`text-lg font-semibold tabular-nums text-fg`,children:e}),(0,E.jsx)(`div`,{className:`text-xs text-fg-muted`,children:t})]},t))}):null,be?(0,E.jsx)(`div`,{className:`rounded-lg border border-edge bg-red-50 px-3 py-2 text-sm text-red-700 dark:border-edge dark:bg-red-950/40 dark:text-red-300`,children:be}):null,(0,E.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,E.jsx)(`p`,{className:`text-xs text-fg-muted`,children:j(t.sessionCount,{count:L.length})}),(0,E.jsxs)(`div`,{className:ce,role:`group`,"aria-label":t.layoutToggleGroup,children:[(0,E.jsx)(S,{type:`button`,variant:`segmented`,title:t.gridView,"aria-pressed":F===`grid`,onClick:()=>I(`grid`),className:c(se,`size-7 p-0`,F===`grid`&&`bg-surface-panel shadow-sm dark:bg-surface-panel dark:shadow-sm dark:ring-1 dark:ring-edge-strong/40`,F===`grid`&&`text-accent-fg`),children:(0,E.jsx)(ee,{className:`size-3.5`,strokeWidth:1.5})}),(0,E.jsx)(S,{type:`button`,variant:`segmented`,title:t.listView,"aria-pressed":F===`list`,onClick:()=>I(`list`),className:c(se,`size-9 p-0`,F===`list`&&`bg-surface-panel shadow-sm dark:bg-surface-panel dark:shadow-sm dark:ring-1 dark:ring-edge-strong/40`,F===`list`&&`text-accent-fg`),children:(0,E.jsx)(te,{className:`size-3.5`,strokeWidth:1.5})})]})]}),z&&L.length===0?(0,E.jsx)(`div`,{className:`grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3`,children:Array.from({length:6}).map((e,t)=>(0,E.jsx)(`div`,{className:`h-40 animate-pulse rounded-xl bg-surface-hover/60 dark:bg-surface-hover/40`},t))}):L.length===0?(0,E.jsxs)(`div`,{className:`flex flex-col items-center justify-center rounded-2xl bg-surface-base py-16 text-center dark:bg-surface-hover/25`,children:[(0,E.jsx)(l,{className:`mb-3 size-12 text-fg-disabled`,strokeWidth:1.25,"aria-hidden":!0}),(0,E.jsx)(`p`,{className:`text-base font-semibold text-fg`,children:t.noSessions}),(0,E.jsx)(`p`,{className:`mt-1 max-w-sm text-sm text-fg-muted`,children:t.noSessionsDescription}),(0,E.jsx)(S,{variant:`primary`,className:`mt-6`,onClick:()=>{window.dispatchEvent(new CustomEvent(`navigate-to-chat`,{detail:{sessionKey:``},bubbles:!0}))},children:t.startNewChat})]}):(0,E.jsxs)(E.Fragment,{children:[(0,E.jsx)(`div`,{className:c(`grid min-w-0 gap-3`,F===`grid`?`sm:grid-cols-2 lg:grid-cols-3`:`grid-cols-1`),children:L.map(e=>(0,E.jsx)(ve,{session:e,variant:F,labels:ke,onOpen:()=>De(e.key),onAction:t=>void $(e.key,t)},e.key))}),H?(0,E.jsx)(`div`,{className:`flex justify-center pt-2`,children:(0,E.jsx)(S,{type:`button`,variant:`secondary`,disabled:z,onClick:()=>void Te(),children:t.loadMore})}):null]})]}),(0,E.jsx)(ye,{open:Se,loading:Ce,session:q,labels:Ae,onClose:()=>{G(!1),J(null)},onArchive:()=>q&&void $(q.key,`archive`),onUnarchive:()=>q&&void $(q.key,`unarchive`),onPin:()=>q&&void $(q.key,`pin`),onUnpin:()=>q&&void $(q.key,`unpin`),onExport:()=>q&&void $(q.key,`export`),onDelete:()=>q&&(Z(q.key),Y(!0))}),(0,E.jsx)(y,{open:we,onOpenChange:Y,children:(0,E.jsxs)(_,{children:[(0,E.jsx)(oe,{className:`xopc-dialog-overlay fixed inset-0 z-[60] bg-scrim`}),(0,E.jsxs)(v,{className:`xopc-dialog-content fixed left-1/2 top-1/2 z-[60] w-[min(100%-2rem,24rem)] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-edge bg-surface-panel p-4 shadow-popover dark:border-edge`,children:[(0,E.jsx)(b,{className:`text-base font-semibold text-fg`,children:t.deleteSessionTitle}),(0,E.jsx)(`p`,{className:`mt-2 text-sm text-fg-muted`,children:X?j(t.deleteSessionMessage,{name:L.find(e=>e.key===X)?.name?.trim()||e.chat.newSession}):``}),(0,E.jsxs)(`div`,{className:`mt-4 flex justify-end gap-2`,children:[(0,E.jsx)(S,{type:`button`,variant:`secondary`,onClick:()=>Y(!1),children:t.cancel}),(0,E.jsx)(S,{type:`button`,variant:`primary`,className:`bg-red-600 hover:bg-red-700`,onClick:()=>{X&&Oe(X),Y(!1),Z(null)},children:t.delete})]})]})]})})]}):(0,E.jsx)(`div`,{className:`mx-auto w-full max-w-2xl px-4 py-16 text-center text-sm text-fg-muted sm:px-8 lg:max-w-app-main`,children:t.needToken})}export{P as SessionsPage};
2
+ //# sourceMappingURL=sessions-page-BYlWP1ep.js.map