@xopcai/xopc 0.0.6 → 0.0.8

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 (291) 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/api/api.js +1 -1
  4. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  5. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  6. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  7. package/dist/extensions/weixin/src/plugin.js +1 -1
  8. package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js → agents-BSNzJWbQ.js} +2 -2
  9. package/dist/gateway/static/root/assets/{agents-B6s2BvpH.js.map → agents-BSNzJWbQ.js.map} +1 -1
  10. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js → apps-page-BKk9SB4D.js} +2 -2
  11. package/dist/gateway/static/root/assets/{apps-page-BtsZ5ZPx.js.map → apps-page-BKk9SB4D.js.map} +1 -1
  12. package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +1 -0
  13. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js → channels-settings-_J6cQN6G.js} +2 -2
  14. package/dist/gateway/static/root/assets/{channels-settings-BUfWBEVU.js.map → channels-settings-_J6cQN6G.js.map} +1 -1
  15. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js → chat-agents-api-DPb_0O8M.js} +2 -2
  16. package/dist/gateway/static/root/assets/{chat-agents-api-BR30M2YQ.js.map → chat-agents-api-DPb_0O8M.js.map} +1 -1
  17. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js → cron-page-BUJOuuKX.js} +2 -2
  18. package/dist/gateway/static/root/assets/{cron-page-CMTx0Mjz.js.map → cron-page-BUJOuuKX.js.map} +1 -1
  19. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js → cron-utils-Cn0YVg8x.js} +2 -2
  20. package/dist/gateway/static/root/assets/{cron-utils-BJma9IcD.js.map → cron-utils-Cn0YVg8x.js.map} +1 -1
  21. package/dist/gateway/static/root/assets/electron-env-D9bm1FIu.js +2 -0
  22. package/dist/gateway/static/root/assets/electron-env-D9bm1FIu.js.map +1 -0
  23. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js → extension-debug-page-DTz4O5Ua.js} +2 -2
  24. package/dist/gateway/static/root/assets/{extension-debug-page-BCVoNSo6.js.map → extension-debug-page-DTz4O5Ua.js.map} +1 -1
  25. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js → extension-iframe-host-Cs1Kde9o.js} +2 -2
  26. package/dist/gateway/static/root/assets/{extension-iframe-host-PWB-Pw2d.js.map → extension-iframe-host-Cs1Kde9o.js.map} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js → extension-page-G52iX0Bo.js} +2 -2
  28. package/dist/gateway/static/root/assets/{extension-page-D2tTklsD.js.map → extension-page-G52iX0Bo.js.map} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js → extension-provider-CO2jxBA9.js} +2 -2
  30. package/dist/gateway/static/root/assets/{extension-provider-BpHodVRj.js.map → extension-provider-CO2jxBA9.js.map} +1 -1
  31. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js → extension-settings-page-D9Ul8uSt.js} +2 -2
  32. package/dist/gateway/static/root/assets/{extension-settings-page-BEu6Xw1Z.js.map → extension-settings-page-D9Ul8uSt.js.map} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js → gateway-config-swr-Bc8SVD15.js} +2 -2
  34. package/dist/gateway/static/root/assets/{gateway-config-swr-C7ZFPhNj.js.map → gateway-config-swr-Bc8SVD15.js.map} +1 -1
  35. package/dist/gateway/static/root/assets/index-BXUJbteW.js +16 -0
  36. package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +1 -0
  37. package/dist/gateway/static/root/assets/index-CQLMxWSA.css +2 -0
  38. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js → logs-page-5V25JkQY.js} +2 -2
  39. package/dist/gateway/static/root/assets/{logs-page-BpsxYdcL.js.map → logs-page-5V25JkQY.js.map} +1 -1
  40. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js → model-selector-he3aQfme.js} +2 -2
  41. package/dist/gateway/static/root/assets/{model-selector-BiiDq8Pk.js.map → model-selector-he3aQfme.js.map} +1 -1
  42. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js +2 -0
  43. package/dist/gateway/static/root/assets/navigation-DB9S-C6S.js.map +1 -0
  44. package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +2 -0
  45. package/dist/gateway/static/root/assets/{page-header-store-HcRZK5CZ.js.map → page-header-store-DJHD9Ean.js.map} +1 -1
  46. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js → session-api-n-4O5d9U.js} +2 -2
  47. package/dist/gateway/static/root/assets/{session-api-DxNaAkmX.js.map → session-api-n-4O5d9U.js.map} +1 -1
  48. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js → session-working-directory-control-B6dHLvbr.js} +3 -3
  49. package/dist/gateway/static/root/assets/{session-working-directory-control-CDH-Wk4E.js.map → session-working-directory-control-B6dHLvbr.js.map} +1 -1
  50. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js → sessions-page-rBUfTdm3.js} +2 -2
  51. package/dist/gateway/static/root/assets/{sessions-page-5PK75r1n.js.map → sessions-page-rBUfTdm3.js.map} +1 -1
  52. package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js +2 -0
  53. package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +1 -0
  54. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js → skill-api-vxtE8kI6.js} +2 -2
  55. package/dist/gateway/static/root/assets/{skill-api-CxbNlOD_.js.map → skill-api-vxtE8kI6.js.map} +1 -1
  56. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js → skills-page-D36_O2Ub.js} +2 -2
  57. package/dist/gateway/static/root/assets/{skills-page-Dd8ZzYJb.js.map → skills-page-D36_O2Ub.js.map} +1 -1
  58. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js → theme-store-CmiSsYBd.js} +2 -2
  59. package/dist/gateway/static/root/assets/{theme-store-CPTH77BE.js.map → theme-store-CmiSsYBd.js.map} +1 -1
  60. package/dist/gateway/static/root/assets/url-CtSqjF9J.js +2 -0
  61. package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +1 -0
  62. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js → useTranslation-DYORQ7x6.js} +2 -2
  63. package/dist/gateway/static/root/assets/{useTranslation-BEUWOMuh.js.map → useTranslation-DYORQ7x6.js.map} +1 -1
  64. package/dist/gateway/static/root/index.html +16 -16
  65. package/dist/package.js +1 -1
  66. package/dist/src/agent/agent-manager.js +4 -4
  67. package/dist/src/agent/context/workspace-seed.js +2 -2
  68. package/dist/src/agent/image/index.d.ts +0 -1
  69. package/dist/src/agent/image/index.js +1 -2
  70. package/dist/src/agent/ipc/bus.js +1 -1
  71. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  72. package/dist/src/agent/models/manager.js +1 -1
  73. package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
  74. package/dist/src/agent/service.js +3 -3
  75. package/dist/src/agent/skills/hub-hash.js +1 -1
  76. package/dist/src/agent/skills/hub-pull.js +1 -1
  77. package/dist/src/agent/skills/index.js +1 -1
  78. package/dist/src/agent/skills/skill-manager.js +1 -1
  79. package/dist/src/agent/tools/browser/tools.js +2 -2
  80. package/dist/src/agent/tools/browser/tools.js.map +1 -1
  81. package/dist/src/agent/tools/factory.js +1 -1
  82. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  83. package/dist/src/agent/tools/image-tool.js +2 -2
  84. package/dist/src/agent/tools/image-tool.js.map +1 -1
  85. package/dist/src/agent/tools/index.d.ts +1 -1
  86. package/dist/src/agent/tools/index.js +2 -2
  87. package/dist/src/agent/tools/read.d.ts +0 -2
  88. package/dist/src/agent/tools/read.js +1 -3
  89. package/dist/src/agent/tools/read.js.map +1 -1
  90. package/dist/src/agent/tools/send-media.js +1 -1
  91. package/dist/src/auth/sync-provider-auth.js +1 -1
  92. package/dist/src/channels/plugin-types.d.ts +14 -0
  93. package/dist/src/cli/commands/agent.js +1 -1
  94. package/dist/src/cli/commands/doctor/checks/channel-config.d.ts +2 -0
  95. package/dist/src/cli/commands/doctor/checks/channel-config.js +113 -0
  96. package/dist/src/cli/commands/doctor/checks/channel-config.js.map +1 -0
  97. package/dist/src/cli/commands/doctor/checks/channel-plugins.d.ts +2 -0
  98. package/dist/src/cli/commands/doctor/checks/channel-plugins.js +47 -0
  99. package/dist/src/cli/commands/doctor/checks/channel-plugins.js.map +1 -0
  100. package/dist/src/cli/commands/doctor/checks/config-health.d.ts +2 -0
  101. package/dist/src/cli/commands/doctor/checks/config-health.js +82 -0
  102. package/dist/src/cli/commands/doctor/checks/config-health.js.map +1 -0
  103. package/dist/src/cli/commands/doctor/checks/cron-health.d.ts +2 -0
  104. package/dist/src/cli/commands/doctor/checks/cron-health.js +116 -0
  105. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +1 -0
  106. package/dist/src/cli/commands/doctor/checks/gateway-health.d.ts +2 -0
  107. package/dist/src/cli/commands/doctor/checks/gateway-health.js +64 -0
  108. package/dist/src/cli/commands/doctor/checks/gateway-health.js.map +1 -0
  109. package/dist/src/cli/commands/doctor/checks/gateway-service.d.ts +2 -0
  110. package/dist/src/cli/commands/doctor/checks/gateway-service.js +64 -0
  111. package/dist/src/cli/commands/doctor/checks/gateway-service.js.map +1 -0
  112. package/dist/src/cli/commands/doctor/checks/node-version.d.ts +2 -0
  113. package/dist/src/cli/commands/doctor/checks/node-version.js +33 -0
  114. package/dist/src/cli/commands/doctor/checks/node-version.js.map +1 -0
  115. package/dist/src/cli/commands/doctor/checks/provider-auth.d.ts +2 -0
  116. package/dist/src/cli/commands/doctor/checks/provider-auth.js +91 -0
  117. package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -0
  118. package/dist/src/cli/commands/doctor/checks/security-audit.d.ts +2 -0
  119. package/dist/src/cli/commands/doctor/checks/security-audit.js +85 -0
  120. package/dist/src/cli/commands/doctor/checks/security-audit.js.map +1 -0
  121. package/dist/src/cli/commands/doctor/checks/session-integrity.d.ts +2 -0
  122. package/dist/src/cli/commands/doctor/checks/session-integrity.js +118 -0
  123. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -0
  124. package/dist/src/cli/commands/doctor/checks/state-integrity.d.ts +2 -0
  125. package/dist/src/cli/commands/doctor/checks/state-integrity.js +99 -0
  126. package/dist/src/cli/commands/doctor/checks/state-integrity.js.map +1 -0
  127. package/dist/src/cli/commands/doctor/checks/version-check.d.ts +2 -0
  128. package/dist/src/cli/commands/doctor/checks/version-check.js +71 -0
  129. package/dist/src/cli/commands/doctor/checks/version-check.js.map +1 -0
  130. package/dist/src/cli/commands/doctor/checks/workspace-status.d.ts +2 -0
  131. package/dist/src/cli/commands/doctor/checks/workspace-status.js +73 -0
  132. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -0
  133. package/dist/src/cli/commands/doctor/flow.d.ts +9 -0
  134. package/dist/src/cli/commands/doctor/flow.js +51 -0
  135. package/dist/src/cli/commands/doctor/flow.js.map +1 -0
  136. package/dist/src/cli/commands/doctor/format.d.ts +6 -0
  137. package/dist/src/cli/commands/doctor/format.js +61 -0
  138. package/dist/src/cli/commands/doctor/format.js.map +1 -0
  139. package/dist/src/cli/commands/doctor/index.js +44 -0
  140. package/dist/src/cli/commands/doctor/index.js.map +1 -0
  141. package/dist/src/cli/commands/doctor/types.d.ts +20 -0
  142. package/dist/src/cli/commands/doctor/types.js +1 -0
  143. package/dist/src/cli/commands/init.js +2 -2
  144. package/dist/src/cli/index.d.ts +1 -1
  145. package/dist/src/cli/index.js +1 -1
  146. package/dist/src/cli/index.js.map +1 -1
  147. package/dist/src/cli/utils/init-workspace.js +1 -1
  148. package/dist/src/config/index.js +3 -3
  149. package/dist/src/config/loader.js +1 -1
  150. package/dist/src/config/models-json.d.ts +15 -15
  151. package/dist/src/config/models-json.js +1 -1
  152. package/dist/src/config/profile.js +1 -1
  153. package/dist/src/config/schema.d.ts +0 -105
  154. package/dist/src/config/schema.js +3 -40
  155. package/dist/src/config/schema.js.map +1 -1
  156. package/dist/src/cron/executor.js +2 -2
  157. package/dist/src/daemon/launchd.js +2 -2
  158. package/dist/src/daemon/launchd.js.map +1 -1
  159. package/dist/src/daemon/systemd.js +2 -2
  160. package/dist/src/daemon/systemd.js.map +1 -1
  161. package/dist/src/extensions/loader.js +1 -1
  162. package/dist/src/gateway/agents-admin.js +1 -1
  163. package/dist/src/gateway/hono/lib/static-ui.js +1 -1
  164. package/dist/src/gateway/hono/routes/doctor.d.ts +3 -0
  165. package/dist/src/gateway/hono/routes/doctor.js +35 -0
  166. package/dist/src/gateway/hono/routes/doctor.js.map +1 -0
  167. package/dist/src/gateway/hono/routes/index.js +2 -0
  168. package/dist/src/gateway/hono/routes/index.js.map +1 -1
  169. package/dist/src/gateway/hono/routes/workspace.js +2 -2
  170. package/dist/src/gateway/hono/sse.js +2 -2
  171. package/dist/src/gateway/lock.js +1 -1
  172. package/dist/src/gateway/ports.js +98 -3
  173. package/dist/src/gateway/ports.js.map +1 -1
  174. package/dist/src/gateway/service.d.ts +0 -4
  175. package/dist/src/gateway/service.js +4 -23
  176. package/dist/src/gateway/service.js.map +1 -1
  177. package/dist/src/routing/bindings.js +1 -1
  178. package/dist/src/routing/index.d.ts +1 -1
  179. package/dist/src/routing/index.js +2 -2
  180. package/dist/src/routing/index.js.map +1 -1
  181. package/dist/src/routing/resolve-route.js +1 -1
  182. package/dist/src/routing/session-key.d.ts +0 -5
  183. package/dist/src/routing/session-key.js +1 -27
  184. package/dist/src/routing/session-key.js.map +1 -1
  185. package/dist/src/session/session-title.js +1 -1
  186. package/dist/src/session/store.js +2 -6
  187. package/dist/src/session/store.js.map +1 -1
  188. package/dist/src/session/types.d.ts +0 -10
  189. package/dist/src/session/types.js.map +1 -1
  190. package/package.json +1 -2
  191. package/dist/gateway/static/root/assets/attachment-load-6pRlDPZ8.js +0 -1
  192. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js +0 -16
  193. package/dist/gateway/static/root/assets/index-DBZ5eXW5.js.map +0 -1
  194. package/dist/gateway/static/root/assets/index-KsVMH-Jo.css +0 -2
  195. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js +0 -2
  196. package/dist/gateway/static/root/assets/navigation-BpLKd2Ca.js.map +0 -1
  197. package/dist/gateway/static/root/assets/page-header-store-HcRZK5CZ.js +0 -2
  198. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js +0 -2
  199. package/dist/gateway/static/root/assets/preference-select-fields-B4AJBqUY.js.map +0 -1
  200. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js +0 -2
  201. package/dist/gateway/static/root/assets/settings-page-BvSj0JqX.js.map +0 -1
  202. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js +0 -2
  203. package/dist/gateway/static/root/assets/url-QmwQTJ-j.js.map +0 -1
  204. package/dist/src/acp/commands.d.ts +0 -11
  205. package/dist/src/acp/commands.js +0 -17
  206. package/dist/src/acp/commands.js.map +0 -1
  207. package/dist/src/acp/control-plane/identity-reconcile.d.ts +0 -36
  208. package/dist/src/acp/control-plane/identity-reconcile.js +0 -124
  209. package/dist/src/acp/control-plane/identity-reconcile.js.map +0 -1
  210. package/dist/src/acp/control-plane/index.d.ts +0 -10
  211. package/dist/src/acp/control-plane/index.js +0 -6
  212. package/dist/src/acp/control-plane/manager.d.ts +0 -86
  213. package/dist/src/acp/control-plane/manager.js +0 -502
  214. package/dist/src/acp/control-plane/manager.js.map +0 -1
  215. package/dist/src/acp/control-plane/manager.types.d.ts +0 -125
  216. package/dist/src/acp/control-plane/manager.types.js +0 -14
  217. package/dist/src/acp/control-plane/manager.types.js.map +0 -1
  218. package/dist/src/acp/control-plane/manager.utils.d.ts +0 -29
  219. package/dist/src/acp/control-plane/manager.utils.js +0 -46
  220. package/dist/src/acp/control-plane/manager.utils.js.map +0 -1
  221. package/dist/src/acp/control-plane/runtime-cache-manager.d.ts +0 -49
  222. package/dist/src/acp/control-plane/runtime-cache-manager.js +0 -155
  223. package/dist/src/acp/control-plane/runtime-cache-manager.js.map +0 -1
  224. package/dist/src/acp/control-plane/runtime-cache.d.ts +0 -45
  225. package/dist/src/acp/control-plane/runtime-cache.js +0 -58
  226. package/dist/src/acp/control-plane/runtime-cache.js.map +0 -1
  227. package/dist/src/acp/control-plane/runtime-options.d.ts +0 -30
  228. package/dist/src/acp/control-plane/runtime-options.js +0 -92
  229. package/dist/src/acp/control-plane/runtime-options.js.map +0 -1
  230. package/dist/src/acp/control-plane/session-actor-queue.d.ts +0 -22
  231. package/dist/src/acp/control-plane/session-actor-queue.js +0 -70
  232. package/dist/src/acp/control-plane/session-actor-queue.js.map +0 -1
  233. package/dist/src/acp/control-plane/session-lifecycle-manager.d.ts +0 -59
  234. package/dist/src/acp/control-plane/session-lifecycle-manager.js +0 -209
  235. package/dist/src/acp/control-plane/session-lifecycle-manager.js.map +0 -1
  236. package/dist/src/acp/control-plane/session-store.d.ts +0 -39
  237. package/dist/src/acp/control-plane/session-store.js +0 -149
  238. package/dist/src/acp/control-plane/session-store.js.map +0 -1
  239. package/dist/src/acp/control-plane/turn-manager.d.ts +0 -40
  240. package/dist/src/acp/control-plane/turn-manager.js +0 -134
  241. package/dist/src/acp/control-plane/turn-manager.js.map +0 -1
  242. package/dist/src/acp/event-mapper.d.ts +0 -48
  243. package/dist/src/acp/event-mapper.js +0 -94
  244. package/dist/src/acp/event-mapper.js.map +0 -1
  245. package/dist/src/acp/index.d.ts +0 -10
  246. package/dist/src/acp/index.js +0 -5
  247. package/dist/src/acp/meta.d.ts +0 -15
  248. package/dist/src/acp/meta.js +0 -36
  249. package/dist/src/acp/meta.js.map +0 -1
  250. package/dist/src/acp/routing-integration.d.ts +0 -37
  251. package/dist/src/acp/routing-integration.js +0 -58
  252. package/dist/src/acp/routing-integration.js.map +0 -1
  253. package/dist/src/acp/runtime/backends/index.d.ts +0 -4
  254. package/dist/src/acp/runtime/backends/index.js +0 -2
  255. package/dist/src/acp/runtime/backends/local.d.ts +0 -136
  256. package/dist/src/acp/runtime/backends/local.js +0 -603
  257. package/dist/src/acp/runtime/backends/local.js.map +0 -1
  258. package/dist/src/acp/runtime/error-text.d.ts +0 -16
  259. package/dist/src/acp/runtime/error-text.js +0 -40
  260. package/dist/src/acp/runtime/error-text.js.map +0 -1
  261. package/dist/src/acp/runtime/errors.d.ts +0 -31
  262. package/dist/src/acp/runtime/errors.js +0 -47
  263. package/dist/src/acp/runtime/errors.js.map +0 -1
  264. package/dist/src/acp/runtime/index.d.ts +0 -7
  265. package/dist/src/acp/runtime/index.js +0 -4
  266. package/dist/src/acp/runtime/registry.d.ts +0 -35
  267. package/dist/src/acp/runtime/registry.js +0 -85
  268. package/dist/src/acp/runtime/registry.js.map +0 -1
  269. package/dist/src/acp/runtime/session-identity.d.ts +0 -35
  270. package/dist/src/acp/runtime/session-identity.js +0 -134
  271. package/dist/src/acp/runtime/session-identity.js.map +0 -1
  272. package/dist/src/acp/runtime/types.d.ts +0 -214
  273. package/dist/src/acp/secret-file.d.ts +0 -7
  274. package/dist/src/acp/secret-file.js +0 -19
  275. package/dist/src/acp/secret-file.js.map +0 -1
  276. package/dist/src/acp/server.d.ts +0 -48
  277. package/dist/src/acp/server.js +0 -300
  278. package/dist/src/acp/server.js.map +0 -1
  279. package/dist/src/acp/session.d.ts +0 -29
  280. package/dist/src/acp/session.js +0 -30
  281. package/dist/src/acp/session.js.map +0 -1
  282. package/dist/src/acp/types.d.ts +0 -39
  283. package/dist/src/acp/types.js +0 -13
  284. package/dist/src/acp/types.js.map +0 -1
  285. package/dist/src/agent/image/describe-images.d.ts +0 -18
  286. package/dist/src/agent/image/describe-images.js +0 -19
  287. package/dist/src/agent/image/describe-images.js.map +0 -1
  288. package/dist/src/cli/commands/acp.d.ts +0 -4
  289. package/dist/src/cli/commands/acp.js +0 -200
  290. package/dist/src/cli/commands/acp.js.map +0 -1
  291. /package/dist/src/{acp/runtime/types.js → cli/commands/doctor/index.d.ts} +0 -0
@@ -0,0 +1,2 @@
1
+ import{r as e}from"./cn-DPF56z7S.js";var t=e(`copy`,[[`rect`,{width:`14`,height:`14`,x:`8`,y:`8`,rx:`2`,ry:`2`,key:`17jyea`}],[`path`,{d:`M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2`,key:`zix9uf`}]]),n=e(`eye-off`,[[`path`,{d:`M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49`,key:`ct8e1f`}],[`path`,{d:`M14.084 14.158a3 3 0 0 1-4.242-4.242`,key:`151rxh`}],[`path`,{d:`M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143`,key:`13bj9a`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}]]),r=e(`pencil`,[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]),i={settingsAppearance:`appearance`,settingsSystem:`system`,settingsAgentDefaults:`agent-defaults`,settingsAgents:`agents`,settingsProviders:`providers`,settingsModels:`models`,settingsChannels:`channels`,settingsVoice:`voice`,settingsGateway:`gateway`,settingsHeartbeat:`heartbeat`,settingsSearch:`search`};function a(e){return i[e]??null}var o=[{id:`interface`,tabs:[`settingsAppearance`,`settingsSystem`]},{id:`data`,tabs:[`sessions`,`logs`]},{id:`agentAndModels`,tabs:[`settingsProviders`,`settingsModels`,`settingsAgentDefaults`,`settingsSearch`]},{id:`voice`,tabs:[`settingsVoice`]},{id:`gateway`,tabs:[`settingsGateway`,`settingsHeartbeat`]}];o.filter(e=>e.id!==`data`).flatMap(e=>[...e.tabs]),o.flatMap(e=>[...e.tabs]);var s=`https://xopcai.github.io/xopc`;function c(e){return e===`zh`?`${s}/zh/`:`${s}/`}function l(e,t){let n=t.replace(/^\/+/,``).replace(/\.md$/,``);return e===`zh`?`${s}/zh/${n}`:`${s}/${n}`}function u(e){if(e===`chat`)return`/chat`;if(e===`cron`)return`/cron`;if(e===`skills`)return`/skills`;if(e===`channels`||e===`settingsChannels`)return`/channels`;if(e===`agents`||e===`settingsAgents`)return`/agents`;let t=a(e);return t?`/settings/${t}`:e===`sessions`||e===`logs`?`/settings/${e}`:`/${e}`}export{r as a,u as i,l as n,n as o,c as r,t as s,o as t};
2
+ //# sourceMappingURL=navigation-DB9S-C6S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation-DB9S-C6S.js","names":["__iconNode","__iconNode"],"sources":["../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/copy.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/eye-off.js","../../../../../node_modules/.pnpm/lucide-react@1.8.0_react@19.2.5/node_modules/lucide-react/dist/esm/icons/pencil.js","../../../../../web/src/navigation/index.ts"],"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 = [\n [\"rect\", { width: \"14\", height: \"14\", x: \"8\", y: \"8\", rx: \"2\", ry: \"2\", key: \"17jyea\" }],\n [\"path\", { d: \"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\", key: \"zix9uf\" }]\n];\nconst Copy = createLucideIcon(\"copy\", __iconNode);\n\nexport { __iconNode, Copy as default };\n//# sourceMappingURL=copy.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: \"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\",\n key: \"ct8e1f\"\n }\n ],\n [\"path\", { d: \"M14.084 14.158a3 3 0 0 1-4.242-4.242\", key: \"151rxh\" }],\n [\n \"path\",\n {\n d: \"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\",\n key: \"13bj9a\"\n }\n ],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }]\n];\nconst EyeOff = createLucideIcon(\"eye-off\", __iconNode);\n\nexport { __iconNode, EyeOff as default };\n//# sourceMappingURL=eye-off.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: \"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\",\n key: \"1a8usu\"\n }\n ],\n [\"path\", { d: \"m15 5 4 4\", key: \"1mk7zo\" }]\n];\nconst Pencil = createLucideIcon(\"pencil\", __iconNode);\n\nexport { __iconNode, Pencil as default };\n//# sourceMappingURL=pencil.js.map\n","import type { StoredLanguage } from '@/lib/storage';\n\nimport type { SettingsSectionId, Tab } from '@/i18n/messages';\n\nexport type { SettingsSectionId, Tab } from '@/i18n/messages';\n\nexport type ChatRoute =\n | { type: 'recent' }\n | { type: 'session'; sessionKey: string }\n | { type: 'new' };\n\nconst SETTINGS_SECTION_TO_TAB: Record<SettingsSectionId, Tab> = {\n appearance: 'settingsAppearance',\n system: 'settingsSystem',\n agent: 'settingsAgents',\n 'agent-defaults': 'settingsAgentDefaults',\n agents: 'settingsAgents',\n providers: 'settingsProviders',\n models: 'settingsModels',\n channels: 'settingsChannels',\n voice: 'settingsVoice',\n gateway: 'settingsGateway',\n heartbeat: 'settingsHeartbeat',\n search: 'settingsSearch',\n};\n\nconst TAB_TO_SETTINGS_SECTION: Record<\n | 'settingsAppearance'\n | 'settingsSystem'\n | 'settingsAgentDefaults'\n | 'settingsAgents'\n | 'settingsProviders'\n | 'settingsModels'\n | 'settingsChannels'\n | 'settingsVoice'\n | 'settingsGateway'\n | 'settingsHeartbeat'\n | 'settingsSearch',\n SettingsSectionId\n> = {\n settingsAppearance: 'appearance',\n settingsSystem: 'system',\n settingsAgentDefaults: 'agent-defaults',\n settingsAgents: 'agents',\n settingsProviders: 'providers',\n settingsModels: 'models',\n settingsChannels: 'channels',\n settingsVoice: 'voice',\n settingsGateway: 'gateway',\n settingsHeartbeat: 'heartbeat',\n settingsSearch: 'search',\n};\n\nexport function settingsSectionToTab(section: SettingsSectionId): Tab {\n return SETTINGS_SECTION_TO_TAB[section];\n}\n\nexport function tabToSettingsSection(tab: Tab): SettingsSectionId | null {\n return TAB_TO_SETTINGS_SECTION[tab as keyof typeof TAB_TO_SETTINGS_SECTION] ?? null;\n}\n\n/** Group keys for `messages(lang).settingsNavGroups` — left rail sections + sort order. */\nexport type SettingsNavGroupId =\n | 'interface'\n | 'agentAndModels'\n | 'voice'\n | 'gateway'\n | 'data';\n\nexport type SettingsShellNavGroup = {\n id: SettingsNavGroupId;\n tabs: readonly Tab[];\n};\n\n/**\n * Priority: general UX → history → AI (providers → models → agent → search) → voice →\n * connection/heartbeat → extensions (rendered last in settings shell).\n * Cron, skills, IM channels, and agents live under the main app shell (`/cron`, `/skills`, `/channels`, `/agents`), not here.\n * See `settingsNavGroups` copy in i18n.\n */\nexport const SETTINGS_SHELL_NAV_GROUPS: readonly SettingsShellNavGroup[] = [\n { id: 'interface', tabs: ['settingsAppearance', 'settingsSystem'] },\n { id: 'data', tabs: ['sessions', 'logs'] },\n {\n id: 'agentAndModels',\n tabs: ['settingsProviders', 'settingsModels', 'settingsAgentDefaults', 'settingsSearch'],\n },\n { id: 'voice', tabs: ['settingsVoice'] },\n { id: 'gateway', tabs: ['settingsGateway', 'settingsHeartbeat'] },\n] as const;\n\n/** Flat order: settings routes only (excludes sessions/logs). */\nexport const SETTINGS_NAV_TABS: readonly Tab[] = SETTINGS_SHELL_NAV_GROUPS.filter(\n (g) => g.id !== 'data',\n).flatMap((g) => [...g.tabs]);\n\n/** Settings shell: full left rail including sessions + logs. */\nexport const SETTINGS_SHELL_NAV_TABS: readonly Tab[] = SETTINGS_SHELL_NAV_GROUPS.flatMap((g) => [...g.tabs]);\n\n/** Official docs site (VitePress `base: /xopc/`). */\nexport const HELP_DOCS_BASE_URL = 'https://xopcai.github.io/xopc';\n\n/** Sidebar “Documentation” — English root vs `zh/` locale home. */\nexport function helpDocsHomeUrl(language: StoredLanguage): string {\n return language === 'zh' ? `${HELP_DOCS_BASE_URL}/zh/` : `${HELP_DOCS_BASE_URL}/`;\n}\n\n/**\n * Path to a VitePress guide page (e.g. `models` → `/models.md` in repo root `docs/`).\n * Chinese lives under `docs/zh/` → `/zh/<page>` on the site.\n */\nexport function docsGuidePageUrl(language: StoredLanguage, page: string): string {\n const slug = page.replace(/^\\/+/, '').replace(/\\.md$/, '');\n if (language === 'zh') {\n return `${HELP_DOCS_BASE_URL}/zh/${slug}`;\n }\n return `${HELP_DOCS_BASE_URL}/${slug}`;\n}\n\n/** Parse `#/settings/<section>` etc. Returns null if not a settings route. */\nexport function parseSettingsHash(hash: string): SettingsSectionId | null {\n let h = hash.startsWith('#') ? hash.slice(1) : hash;\n if (h.startsWith('/')) h = h.slice(1);\n if (h === 'settings' || h === 'settings/') {\n return 'appearance';\n }\n if (!h.startsWith('settings/')) return null;\n const rest = h.slice('settings/'.length);\n const parts = rest.split('/').filter(Boolean);\n const section = parts[0];\n if (!section) return 'appearance';\n if (section === 'agents' && parts.length > 1) {\n return 'agents';\n }\n return section in SETTINGS_SECTION_TO_TAB ? (section as SettingsSectionId) : null;\n}\n\nexport function getSettingsHash(section: SettingsSectionId): string {\n return `#/settings/${section}`;\n}\n\nexport function parseChatHash(hash: string): ChatRoute | null {\n const withoutHash = hash.startsWith('#') ? hash.slice(1) : hash;\n const cleanHash = withoutHash.replace(/^\\/?chat\\/?/, '');\n\n if (!cleanHash || cleanHash === '/') {\n return { type: 'recent' };\n }\n\n const path = cleanHash.replace(/^\\/?/, '');\n\n if (path === 'new') {\n return { type: 'new' };\n }\n\n if (path && path.length > 0) {\n return { type: 'session', sessionKey: decodeURIComponent(path) };\n }\n\n return { type: 'recent' };\n}\n\nexport function getChatHash(route: ChatRoute): string {\n switch (route.type) {\n case 'recent':\n return '#/chat';\n case 'new':\n return '#/chat/new';\n case 'session':\n return `#/chat/${encodeURIComponent(route.sessionKey)}`;\n }\n}\n\n/** Path for React Router `to` prop (hash router, no `#`). */\nexport function pathForTab(tab: Tab): string {\n if (tab === 'chat') return '/chat';\n if (tab === 'cron') return '/cron';\n if (tab === 'skills') return '/skills';\n if (tab === 'channels' || tab === 'settingsChannels') return '/channels';\n if (tab === 'agents' || tab === 'settingsAgents') return '/agents';\n const section = tabToSettingsSection(tab);\n if (section) return `/settings/${section}`;\n if (tab === 'sessions' || tab === 'logs') {\n return `/settings/${tab}`;\n }\n return `/${tab}`;\n}\n"],"x_google_ignoreList":[0,1,2],"mappings":"qCAaA,IAAM,EAAO,EAAiB,OAJX,CACjB,CAAC,OAAQ,CAAE,MAAO,KAAM,OAAQ,KAAM,EAAG,IAAK,EAAG,IAAK,GAAI,IAAK,GAAI,IAAK,IAAK,SAAU,CAAC,CACxF,CAAC,OAAQ,CAAE,EAAG,0DAA2D,IAAK,SAAU,CAAC,CAC1F,CACgD,CCc3C,EAAS,EAAiB,UAlBb,CACjB,CACE,OACA,CACE,EAAG,iGACH,IAAK,SACN,CACF,CACD,CAAC,OAAQ,CAAE,EAAG,uCAAwC,IAAK,SAAU,CAAC,CACtE,CACE,OACA,CACE,EAAG,+FACH,IAAK,SACN,CACF,CACD,CAAC,OAAQ,CAAE,EAAG,aAAc,IAAK,SAAU,CAAC,CAC7C,CACqD,CCRhD,EAAS,EAAiB,SAVb,CACjB,CACE,OACA,CACE,EAAG,mIACH,IAAK,SACN,CACF,CACD,CAAC,OAAQ,CAAE,EAAG,YAAa,IAAK,SAAU,CAAC,CAC5C,CACoD,CCO/C,EAaF,CACF,mBAAoB,aACpB,eAAgB,SAChB,sBAAuB,iBACvB,eAAgB,SAChB,kBAAmB,YACnB,eAAgB,SAChB,iBAAkB,WAClB,cAAe,QACf,gBAAiB,UACjB,kBAAmB,YACnB,eAAgB,SACjB,CAMD,SAAgB,EAAqB,EAAoC,CACvE,OAAO,EAAwB,IAAgD,KAsBjF,IAAa,EAA8D,CACzE,CAAE,GAAI,YAAa,KAAM,CAAC,qBAAsB,iBAAiB,CAAE,CACnE,CAAE,GAAI,OAAQ,KAAM,CAAC,WAAY,OAAO,CAAE,CAC1C,CACE,GAAI,iBACJ,KAAM,CAAC,oBAAqB,iBAAkB,wBAAyB,iBAAiB,CACzF,CACD,CAAE,GAAI,QAAS,KAAM,CAAC,gBAAgB,CAAE,CACxC,CAAE,GAAI,UAAW,KAAM,CAAC,kBAAmB,oBAAoB,CAAE,CAClE,CAGgD,EAA0B,OACxE,GAAM,EAAE,KAAO,OACjB,CAAC,QAAS,GAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAG0B,EAA0B,QAAS,GAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAG5G,IAAa,EAAqB,gCAGlC,SAAgB,EAAgB,EAAkC,CAChE,OAAO,IAAa,KAAO,GAAG,EAAmB,MAAQ,GAAG,EAAmB,GAOjF,SAAgB,EAAiB,EAA0B,EAAsB,CAC/E,IAAM,EAAO,EAAK,QAAQ,OAAQ,GAAG,CAAC,QAAQ,QAAS,GAAG,CAI1D,OAHI,IAAa,KACR,GAAG,EAAmB,MAAM,IAE9B,GAAG,EAAmB,GAAG,IA0DlC,SAAgB,EAAW,EAAkB,CAC3C,GAAI,IAAQ,OAAQ,MAAO,QAC3B,GAAI,IAAQ,OAAQ,MAAO,QAC3B,GAAI,IAAQ,SAAU,MAAO,UAC7B,GAAI,IAAQ,YAAc,IAAQ,mBAAoB,MAAO,YAC7D,GAAI,IAAQ,UAAY,IAAQ,iBAAkB,MAAO,UACzD,IAAM,EAAU,EAAqB,EAAI,CAKzC,OAJI,EAAgB,aAAa,IAC7B,IAAQ,YAAc,IAAQ,OACzB,aAAa,IAEf,IAAI"}
@@ -0,0 +1,2 @@
1
+ import{c as e}from"./url-CtSqjF9J.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-DJHD9Ean.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"page-header-store-HcRZK5CZ.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-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,2 +1,2 @@
1
- import{r as e,t}from"./url-QmwQTJ-j.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-DxNaAkmX.js.map
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 +1 @@
1
- {"version":3,"file":"session-api-DxNaAkmX.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-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,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-QmwQTJ-j.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-DxNaAkmX.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-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,`
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
- `):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()=>{let e=s.trim();if(e)try{await navigator.clipboard.writeText(e),window.dispatchEvent(new CustomEvent(`extension-notification`,{detail:{type:`success`,title:a.copied,duration:2500}}))}catch{}},[s,a.copied]);if(!e)return null;let b=s.trim(),S=!!b,w=S?$(b):a.notSet,E=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),D=e=>(0,Y.jsxs)(`div`,{className:l(E,!S&&`cursor-default text-fg-muted`),title:e,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:w})]}),O=e=>(0,Y.jsxs)(`button`,{type:`button`,disabled:t||u||!S,className:l(E,S&&`cursor-pointer hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50`,(t||u||!S)&&`cursor-not-allowed opacity-60`),title:e,"aria-label":e,onClick:e=>{e.stopPropagation(),S&&!t&&!u&&y()},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:w})]});if(!n)return S?(0,Y.jsx)(`div`,{className:`inline-flex items-center`,children:O(`${b}\n${a.clickToCopyFullPath}`)}):(0,Y.jsx)(`div`,{className:`inline-flex items-center`,children:D(`${a.notSet}\n${a.selectionOnlyAtNewChat}`)});let k=S?`${b}\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(E,`cursor-pointer hover:bg-surface-hover/70 dark:hover:bg-surface-hover/50`,(t||u)&&`cursor-not-allowed opacity-60`),title:k,"aria-label":k,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:w})]})}),_?null:(0,Y.jsx)(Z,{open:f,onOpenChange:p,initialAbsolutePath:b||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-CDH-Wk4E.js.map
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
@@ -1 +1 @@
1
- {"version":3,"file":"session-working-directory-control-CDH-Wk4E.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(async () => {\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: { type: 'success' as const, title: wd.copied, duration: 2500 },\n }),\n );\n } catch {\n /* ignore */\n }\n }, [effectivePath, wd.copied]);\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 <div\n className={cn(chipClass, !hasPath && 'cursor-default text-fg-muted')}\n title={title}\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 </div>\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();\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
+ {"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":"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"}