@xopcai/xopc 0.0.277 → 0.0.282

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 (315) hide show
  1. package/dist/browser-ext/_locales/en/messages.json +34 -4
  2. package/dist/browser-ext/_locales/zh_CN/messages.json +34 -4
  3. package/dist/browser-ext/dist/assets/{auth-ClQTK565.js → auth-DB30ywek.js} +18 -53
  4. package/dist/browser-ext/dist/assets/pcm-capture-worklet-CGN9Mlhg.js +39 -0
  5. package/dist/browser-ext/dist/assets/{sidepanel-CeGqObOa.css → sidepanel-BbSQr-6P.css} +40 -6
  6. package/dist/browser-ext/dist/background.js +1 -1
  7. package/dist/browser-ext/dist/sidepanel.html +2 -2
  8. package/dist/browser-ext/dist/sidepanel.js +1219 -191
  9. package/dist/browser-ext/manifest.json +2 -1
  10. package/dist/extensions/telegram/xopc.extension.json +1 -1
  11. package/dist/gateway/static/root/assets/{activity-store-SJZ5fS17.js → activity-store-iPtG94qr.js} +1 -1
  12. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-Cavbls0E.js → agent-avatar-dicebear-Cny5rATT.js} +1 -1
  13. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-OyQ8OVZv.js +1 -0
  14. package/dist/gateway/static/root/assets/{agents-DlDTItrB.js → agents-DJeh_Ewo.js} +1 -1
  15. package/dist/gateway/static/root/assets/{api-CnJAVHBu.js → api-KMJzMqvZ.js} +1 -1
  16. package/dist/gateway/static/root/assets/{app-management-settings-panel-5gS0_LWl.js → app-management-settings-panel-ZqQKaZEI.js} +1 -1
  17. package/dist/gateway/static/root/assets/{appearance-settings-DHzZCJon.js → appearance-settings-CfXsQkKO.js} +1 -1
  18. package/dist/gateway/static/root/assets/apps-page-k-QnVhKW.js +1 -0
  19. package/dist/gateway/static/root/assets/{archive-plugin-Ia98yZe3.js → archive-plugin-BrW53Hap.js} +1 -1
  20. package/dist/gateway/static/root/assets/{attachment-load-DiWW88c8.js → attachment-load-DkfFWHiS.js} +1 -1
  21. package/dist/gateway/static/root/assets/{automations-page-BuhTeGRJ.js → automations-page-e-5we6kD.js} +1 -1
  22. package/dist/gateway/static/root/assets/automations-workspace-DPnlkws1.js +6 -0
  23. package/dist/gateway/static/root/assets/{binary-plugins-DDD48cJv.js → binary-plugins-CWIswfXA.js} +1 -1
  24. package/dist/gateway/static/root/assets/{block-editor-0AWVCD3F.js → block-editor-CfeF1OmA.js} +3 -3
  25. package/dist/gateway/static/root/assets/{browser-automation-inputs-DcVLoIzc.js → browser-automation-inputs-BTIufVbM.js} +1 -1
  26. package/dist/gateway/static/root/assets/{browser-automations-page-BKOYX5xV.js → browser-automations-page-DnsuWKRu.js} +2 -2
  27. package/dist/gateway/static/root/assets/browser-settings-page-C8xyhnxx.js +8 -0
  28. package/dist/gateway/static/root/assets/capabilities-settings-panel-CBllOLTT.js +8 -0
  29. package/dist/gateway/static/root/assets/channels-settings-C-A-VUE2.js +1 -0
  30. package/dist/gateway/static/root/assets/{channels-status-swr-BTT2sIGg.js → channels-status-swr-DMYQNs-J.js} +1 -1
  31. package/dist/gateway/static/root/assets/{chat-terminal-dock-CW9Byi8V.js → chat-terminal-dock-C_Fw1R3A.js} +2 -2
  32. package/dist/gateway/static/root/assets/connectors-page-CtsMo3H4.js +2 -0
  33. package/dist/gateway/static/root/assets/{date-picker-gdYmKC8W.js → date-picker-CPFxjTrN.js} +1 -1
  34. package/dist/gateway/static/root/assets/{dependency-picker-40BYmiMc.js → dependency-picker-C_5KqzjI.js} +1 -1
  35. package/dist/gateway/static/root/assets/{desktop-pet-By1z6tKE.js → desktop-pet-DLJsC5D6.js} +1 -1
  36. package/dist/gateway/static/root/assets/{desktop-pet-settings-ByjJqx9H.js → desktop-pet-settings-BdI1iSgz.js} +1 -1
  37. package/dist/gateway/static/root/assets/device-pairing-wizard-BnvPLhc9.js +1 -0
  38. package/dist/gateway/static/root/assets/{extension-debug-page-DQO65KbM.js → extension-debug-page-BNS_TkZQ.js} +1 -1
  39. package/dist/gateway/static/root/assets/{extension-page-Db32jip6.js → extension-page-DMSDZ7Mb.js} +1 -1
  40. package/dist/gateway/static/root/assets/{extension-settings-page-CL8Wx8lf.js → extension-settings-page-CeGCWMt0.js} +1 -1
  41. package/dist/gateway/static/root/assets/gateway-config-swr-Cpmyrr6j.js +1 -0
  42. package/dist/gateway/static/root/assets/gateway-settings-DKOTYUma.js +1 -0
  43. package/dist/gateway/static/root/assets/{gateway-startup-retry-CVTa5HSS.js → gateway-startup-retry-DoiPeb65.js} +1 -1
  44. package/dist/gateway/static/root/assets/{heartbeat-settings-CRXWu2q6.js → heartbeat-settings-gsdL_Yij.js} +1 -1
  45. package/dist/gateway/static/root/assets/home-page-CbeAdS1T.js +1 -0
  46. package/dist/gateway/static/root/assets/{image-generation-api-DtMRG1oF.js → image-generation-api-CqjHWl2T.js} +1 -1
  47. package/dist/gateway/static/root/assets/index-B_Sg5BKz.css +2 -0
  48. package/dist/gateway/static/root/assets/index-enFXJJCU.js +74 -0
  49. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CjehhHPz.js → keyboard-shortcuts-settings-CCLtJ8s8.js} +1 -1
  50. package/dist/gateway/static/root/assets/local-app-workbench-page-DzC6SrXj.js +2 -0
  51. package/dist/gateway/static/root/assets/{local-apps-page-CrIdaTrK.js → local-apps-page-3LdgP3NO.js} +1 -1
  52. package/dist/gateway/static/root/assets/{locale-store-BkJ7g35d.js → locale-store-1urrBBCQ.js} +1 -1
  53. package/dist/gateway/static/root/assets/{logs-page-FfSwV_VH.js → logs-page-DHClKtm6.js} +2 -2
  54. package/dist/gateway/static/root/assets/{management-settings-BakV5aOw.js → management-settings-BaCo9Nuo.js} +1 -1
  55. package/dist/gateway/static/root/assets/{markdown-view-DF3aJP_5.js → markdown-view-BZmcFIWa.js} +1 -1
  56. package/dist/gateway/static/root/assets/{media-plugins-Blv9U6Jp.js → media-plugins-BMjJppvG.js} +1 -1
  57. package/dist/gateway/static/root/assets/messages-DYFcjtmG.js +3 -0
  58. package/dist/gateway/static/root/assets/note-detail-panel-hYO2jNhL.js +5 -0
  59. package/dist/gateway/static/root/assets/notes-page-BT66x22W.js +8 -0
  60. package/dist/gateway/static/root/assets/{page-tabs-BQfVtLLC.js → page-tabs-BCszXX_F.js} +1 -1
  61. package/dist/gateway/static/root/assets/{preview-open-alternatives-kjHFiO81.js → preview-open-alternatives-CyM0sanh.js} +1 -1
  62. package/dist/gateway/static/root/assets/proactive-page-CVDGA5sB.js +1 -0
  63. package/dist/gateway/static/root/assets/{product-open-page-BazdmFYo.js → product-open-page-lXAbj85Z.js} +1 -1
  64. package/dist/gateway/static/root/assets/project-detail-page-Bp6TAMPM.js +1 -0
  65. package/dist/gateway/static/root/assets/projects-page-UhOIiDcg.js +1 -0
  66. package/dist/gateway/static/root/assets/{registry-api-CTq8EECj.js → registry-api-CxQstD00.js} +1 -1
  67. package/dist/gateway/static/root/assets/remote-access-hub-DJIkMFT4.js +1 -0
  68. package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-BhJlopSW.js → runtime-tools-settings-panel-BUFCgRdZ.js} +2 -2
  69. package/dist/gateway/static/root/assets/{schema-form-cxoO_5I1.js → schema-form-B01ksA8M.js} +1 -1
  70. package/dist/gateway/static/root/assets/sessions-page-CQOAwPDt.js +1 -0
  71. package/dist/gateway/static/root/assets/settings-advanced-gate-CFuqbGI-.js +1 -0
  72. package/dist/gateway/static/root/assets/{settings-form-section-BiRTWiBi.js → settings-form-section-DvnjZBa3.js} +1 -1
  73. package/dist/gateway/static/root/assets/{settings-page-D-Cb71pq.js → settings-page-DZRkpbCh.js} +1 -1
  74. package/dist/gateway/static/root/assets/setup-status-panel-DVaEiAVY.js +1 -0
  75. package/dist/gateway/static/root/assets/{share-preview-page-DciHfTay.js → share-preview-page-vz0UK56q.js} +1 -1
  76. package/dist/gateway/static/root/assets/shares-settings-XiCqMDAe.js +1 -0
  77. package/dist/gateway/static/root/assets/skill-reload-api-C3D8WanU.js +1 -0
  78. package/dist/gateway/static/root/assets/skills-page-CxMWwR6A.js +2 -0
  79. package/dist/gateway/static/root/assets/{skills-page.utils-CU7PALQB.js → skills-page.utils-BryVNYll.js} +1 -1
  80. package/dist/gateway/static/root/assets/{system-settings-panel-GpjNbqvM.js → system-settings-panel-mKFDe6aZ.js} +1 -1
  81. package/dist/gateway/static/root/assets/task-detail-page-Cb0RZNcJ.js +1 -0
  82. package/dist/gateway/static/root/assets/{theme-store-mV6Cm2vj.js → theme-store-Dc5s20V3.js} +1 -1
  83. package/dist/gateway/static/root/assets/tunnel-api-FcI3wKy3.js +1 -0
  84. package/dist/gateway/static/root/assets/{use-autosave-LvCiBidm.js → use-autosave-CDgdtjTC.js} +1 -1
  85. package/dist/gateway/static/root/assets/user-model-page-Cnshc-Un.js +2 -0
  86. package/dist/gateway/static/root/assets/{voice-api-key-field-BlGysZxj.js → voice-api-key-field-Ce8cb5mN.js} +1 -1
  87. package/dist/gateway/static/root/assets/{voice-config-api-D3DdcH4H.js → voice-config-api-Mk78BrpP.js} +1 -1
  88. package/dist/gateway/static/root/assets/{work-discovery-overlay-DvIQOR5x.js → work-discovery-overlay-CacFTtXe.js} +1 -1
  89. package/dist/gateway/static/root/assets/{workflow-run-setup-panel-JIp60bXT.js → workflow-run-setup-panel-V-5HuEUq.js} +2 -2
  90. package/dist/gateway/static/root/assets/workflows-page-1OJimIfo.js +6 -0
  91. package/dist/gateway/static/root/index.html +10 -11
  92. package/dist/package.js +1 -1
  93. package/dist/packages/gateway-contract/src/discussions.js +17 -0
  94. package/dist/packages/gateway-contract/src/proactive.js +4 -2
  95. package/dist/src/agent/agent-manager.js +4 -4
  96. package/dist/src/agent/background-review/run-background-review.js +1 -1
  97. package/dist/src/agent/commands/command-isolation.d.ts +1 -0
  98. package/dist/src/agent/commands/command-isolation.js +17 -1
  99. package/dist/src/agent/commands/commandMounts.d.ts +2 -0
  100. package/dist/src/agent/commands/commandMounts.js +32 -0
  101. package/dist/src/agent/context/project-context.d.ts +1 -0
  102. package/dist/src/agent/context/project-context.js +12 -4
  103. package/dist/src/agent/embedded/model-runtime.js +1 -1
  104. package/dist/src/agent/embedded/run-for-session.js +1 -1
  105. package/dist/src/agent/image/tool-model-config.js +1 -1
  106. package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
  107. package/dist/src/agent/sandbox/command-validator.js +0 -20
  108. package/dist/src/agent/sandbox/exec-policy.d.ts +1 -0
  109. package/dist/src/agent/sandbox/exec-policy.js +4 -4
  110. package/dist/src/agent/sandbox/fileAccess.d.ts +7 -0
  111. package/dist/src/agent/sandbox/fileAccess.js +100 -0
  112. package/dist/src/agent/sandbox/path-policy.d.ts +2 -0
  113. package/dist/src/agent/sandbox/path-policy.js +54 -17
  114. package/dist/src/agent/sandbox/sanitize-env-vars.js +10 -9
  115. package/dist/src/agent/service.js +2 -2
  116. package/dist/src/agent/session/session-inspector.js +1 -1
  117. package/dist/src/agent/skills/hub-lock.js +1 -1
  118. package/dist/src/agent/source-context/task-context.d.ts +3 -0
  119. package/dist/src/agent/source-context/task-context.js +24 -0
  120. package/dist/src/agent/source-context/types.d.ts +2 -2
  121. package/dist/src/agent/source-context/types.js +1 -1
  122. package/dist/src/agent/tasks/task-judge-service.js +3 -3
  123. package/dist/src/agent/tool-manuals/xopc-use.d.ts +1 -1
  124. package/dist/src/agent/tool-manuals/xopc-use.js +1 -0
  125. package/dist/src/agent/tools/apply-patch.js +25 -38
  126. package/dist/src/agent/tools/create-share-tool.js +11 -3
  127. package/dist/src/agent/tools/exec-command.js +2 -1
  128. package/dist/src/agent/tools/find.js +5 -3
  129. package/dist/src/agent/tools/grep.js +21 -4
  130. package/dist/src/agent/tools/image-generate-tool.js +3 -4
  131. package/dist/src/agent/tools/list-dir.js +2 -1
  132. package/dist/src/agent/tools/managed-job-tool.js +1 -0
  133. package/dist/src/agent/tools/publish-artifacts.js +4 -3
  134. package/dist/src/agent/tools/read.js +8 -5
  135. package/dist/src/agent/tools/repository-search.d.ts +2 -0
  136. package/dist/src/agent/tools/repository-search.js +51 -7
  137. package/dist/src/agent/tools/review-workspace.js +76 -10
  138. package/dist/src/agent/tools/send-media.js +4 -3
  139. package/dist/src/agent/tools/tool-paths.js +3 -1
  140. package/dist/src/agent/tools/write.js +4 -17
  141. package/dist/src/agent/tools/xopc-use-tool.js +2 -2
  142. package/dist/src/agent-config/schema.d.ts +24 -0
  143. package/dist/src/agent-config/schema.js +2 -1
  144. package/dist/src/ai-assist/text-assist.js +1 -1
  145. package/dist/src/auth/credentials.js +2 -2
  146. package/dist/src/auth/oauth/xopc-cloud.js +1 -1
  147. package/dist/src/auth/profiles/store.js +1 -1
  148. package/dist/src/auth/sync-provider-auth.js +2 -2
  149. package/dist/src/browser/providers/browser-ext-install.js +1 -1
  150. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  151. package/dist/src/cli/commands/doctor/checks/provider-auth.js +21 -20
  152. package/dist/src/cli/commands/models.js +1 -1
  153. package/dist/src/config/index.js +4 -4
  154. package/dist/src/config/loader.js +2 -2
  155. package/dist/src/config/models-json.js +1 -1
  156. package/dist/src/config/schema.d.ts +16 -0
  157. package/dist/src/discussions/action-tasks.d.ts +21 -0
  158. package/dist/src/discussions/action-tasks.js +93 -0
  159. package/dist/src/discussions/analyzer.d.ts +21 -1
  160. package/dist/src/discussions/analyzer.js +229 -62
  161. package/dist/src/discussions/audio-repair.d.ts +8 -0
  162. package/dist/src/discussions/audio-repair.js +41 -0
  163. package/dist/src/discussions/edits.d.ts +22 -0
  164. package/dist/src/discussions/edits.js +81 -0
  165. package/dist/src/discussions/errors.d.ts +4 -0
  166. package/dist/src/discussions/errors.js +10 -0
  167. package/dist/src/discussions/export.d.ts +2 -0
  168. package/dist/src/discussions/export.js +23 -0
  169. package/dist/src/discussions/index.js +3 -2
  170. package/dist/src/discussions/live-worker.js +1 -1
  171. package/dist/src/discussions/organizer-worker.js +14 -5
  172. package/dist/src/discussions/organizer.d.ts +1 -1
  173. package/dist/src/discussions/organizer.js +60 -24
  174. package/dist/src/discussions/reconcile.d.ts +22 -0
  175. package/dist/src/discussions/reconcile.js +55 -0
  176. package/dist/src/discussions/recording.d.ts +9 -0
  177. package/dist/src/discussions/recording.js +219 -0
  178. package/dist/src/discussions/recordingJobs.d.ts +14 -0
  179. package/dist/src/discussions/recordingJobs.js +69 -0
  180. package/dist/src/discussions/repository.d.ts +4 -2
  181. package/dist/src/discussions/repository.js +15 -10
  182. package/dist/src/discussions/revisions.d.ts +12 -0
  183. package/dist/src/discussions/revisions.js +36 -0
  184. package/dist/src/discussions/sealer.d.ts +1 -0
  185. package/dist/src/discussions/sealer.js +61 -11
  186. package/dist/src/discussions/service.d.ts +16 -15
  187. package/dist/src/discussions/service.js +177 -91
  188. package/dist/src/discussions/types.d.ts +4 -139
  189. package/dist/src/discussions/types.js +1 -11
  190. package/dist/src/extensions/lockfile.js +1 -1
  191. package/dist/src/files/file-service.d.ts +1 -0
  192. package/dist/src/files/file-service.js +77 -0
  193. package/dist/src/gateway/agents-admin.js +1 -1
  194. package/dist/src/gateway/chat-stream/mapper.js +2 -2
  195. package/dist/src/gateway/file-path-classifier.js +1 -1
  196. package/dist/src/gateway/hono/app.js +3 -2
  197. package/dist/src/gateway/hono/lib/config-payload.d.ts +1 -0
  198. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  199. package/dist/src/gateway/hono/lib/static-ui.js +1 -0
  200. package/dist/src/gateway/hono/routes/context-sources.js +1 -2
  201. package/dist/src/gateway/hono/routes/discussions.js +126 -19
  202. package/dist/src/gateway/hono/routes/files.js +1 -22
  203. package/dist/src/gateway/hono/routes/index.js +0 -2
  204. package/dist/src/gateway/hono/routes/lazy-bundles.js +16 -0
  205. package/dist/src/gateway/hono/routes/models.js +1 -1
  206. package/dist/src/gateway/hono/routes/proactive-control.js +2 -36
  207. package/dist/src/gateway/hono/routes/project-understanding.d.ts +3 -0
  208. package/dist/src/gateway/hono/routes/project-understanding.js +65 -0
  209. package/dist/src/gateway/hono/routes/projects.js +20 -4
  210. package/dist/src/gateway/hono/routes/session-input-handler.d.ts +2 -2
  211. package/dist/src/gateway/hono/routes/session-input-handler.js +2 -2
  212. package/dist/src/gateway/hono/routes/shares.js +22 -1
  213. package/dist/src/gateway/hono/routes/tasks.js +2 -1
  214. package/dist/src/gateway/hono/routes/work-discovery.d.ts +0 -2
  215. package/dist/src/gateway/hono/routes/work-discovery.js +2 -16
  216. package/dist/src/gateway/hono/routes/workflows.js +2 -2
  217. package/dist/src/gateway/security/gateway-scopes.js +1 -2
  218. package/dist/src/gateway/service/agent-runner.js +2 -2
  219. package/dist/src/gateway/service/config-coordinator.js +1 -1
  220. package/dist/src/gateway/service.d.ts +3 -0
  221. package/dist/src/gateway/service.js +28 -13
  222. package/dist/src/knowledge-memory/repository.d.ts +2 -0
  223. package/dist/src/knowledge-memory/repository.js +3 -1
  224. package/dist/src/media-understanding/audio-transcription-runner.d.ts +1 -0
  225. package/dist/src/media-understanding/audio-transcription-runner.js +1 -0
  226. package/dist/src/media-understanding/runner.js +1 -0
  227. package/dist/src/media-understanding/types.d.ts +8 -0
  228. package/dist/src/notes/service.d.ts +3 -1
  229. package/dist/src/notes/store.d.ts +3 -1
  230. package/dist/src/notes/store.js +5 -3
  231. package/dist/src/proactive/experience.d.ts +38 -21
  232. package/dist/src/proactive/experience.js +113 -12
  233. package/dist/src/proactive/inbox/cards.js +7 -1
  234. package/dist/src/proactive/maintenance.js +0 -1
  235. package/dist/src/proactive/scenarios/control.d.ts +0 -35
  236. package/dist/src/proactive/scenarios/control.js +7 -53
  237. package/dist/src/projects/project-store.js +6 -4
  238. package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
  239. package/dist/src/providers/index.js +2 -2
  240. package/dist/src/providers/model-registry.js +1 -1
  241. package/dist/src/providers/register-runtime-providers.js +1 -1
  242. package/dist/src/session/client-history.d.ts +1 -1
  243. package/dist/src/session/client-history.js +1 -1
  244. package/dist/src/share/share-store.js +5 -1
  245. package/dist/src/storage/sqlite/migrations/166_remove_proactive_previews.sql +1 -0
  246. package/dist/src/storage/sqlite/migrations/167_discussion_recording_chunks.sql +7 -0
  247. package/dist/src/storage/sqlite/migrations/168_discussion_evidence.sql +16 -0
  248. package/dist/src/storage/sqlite/migrations/169_discussion_action_tasks.sql +8 -0
  249. package/dist/src/storage/sqlite/migrations/170_discussion_edits.sql +8 -0
  250. package/dist/src/storage/sqlite/migrations/171_discussion_recording_jobs.sql +14 -0
  251. package/dist/src/storage/sqlite/migrations/172_project_understanding.sql +3 -0
  252. package/dist/src/storage/sqlite/migrations/173_mobile_discussion_source.sql +5 -0
  253. package/dist/src/storage/sqlite/migrations/__tests__/mobile-discussion-source.test.ts +20 -0
  254. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  255. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  256. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  257. package/dist/src/storage/sqlite/schema.js +1 -1
  258. package/dist/src/tasks/home-query-service.js +2 -2
  259. package/dist/src/tasks/task-application-service.js +77 -3
  260. package/dist/src/tasks/task-context-assembler.js +1 -0
  261. package/dist/src/tasks/task-repository.d.ts +1 -0
  262. package/dist/src/tasks/task-repository.js +4 -0
  263. package/dist/src/tui/tui-startup-resources.js +1 -1
  264. package/dist/src/tui/tui.js +1 -1
  265. package/dist/src/voice/audio/normalize.js +28 -5
  266. package/dist/src/voice/stt/transcribe-core.js +1 -0
  267. package/dist/src/voice/stt/types.d.ts +1 -0
  268. package/dist/src/work-discovery/analyzer.d.ts +1 -0
  269. package/dist/src/work-discovery/analyzer.js +28 -22
  270. package/dist/src/work-discovery/index.js +3 -3
  271. package/dist/src/work-discovery/investigator.js +1 -1
  272. package/dist/src/work-discovery/project-understanding.d.ts +7 -0
  273. package/dist/src/work-discovery/project-understanding.js +38 -0
  274. package/dist/src/work-discovery/repository.d.ts +3 -1
  275. package/dist/src/work-discovery/repository.js +18 -5
  276. package/dist/src/work-discovery/service.d.ts +8 -0
  277. package/dist/src/work-discovery/service.js +163 -15
  278. package/dist/src/work-discovery/types.d.ts +2 -0
  279. package/dist/src/workflows/service/workflow-run-service.js +2 -2
  280. package/package.json +3 -3
  281. package/dist/gateway/static/root/assets/agent-defaults-settings-panel-DyCyuJpi.js +0 -1
  282. package/dist/gateway/static/root/assets/apps-page-__yzruGu.js +0 -1
  283. package/dist/gateway/static/root/assets/automations-workspace-DaisbmEh.js +0 -6
  284. package/dist/gateway/static/root/assets/browser-settings-page-C7zjxscU.js +0 -8
  285. package/dist/gateway/static/root/assets/capabilities-settings-panel-7kZMT1r6.js +0 -8
  286. package/dist/gateway/static/root/assets/channels-settings-BKUh6hzO.js +0 -1
  287. package/dist/gateway/static/root/assets/config-reload-event-Bjh2zoKp.js +0 -1
  288. package/dist/gateway/static/root/assets/connectors-page-m8J-F3u8.js +0 -2
  289. package/dist/gateway/static/root/assets/device-pairing-wizard-CfWQfz7a.js +0 -1
  290. package/dist/gateway/static/root/assets/gateway-config-swr-cd8owslz.js +0 -1
  291. package/dist/gateway/static/root/assets/gateway-settings-wLly-Kc1.js +0 -1
  292. package/dist/gateway/static/root/assets/home-page-FqgSwpmy.js +0 -1
  293. package/dist/gateway/static/root/assets/index-5UchbidQ.js +0 -74
  294. package/dist/gateway/static/root/assets/index-CXF9-BpI.css +0 -2
  295. package/dist/gateway/static/root/assets/local-app-workbench-page-DtYZa4GK.js +0 -2
  296. package/dist/gateway/static/root/assets/messages-BGc-OJWg.js +0 -3
  297. package/dist/gateway/static/root/assets/note-detail-panel-JbvyGdEX.js +0 -5
  298. package/dist/gateway/static/root/assets/notes-page-Dm7mE2FK.js +0 -8
  299. package/dist/gateway/static/root/assets/proactive-card-Ap_wfos3.js +0 -1
  300. package/dist/gateway/static/root/assets/proactive-page-D1lEuanT.js +0 -1
  301. package/dist/gateway/static/root/assets/project-detail-page-h00lfcVI.js +0 -1
  302. package/dist/gateway/static/root/assets/projects-page-C2Bo1FAL.js +0 -1
  303. package/dist/gateway/static/root/assets/remote-access-hub-_bZYAl49.js +0 -1
  304. package/dist/gateway/static/root/assets/sessions-page-F7CHsvPa.js +0 -1
  305. package/dist/gateway/static/root/assets/settings-advanced-gate-DtZSUX4H.js +0 -1
  306. package/dist/gateway/static/root/assets/setup-status-panel-VPfBsNVv.js +0 -1
  307. package/dist/gateway/static/root/assets/shares-settings-BS2pnWO0.js +0 -1
  308. package/dist/gateway/static/root/assets/skill-reload-api-DX344nVV.js +0 -1
  309. package/dist/gateway/static/root/assets/skills-page-BhfKUsv7.js +0 -2
  310. package/dist/gateway/static/root/assets/task-detail-page-CElcQ9Sh.js +0 -1
  311. package/dist/gateway/static/root/assets/tunnel-api-Bhi_x_h4.js +0 -1
  312. package/dist/gateway/static/root/assets/user-model-page-BydARq2C.js +0 -2
  313. package/dist/gateway/static/root/assets/workflows-page-B972t6zx.js +0 -6
  314. package/dist/src/proactive/scenarios/preview.d.ts +0 -13
  315. package/dist/src/proactive/scenarios/preview.js +0 -81
@@ -1,4 +1,4 @@
1
- import { R as RealtimeClient, k as gatewayFetch, a as activeTabId, w as t, r as readBrowserOutbox, x as writeBrowserOutbox, g as deleteBrowserOutbox, f as currentTabDescriptor, T as TAB_BINDING_PREFIX, l as getAccessProfile, t as runWithTabSiteAccess, P as PENDING_CONTEXT_KEY, j as extensionLocale, d as captureTabWithPermission, p as pairGateway, v as setAutoConnectEnabled, n as readPendingBrowserPairing, o as readProfile, h as discoverLocalGateway, u as saveExtensionLocalePreference, s as revokeAndForgetProfile, c as cancelPendingBrowserPairing, m as loadExtensionLocalePreference } from './assets/auth-ClQTK565.js';
1
+ import { w as t, M as MAX_BROWSER_CONTEXTS_PER_TURN, b as browserPageContextsInputSchema, k as gatewayFetch, o as openDatabase, R as RealtimeClient, a as activeTabId, g as currentTabDescriptor, T as TAB_BINDING_PREFIX, l as getAccessProfile, t as runWithTabSiteAccess, P as PENDING_CONTEXT_KEY, j as extensionLocale, e as captureTabWithPermission, p as pairGateway, v as setAutoConnectEnabled, r as readPendingBrowserPairing, n as readProfile, h as discoverLocalGateway, u as saveExtensionLocalePreference, s as revokeAndForgetProfile, c as cancelPendingBrowserPairing, m as loadExtensionLocalePreference } from './assets/auth-DB30ywek.js';
2
2
 
3
3
  true &&(function polyfill() {
4
4
  const relList = document.createElement("link").relList;
@@ -17345,6 +17345,720 @@ function requireClient () {
17345
17345
 
17346
17346
  var clientExports = requireClient();
17347
17347
 
17348
+ function MicrophonePermission() {
17349
+ const requesting = reactExports.useRef(false);
17350
+ const [state, setState] = reactExports.useState("idle");
17351
+ async function authorize() {
17352
+ if (requesting.current) return;
17353
+ requesting.current = true;
17354
+ setState("pending");
17355
+ try {
17356
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
17357
+ stream.getTracks().forEach((track) => track.stop());
17358
+ setState("granted");
17359
+ } catch {
17360
+ setState("denied");
17361
+ } finally {
17362
+ requesting.current = false;
17363
+ }
17364
+ }
17365
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "microphone-permission", children: [
17366
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h1", { children: t("voicePermissionOpen") }),
17367
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { role: "status", children: t(state === "granted" ? "voicePermissionGranted" : state === "denied" ? "voicePermissionFailed" : "voicePermissionHelp") }),
17368
+ state !== "granted" ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", disabled: state === "pending", onClick: () => void authorize(), children: t(state === "pending" ? "voiceStarting" : "voicePermissionOpen") }) : null
17369
+ ] });
17370
+ }
17371
+
17372
+ function appendPageContext(pages, page) {
17373
+ const others = pages.filter((item) => item.tabId !== page.tabId || Boolean(item.context.selection) !== Boolean(page.context.selection));
17374
+ if (others.length >= MAX_BROWSER_CONTEXTS_PER_TURN) throw new Error(t("errorPageLimit", String(MAX_BROWSER_CONTEXTS_PER_TURN)));
17375
+ const next = [...others, page];
17376
+ if (!browserPageContextsInputSchema.safeParse(next.map((item) => item.context)).success) throw new Error(t("errorPageContextSize"));
17377
+ return next;
17378
+ }
17379
+
17380
+ const LARGE_PASTE_BYTES = 8 * 1024;
17381
+ const LARGE_PASTE_LINES = 80;
17382
+ const CODE_PASTE_BYTES = 2 * 1024;
17383
+ const CODE_PASTE_LINES = 20;
17384
+ function detectPasteFormat(text) {
17385
+ const sample = text.trimStart().slice(0, 8 * 1024);
17386
+ if (/^(?:<!doctype\s+html\b|<(?:html|head|body)\b)/i.test(sample)) return "html";
17387
+ if (/^<(?:div|span|section|main|article|script|style)\b/i.test(sample)) return "html";
17388
+ const trimmed = text.trim();
17389
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) && /[\":,]/.test(sample)) {
17390
+ return "json";
17391
+ }
17392
+ if (/(?:^|\n)\s*(?:(?:import|export|const|let|var|function|class|interface|type|def|SELECT|CREATE)\b|#!\/)/m.test(
17393
+ sample
17394
+ ) || /(?:=>|\{\s*$|;\s*$)/m.test(sample)) {
17395
+ return "code";
17396
+ }
17397
+ return "text";
17398
+ }
17399
+ function classifyPastedText(text) {
17400
+ const byteLength = new TextEncoder().encode(text).byteLength;
17401
+ const lineCount = text.split(/\r\n|\n|\r/).length;
17402
+ const format = detectPasteFormat(text);
17403
+ const isCode = format !== "text";
17404
+ const shouldAttach = byteLength >= LARGE_PASTE_BYTES || lineCount >= LARGE_PASTE_LINES || isCode && (byteLength >= CODE_PASTE_BYTES || lineCount >= CODE_PASTE_LINES);
17405
+ if (!shouldAttach) return null;
17406
+ const file = format === "html" ? { name: "pasted-text.html", mimeType: "text/html" } : format === "json" ? { name: "pasted-text.json", mimeType: "application/json" } : format === "code" ? { name: "pasted-code.txt", mimeType: "text/plain" } : { name: "pasted-text.txt", mimeType: "text/plain" };
17407
+ return { text, ...file, byteLength, lineCount };
17408
+ }
17409
+
17410
+ function thinkingLabel(level) {
17411
+ return t(`thinking${level[0].toUpperCase()}${level.slice(1)}`);
17412
+ }
17413
+ function ModelControls({ models, config, disabled, onModel, onThinking }) {
17414
+ const details = reactExports.useRef(null);
17415
+ const [query, setQuery] = reactExports.useState("");
17416
+ const [saving, setSaving] = reactExports.useState(false);
17417
+ const [error, setError] = reactExports.useState("");
17418
+ const selected = models.find((model) => model.id === config?.model);
17419
+ const filtered = models.filter((model) => `${model.name} ${model.id}`.toLowerCase().includes(query.toLowerCase()));
17420
+ reactExports.useEffect(() => {
17421
+ const close = (event) => {
17422
+ if (details.current && (event instanceof KeyboardEvent && event.key === "Escape" || event instanceof MouseEvent && !details.current.contains(event.target))) details.current.open = false;
17423
+ };
17424
+ document.addEventListener("mousedown", close);
17425
+ document.addEventListener("keydown", close);
17426
+ return () => {
17427
+ document.removeEventListener("mousedown", close);
17428
+ document.removeEventListener("keydown", close);
17429
+ };
17430
+ }, []);
17431
+ async function update(action) {
17432
+ if (saving || disabled) return;
17433
+ setSaving(true);
17434
+ setError("");
17435
+ try {
17436
+ await action();
17437
+ } catch (cause) {
17438
+ setError(String(cause));
17439
+ } finally {
17440
+ setSaving(false);
17441
+ }
17442
+ }
17443
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("details", { className: "model-picker", ref: details, children: [
17444
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("summary", { "aria-label": t("sessionModel"), children: [
17445
+ selected?.name ?? config?.model ?? t("model"),
17446
+ config?.thinkingLevel ? ` · ${thinkingLabel(config.thinkingLevel)}` : ""
17447
+ ] }),
17448
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "model-picker-panel", children: [
17449
+ /* @__PURE__ */ jsxRuntimeExports.jsx("input", { "aria-label": t("searchModels"), placeholder: t("searchModels"), value: query, onChange: (event) => setQuery(event.target.value) }),
17450
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "model-picker-list", children: [
17451
+ filtered.map((model) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", disabled: disabled || saving, "aria-pressed": model.id === config?.model, onClick: () => void update(() => onModel(model.id)), children: [
17452
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: model.name }),
17453
+ /* @__PURE__ */ jsxRuntimeExports.jsx("small", { children: model.id })
17454
+ ] }, model.id)),
17455
+ !filtered.length ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: t("noMatchingModels") }) : null
17456
+ ] }),
17457
+ selected?.thinking?.options?.length ? /* @__PURE__ */ jsxRuntimeExports.jsxs("fieldset", { disabled: disabled || saving, children: [
17458
+ /* @__PURE__ */ jsxRuntimeExports.jsx("legend", { children: t("thinkingLevel") }),
17459
+ selected.thinking.options.map((level) => /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", "aria-pressed": config?.thinkingLevel === level, onClick: () => void update(() => onThinking(level)), children: thinkingLabel(level) }, level))
17460
+ ] }) : null,
17461
+ error ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { role: "alert", children: error }) : null
17462
+ ] })
17463
+ ] });
17464
+ }
17465
+
17466
+ const pcmCaptureWorkletUrl = ""+new URL('assets/pcm-capture-worklet-CGN9Mlhg.js', import.meta.url).href+"";
17467
+
17468
+ const TARGET_SAMPLE_RATE = 16e3;
17469
+ function calculateRmsLevel(samples) {
17470
+ if (samples.length === 0) return 0;
17471
+ let sumSquares = 0;
17472
+ for (let i = 0; i < samples.length; i += 1) {
17473
+ sumSquares += samples[i] * samples[i];
17474
+ }
17475
+ return Math.min(1, Math.sqrt(sumSquares / samples.length));
17476
+ }
17477
+ function mergeChunks(chunks) {
17478
+ const length = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
17479
+ const merged = new Float32Array(length);
17480
+ let offset = 0;
17481
+ for (const chunk of chunks) {
17482
+ merged.set(chunk, offset);
17483
+ offset += chunk.length;
17484
+ }
17485
+ return merged;
17486
+ }
17487
+ function resamplePcm(input, sourceRate, targetRate = TARGET_SAMPLE_RATE) {
17488
+ if (sourceRate === targetRate) return input;
17489
+ const outputLength = Math.max(1, Math.round(input.length * targetRate / sourceRate));
17490
+ const output = new Float32Array(outputLength);
17491
+ const ratio = sourceRate / targetRate;
17492
+ for (let i = 0; i < outputLength; i += 1) {
17493
+ const position = i * ratio;
17494
+ const left = Math.min(input.length - 1, Math.floor(position));
17495
+ const right = Math.min(input.length - 1, left + 1);
17496
+ const fraction = position - left;
17497
+ output[i] = input[left] * (1 - fraction) + input[right] * fraction;
17498
+ }
17499
+ return output;
17500
+ }
17501
+ function encodePcm16Wav(samples, sampleRate = TARGET_SAMPLE_RATE) {
17502
+ const buffer = new ArrayBuffer(44 + samples.length * 2);
17503
+ const view = new DataView(buffer);
17504
+ const writeText = (offset, value) => {
17505
+ for (let i = 0; i < value.length; i += 1) view.setUint8(offset + i, value.charCodeAt(i));
17506
+ };
17507
+ writeText(0, "RIFF");
17508
+ view.setUint32(4, 36 + samples.length * 2, true);
17509
+ writeText(8, "WAVE");
17510
+ writeText(12, "fmt ");
17511
+ view.setUint32(16, 16, true);
17512
+ view.setUint16(20, 1, true);
17513
+ view.setUint16(22, 1, true);
17514
+ view.setUint32(24, sampleRate, true);
17515
+ view.setUint32(28, sampleRate * 2, true);
17516
+ view.setUint16(32, 2, true);
17517
+ view.setUint16(34, 16, true);
17518
+ writeText(36, "data");
17519
+ view.setUint32(40, samples.length * 2, true);
17520
+ for (let i = 0; i < samples.length; i += 1) {
17521
+ const sample = Math.max(-1, Math.min(1, samples[i]));
17522
+ view.setInt16(44 + i * 2, sample < 0 ? sample * 32768 : sample * 32767, true);
17523
+ }
17524
+ return buffer;
17525
+ }
17526
+ const WORKLET_NAME = "xopc-pcm-capture";
17527
+ const WORKLET_FLUSH_TIMEOUT_MS = 1e3;
17528
+ class PcmFrameCapture {
17529
+ constructor(context, source, node, mutedOutput, options) {
17530
+ this.context = context;
17531
+ this.source = source;
17532
+ this.node = node;
17533
+ this.mutedOutput = mutedOutput;
17534
+ this.options = options;
17535
+ this.sampleRate = context.sampleRate;
17536
+ this.node.port.onmessage = (event) => {
17537
+ if (event.data.type === "flushed") {
17538
+ this.flushResolve?.();
17539
+ return;
17540
+ }
17541
+ if (!this.accepting || this.paused) return;
17542
+ const samples = new Float32Array(event.data.buffer);
17543
+ this.options.onSamples(samples);
17544
+ if (this.options.onAudioLevel) {
17545
+ const level = calculateRmsLevel(samples);
17546
+ this.options.onAudioLevel({
17547
+ level,
17548
+ speaking: level >= (this.options.speechThreshold ?? 0.015)
17549
+ });
17550
+ }
17551
+ };
17552
+ this.node.onprocessorerror = () => {
17553
+ this.processorFailed = true;
17554
+ this.flushResolve?.();
17555
+ };
17556
+ }
17557
+ context;
17558
+ source;
17559
+ node;
17560
+ mutedOutput;
17561
+ options;
17562
+ sampleRate;
17563
+ paused = false;
17564
+ accepting = true;
17565
+ processorFailed = false;
17566
+ stopPromise;
17567
+ flushResolve;
17568
+ static async start(stream, options) {
17569
+ if (typeof AudioWorkletNode === "undefined") {
17570
+ throw new Error("PCM audio recording is not supported in this browser");
17571
+ }
17572
+ const context = new AudioContext();
17573
+ try {
17574
+ await context.audioWorklet.addModule(pcmCaptureWorkletUrl);
17575
+ const source = context.createMediaStreamSource(stream);
17576
+ const node = new AudioWorkletNode(context, WORKLET_NAME);
17577
+ const mutedOutput = context.createGain();
17578
+ mutedOutput.gain.value = 0;
17579
+ const capture = new PcmFrameCapture(context, source, node, mutedOutput, options);
17580
+ source.connect(node);
17581
+ node.connect(mutedOutput);
17582
+ mutedOutput.connect(context.destination);
17583
+ await context.resume();
17584
+ return capture;
17585
+ } catch (error) {
17586
+ await context.close().catch(() => void 0);
17587
+ throw error;
17588
+ }
17589
+ }
17590
+ pause() {
17591
+ this.paused = true;
17592
+ }
17593
+ resume() {
17594
+ this.paused = false;
17595
+ }
17596
+ stop() {
17597
+ this.stopPromise ??= this.finish();
17598
+ return this.stopPromise;
17599
+ }
17600
+ cancel() {
17601
+ if (!this.accepting) return;
17602
+ this.accepting = false;
17603
+ this.flushResolve?.();
17604
+ this.flushResolve = void 0;
17605
+ this.disconnect();
17606
+ void this.context.close().catch(() => void 0);
17607
+ }
17608
+ async finish() {
17609
+ if (!this.accepting) return;
17610
+ if (!this.processorFailed) await this.flushWorklet();
17611
+ this.flushResolve = void 0;
17612
+ if (!this.accepting) return;
17613
+ this.accepting = false;
17614
+ this.disconnect();
17615
+ await this.context.close().catch(() => void 0);
17616
+ }
17617
+ flushWorklet() {
17618
+ return new Promise((resolve) => {
17619
+ let settled = false;
17620
+ const complete = () => {
17621
+ if (settled) return;
17622
+ settled = true;
17623
+ globalThis.clearTimeout(timeout);
17624
+ resolve();
17625
+ };
17626
+ const timeout = globalThis.setTimeout(complete, WORKLET_FLUSH_TIMEOUT_MS);
17627
+ this.flushResolve = complete;
17628
+ try {
17629
+ this.node.port.postMessage("flush");
17630
+ } catch {
17631
+ complete();
17632
+ }
17633
+ });
17634
+ }
17635
+ disconnect() {
17636
+ this.node.port.onmessage = null;
17637
+ this.node.onprocessorerror = null;
17638
+ this.source.disconnect();
17639
+ this.node.disconnect();
17640
+ this.mutedOutput.disconnect();
17641
+ }
17642
+ }
17643
+ class PcmWavRecorder {
17644
+ constructor(capture, chunks) {
17645
+ this.capture = capture;
17646
+ this.chunks = chunks;
17647
+ }
17648
+ capture;
17649
+ chunks;
17650
+ stopped = false;
17651
+ static async start(stream, options = {}) {
17652
+ const chunks = [];
17653
+ const capture = await PcmFrameCapture.start(stream, {
17654
+ ...options,
17655
+ onSamples: (samples) => chunks.push(samples)
17656
+ });
17657
+ return new PcmWavRecorder(capture, chunks);
17658
+ }
17659
+ async stop() {
17660
+ if (this.stopped) return new Blob([], { type: "audio/wav" });
17661
+ this.stopped = true;
17662
+ await this.capture.stop();
17663
+ const merged = mergeChunks(this.chunks);
17664
+ const samples = resamplePcm(merged, this.capture.sampleRate);
17665
+ return new Blob([encodePcm16Wav(samples)], { type: "audio/wav" });
17666
+ }
17667
+ cancel() {
17668
+ if (this.stopped) return;
17669
+ this.stopped = true;
17670
+ this.capture.cancel();
17671
+ this.chunks.length = 0;
17672
+ }
17673
+ }
17674
+
17675
+ function IconBase({ children, ...props }) {
17676
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", ...props, children });
17677
+ }
17678
+ function ChevronDownIcon(props) {
17679
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m7 10 5 5 5-5" }) });
17680
+ }
17681
+ function ArrowDownIcon(props) {
17682
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 5v14M6 13l6 6 6-6" }) });
17683
+ }
17684
+ function PlusIcon(props) {
17685
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 5v14M5 12h14" }) });
17686
+ }
17687
+ function SearchIcon(props) {
17688
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17689
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "11", cy: "11", r: "7" }),
17690
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m20 20-4-4" })
17691
+ ] });
17692
+ }
17693
+ function PaperclipIcon(props) {
17694
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m20.5 11.5-8.9 8.9a6 6 0 0 1-8.5-8.5l9.5-9.5a4 4 0 0 1 5.7 5.7l-9.5 9.5A2 2 0 0 1 6 14.8l8.8-8.8" }) });
17695
+ }
17696
+ function PageIcon(props) {
17697
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17698
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" }),
17699
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 2v6h6M8 13h8M8 17h5" })
17700
+ ] });
17701
+ }
17702
+ function SelectionIcon(props) {
17703
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 4H5a1 1 0 0 0-1 1v3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3M8 9h8M8 13h6" }) });
17704
+ }
17705
+ function CameraIcon(props) {
17706
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17707
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14.5 4 16 7h3a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h3l1.5-3Z" }),
17708
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "13", r: "3" })
17709
+ ] });
17710
+ }
17711
+ function FileIcon(props) {
17712
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17713
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" }),
17714
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 2v6h6" })
17715
+ ] });
17716
+ }
17717
+ function EyeIcon(props) {
17718
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17719
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z" }),
17720
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "2.5" })
17721
+ ] });
17722
+ }
17723
+ function CursorIcon(props) {
17724
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m5 3 14 9-6 2-3 6Z" }) });
17725
+ }
17726
+ function SendIcon(props) {
17727
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m5 12 7-7 7 7M12 19V5" }) });
17728
+ }
17729
+ function StopIcon(props) {
17730
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "7", y: "7", width: "10", height: "10", rx: "1.5", fill: "currentColor", stroke: "none" }) });
17731
+ }
17732
+ function CopyIcon(props) {
17733
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17734
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }),
17735
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3" })
17736
+ ] });
17737
+ }
17738
+ function CheckIcon(props) {
17739
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m5 12 4 4L19 6" }) });
17740
+ }
17741
+ function CloseIcon(props) {
17742
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m6 6 12 12M18 6 6 18" }) });
17743
+ }
17744
+ function SparkleIcon(props) {
17745
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 3c.7 4.4 3.6 7.3 8 8-4.4.7-7.3 3.6-8 8-.7-4.4-3.6-7.3-8-8 4.4-.7 7.3-3.6 8-8Z" }) });
17746
+ }
17747
+ function AlertIcon(props) {
17748
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17749
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "9" }),
17750
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 8v5M12 16.5h.01" })
17751
+ ] });
17752
+ }
17753
+ function SettingsIcon(props) {
17754
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17755
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "3" }),
17756
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.86 2.86-.06-.06A1.7 1.7 0 0 0 15 19.4a1.7 1.7 0 0 0-1 .6 1.7 1.7 0 0 0-.4 1.1V21H9.55v-.1A1.7 1.7 0 0 0 8.5 19.4a1.7 1.7 0 0 0-1.88.34l-.06.06-2.86-2.86.06-.06A1.7 1.7 0 0 0 4.1 15a1.7 1.7 0 0 0-.6-1 1.7 1.7 0 0 0-1.1-.4H2V9.55h.4A1.7 1.7 0 0 0 4.1 8.5a1.7 1.7 0 0 0-.34-1.88l-.06-.06L6.56 3.7l.06.06A1.7 1.7 0 0 0 8.5 4.1a1.7 1.7 0 0 0 1-.6 1.7 1.7 0 0 0 .4-1.1V2h4.05v.4A1.7 1.7 0 0 0 15 4.1a1.7 1.7 0 0 0 1.88-.34l.06-.06 2.86 2.86-.06.06A1.7 1.7 0 0 0 19.4 8.5a1.7 1.7 0 0 0 .6 1 1.7 1.7 0 0 0 1.1.4h.4v4.05h-.4A1.7 1.7 0 0 0 19.4 15Z" })
17757
+ ] });
17758
+ }
17759
+ function SunIcon(props) {
17760
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17761
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "4" }),
17762
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.42 1.42M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.42-1.42M17.66 6.34l1.41-1.41" })
17763
+ ] });
17764
+ }
17765
+ function GlobeIcon(props) {
17766
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17767
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "9" }),
17768
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 12h18M12 3a15 15 0 0 1 0 18M12 3a15 15 0 0 0 0 18" })
17769
+ ] });
17770
+ }
17771
+ function MicrophoneIcon(props) {
17772
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
17773
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "9", y: "2", width: "6", height: "12", rx: "3" }),
17774
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8" })
17775
+ ] });
17776
+ }
17777
+
17778
+ function VoiceInput({ disabled, onTranscript, onBusy }) {
17779
+ const [state, setState] = reactExports.useState("idle");
17780
+ const [error, setError] = reactExports.useState("");
17781
+ const [needsPermission, setNeedsPermission] = reactExports.useState(false);
17782
+ const [seconds, setSeconds] = reactExports.useState(0);
17783
+ const recorder = reactExports.useRef(void 0);
17784
+ const stream = reactExports.useRef(void 0);
17785
+ const generation = reactExports.useRef(0);
17786
+ const timer = reactExports.useRef(void 0);
17787
+ const controller = reactExports.useRef(void 0);
17788
+ const audio = reactExports.useRef(void 0);
17789
+ const busy = reactExports.useRef(false);
17790
+ const stopTracks = (media = stream.current) => {
17791
+ media?.getTracks().forEach((track) => track.stop());
17792
+ if (stream.current === media) {
17793
+ stream.current = void 0;
17794
+ clearInterval(timer.current);
17795
+ }
17796
+ };
17797
+ function cancel() {
17798
+ generation.current++;
17799
+ controller.current?.abort();
17800
+ recorder.current?.cancel();
17801
+ recorder.current = void 0;
17802
+ stopTracks();
17803
+ busy.current = false;
17804
+ audio.current = void 0;
17805
+ setState("idle");
17806
+ onBusy(false);
17807
+ }
17808
+ reactExports.useEffect(() => () => {
17809
+ generation.current++;
17810
+ controller.current?.abort();
17811
+ recorder.current?.cancel();
17812
+ stopTracks();
17813
+ onBusy(false);
17814
+ }, []);
17815
+ async function transcribe(blob, id) {
17816
+ setState("transcribing");
17817
+ setError("");
17818
+ busy.current = true;
17819
+ onBusy(true);
17820
+ controller.current = new AbortController();
17821
+ try {
17822
+ const form = new FormData();
17823
+ form.append("audio", blob, "recording.wav");
17824
+ const response = await gatewayFetch("/api/voice/transcriptions", {
17825
+ method: "POST",
17826
+ body: form,
17827
+ signal: AbortSignal.any([controller.current.signal, AbortSignal.timeout(12e4)])
17828
+ });
17829
+ const body = await response.json();
17830
+ if (!response.ok) throw new Error(body.error?.message ?? t("errorGatewayStatus", String(response.status)));
17831
+ if (typeof body.payload?.text !== "string" || !body.payload.text.trim()) throw new Error(t("voiceNoSpeech"));
17832
+ if (generation.current === id) {
17833
+ onTranscript(body.payload.text.trim());
17834
+ audio.current = void 0;
17835
+ }
17836
+ } catch (cause) {
17837
+ if (generation.current === id) setError(cause instanceof Error ? cause.message : String(cause));
17838
+ } finally {
17839
+ if (generation.current === id) {
17840
+ busy.current = false;
17841
+ setState("idle");
17842
+ onBusy(false);
17843
+ }
17844
+ }
17845
+ }
17846
+ async function finish(id = generation.current) {
17847
+ if (!recorder.current || generation.current !== id) return;
17848
+ const current = recorder.current;
17849
+ const media = stream.current;
17850
+ recorder.current = void 0;
17851
+ clearInterval(timer.current);
17852
+ setState("transcribing");
17853
+ try {
17854
+ const blob = await current.stop();
17855
+ stopTracks(media);
17856
+ if (generation.current !== id) return;
17857
+ audio.current = blob;
17858
+ await transcribe(blob, id);
17859
+ } catch (cause) {
17860
+ if (generation.current === id) stopTracks();
17861
+ if (generation.current === id) {
17862
+ busy.current = false;
17863
+ setState("idle");
17864
+ onBusy(false);
17865
+ setError(String(cause));
17866
+ }
17867
+ }
17868
+ }
17869
+ async function start() {
17870
+ if (disabled || busy.current) return;
17871
+ busy.current = true;
17872
+ onBusy(true);
17873
+ setState("starting");
17874
+ setError("");
17875
+ setNeedsPermission(false);
17876
+ audio.current = void 0;
17877
+ const id = ++generation.current;
17878
+ try {
17879
+ const statusResponse = await gatewayFetch("/api/status");
17880
+ if (!statusResponse.ok) throw new Error(t("errorGatewayStatus", String(statusResponse.status)));
17881
+ const status = await statusResponse.json();
17882
+ if (!status.voice?.sttAvailable) throw new Error(t("voiceUnavailable"));
17883
+ if (generation.current !== id) return;
17884
+ const media = await navigator.mediaDevices.getUserMedia({ audio: true });
17885
+ if (generation.current !== id) {
17886
+ media.getTracks().forEach((track) => track.stop());
17887
+ return;
17888
+ }
17889
+ stream.current = media;
17890
+ const next = await PcmWavRecorder.start(media);
17891
+ if (generation.current !== id) {
17892
+ next.cancel();
17893
+ media.getTracks().forEach((track) => track.stop());
17894
+ return;
17895
+ }
17896
+ recorder.current = next;
17897
+ setSeconds(0);
17898
+ setState("recording");
17899
+ const started = Date.now();
17900
+ timer.current = setInterval(() => {
17901
+ const elapsed = Math.floor((Date.now() - started) / 1e3);
17902
+ setSeconds(elapsed);
17903
+ if (elapsed >= 120) void finish(id);
17904
+ }, 1e3);
17905
+ } catch (cause) {
17906
+ if (generation.current === id) stopTracks();
17907
+ if (generation.current === id) {
17908
+ const denied = (cause instanceof DOMException || cause instanceof Error) && cause.name === "NotAllowedError";
17909
+ busy.current = false;
17910
+ setState("idle");
17911
+ onBusy(false);
17912
+ setNeedsPermission(denied);
17913
+ setError(denied ? t("voicePermissionHelp") : cause instanceof Error ? cause.message : String(cause));
17914
+ }
17915
+ }
17916
+ }
17917
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "voice-input", children: [
17918
+ state === "idle" ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "composer-icon-button", disabled, title: t("voiceInput"), "aria-label": t("voiceInput"), onClick: () => void start(), children: /* @__PURE__ */ jsxRuntimeExports.jsx(MicrophoneIcon, {}) }) : null,
17919
+ state !== "idle" || error ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "voice-input-panel", role: "status", children: [
17920
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: error || (state === "recording" ? `${t("voiceRecording")} ${seconds}s` : state === "starting" ? t("voiceStarting") : t("voiceTranscribing")) }),
17921
+ needsPermission ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
17922
+ void chrome.tabs.create({ url: chrome.runtime.getURL("dist/sidepanel.html?microphone-permission") }).catch((cause) => setError(String(cause)));
17923
+ }, children: t("voicePermissionOpen") }) : null,
17924
+ state === "recording" ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => void finish(), children: t("voiceFinish") }) : null,
17925
+ state === "idle" && audio.current ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", disabled, onClick: () => void transcribe(audio.current, ++generation.current), children: t("voiceRetry") }) : null,
17926
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => {
17927
+ cancel();
17928
+ setError("");
17929
+ }, children: t("cancelQueuedMessage") })
17930
+ ] }) : null
17931
+ ] });
17932
+ }
17933
+
17934
+ function QueuedInput({ input, onSave, onCancel }) {
17935
+ const [edit, setEdit] = reactExports.useState();
17936
+ const [busy, setBusy] = reactExports.useState(false);
17937
+ const [error, setError] = reactExports.useState("");
17938
+ async function run(action) {
17939
+ if (busy) return;
17940
+ setBusy(true);
17941
+ setError("");
17942
+ try {
17943
+ await action();
17944
+ setEdit(void 0);
17945
+ } catch (cause) {
17946
+ setError(String(cause));
17947
+ } finally {
17948
+ setBusy(false);
17949
+ }
17950
+ }
17951
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "queued-input", children: [
17952
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "composer-notice", children: [
17953
+ edit ? /* @__PURE__ */ jsxRuntimeExports.jsx("input", { "aria-label": t("editQueuedMessage"), value: edit.content, disabled: busy, onChange: (event) => setEdit({ ...edit, content: event.target.value }), onKeyDown: (event) => {
17954
+ if (event.key === "Enter") {
17955
+ event.preventDefault();
17956
+ if (!event.nativeEvent.isComposing && edit.content.trim()) void run(() => onSave(edit.content, edit.version));
17957
+ }
17958
+ } }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
17959
+ t("queued"),
17960
+ " · ",
17961
+ input.content || t("attachment")
17962
+ ] }),
17963
+ edit ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
17964
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", disabled: busy || !edit.content.trim(), onClick: () => void run(() => onSave(edit.content, edit.version)), children: t("saveQueuedMessage") }),
17965
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", disabled: busy, onClick: () => setEdit(void 0), children: t("cancelQueuedMessage") })
17966
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
17967
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", disabled: busy, onClick: () => setEdit({ content: input.content, version: input.version }), children: t("editQueuedMessage") }),
17968
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", disabled: busy, onClick: () => void run(onCancel), children: t("cancelQueuedMessage") })
17969
+ ] })
17970
+ ] }),
17971
+ error ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "composer-error", role: "alert", children: error }) : null
17972
+ ] });
17973
+ }
17974
+
17975
+ function findCommand(value, cursor) {
17976
+ const match = /(?:^|\s)\/([\w.-]*)$/.exec(value.slice(0, cursor));
17977
+ return match ? { start: cursor - match[1].length - 1, end: cursor, query: match[1] } : void 0;
17978
+ }
17979
+ async function loadComposerCommands(sessionKey) {
17980
+ const params = new URLSearchParams(sessionKey ? { sessionKey } : {});
17981
+ const responses = await Promise.all([
17982
+ gatewayFetch("/api/commands"),
17983
+ gatewayFetch(`/api/chat/skills?${params}`)
17984
+ ]);
17985
+ for (const response of responses) if (!response.ok) throw new Error(t("errorGatewayStatus", String(response.status)));
17986
+ const [commands, skills] = await Promise.all(responses.map((response) => response.json()));
17987
+ return [
17988
+ ...(commands.payload?.commands ?? []).map((command) => ({
17989
+ id: `command:${command.name}`,
17990
+ name: `/${command.name}`,
17991
+ description: command.description,
17992
+ wire: `/${command.name} `
17993
+ })),
17994
+ ...(skills.payload?.skills ?? []).map((skill) => ({
17995
+ id: `skill:${skill.name}`,
17996
+ name: skill.name,
17997
+ description: skill.description,
17998
+ wire: `/skill:${skill.name} `,
17999
+ disabled: !skill.availableForCurrentAgent
18000
+ }))
18001
+ ];
18002
+ }
18003
+
18004
+ function ComposerPreviewDialog({ preview, onClose }) {
18005
+ const dialog = reactExports.useRef(null);
18006
+ reactExports.useEffect(() => {
18007
+ dialog.current?.showModal();
18008
+ }, []);
18009
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("dialog", { ref: dialog, className: "composer-preview", "aria-label": preview.title, onClose, children: [
18010
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("header", { children: [
18011
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: preview.title }),
18012
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => dialog.current?.close(), children: t("closePreview") })
18013
+ ] }),
18014
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "composer-preview-body", children: preview.image ? /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: preview.image, alt: preview.title }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { children: preview.text }) })
18015
+ ] });
18016
+ }
18017
+
18018
+ const OUTBOX_STORE = "outbox";
18019
+ function transactionResult(database, mode, createRequest) {
18020
+ return new Promise((resolve, reject) => {
18021
+ const transaction = database.transaction(OUTBOX_STORE, mode);
18022
+ const request = createRequest(transaction.objectStore(OUTBOX_STORE));
18023
+ let result;
18024
+ request.onsuccess = () => {
18025
+ result = request.result;
18026
+ };
18027
+ request.onerror = () => reject(request.error ?? new Error(t("errorOutboxRequest")));
18028
+ transaction.oncomplete = () => resolve(result);
18029
+ transaction.onerror = () => reject(transaction.error ?? new Error(t("errorOutboxTransaction")));
18030
+ transaction.onabort = () => reject(transaction.error ?? new Error(t("errorOutboxAborted")));
18031
+ });
18032
+ }
18033
+ async function readBrowserOutbox(sessionKey) {
18034
+ const database = await openDatabase();
18035
+ try {
18036
+ return await transactionResult(
18037
+ database,
18038
+ "readonly",
18039
+ (store) => store.get(sessionKey)
18040
+ );
18041
+ } finally {
18042
+ database.close();
18043
+ }
18044
+ }
18045
+ async function writeBrowserOutbox(sessionKey, value) {
18046
+ const database = await openDatabase();
18047
+ try {
18048
+ await transactionResult(database, "readwrite", (store) => store.put(value, sessionKey));
18049
+ } finally {
18050
+ database.close();
18051
+ }
18052
+ }
18053
+ async function deleteBrowserOutbox(sessionKey) {
18054
+ const database = await openDatabase();
18055
+ try {
18056
+ await transactionResult(database, "readwrite", (store) => store.delete(sessionKey));
18057
+ } finally {
18058
+ database.close();
18059
+ }
18060
+ }
18061
+
17348
18062
  const CLIENT_ID_KEY = "xopc.browser.client-id";
17349
18063
  const ACTIVE_CHAT_KEY = "xopc.browser.active-chat";
17350
18064
  const CURSOR_PREFIX = "xopc.browser.cursor.";
@@ -17457,8 +18171,10 @@ class BrowserChatClient {
17457
18171
  turnClaim;
17458
18172
  runTopic;
17459
18173
  recoveringOutbox = false;
18174
+ sendingInput = false;
17460
18175
  lastOutboxRecoveryAt = 0;
17461
18176
  sessionsRequest = 0;
18177
+ inputsRequest = 0;
17462
18178
  endpointBinding;
17463
18179
  endpointBindingTask;
17464
18180
  endpointPoll;
@@ -17659,6 +18375,7 @@ ${endpointId}`;
17659
18375
  stopping: false,
17660
18376
  sessionLoading: true,
17661
18377
  pendingDelivery: false,
18378
+ queuedInputs: [],
17662
18379
  clarification: void 0,
17663
18380
  tabBinding: void 0,
17664
18381
  browserApproval: void 0,
@@ -17699,7 +18416,19 @@ ${endpointId}`;
17699
18416
  }
17700
18417
  }
17701
18418
  }
18419
+ get currentSessionKey() {
18420
+ return this.snapshot.sessionKey;
18421
+ }
17702
18422
  async send(content, browserContexts = [], attachments = []) {
18423
+ if (this.sendingInput) throw new Error(t("errorWaitQueuedMessage"));
18424
+ this.sendingInput = true;
18425
+ try {
18426
+ return await this.sendInput(content, browserContexts, attachments);
18427
+ } finally {
18428
+ this.sendingInput = false;
18429
+ }
18430
+ }
18431
+ async sendInput(content, browserContexts = [], attachments = []) {
17703
18432
  const text = content.trim();
17704
18433
  if (!text && attachments.length === 0) return "sent";
17705
18434
  if (!this.snapshot.sessionKey) throw new Error(t("errorOpenChatBeforeSending"));
@@ -17707,14 +18436,16 @@ ${endpointId}`;
17707
18436
  throw new Error(t("errorWaitQueuedMessage"));
17708
18437
  }
17709
18438
  if (!this.turnClaim) throw new Error(t("errorEndpointNotReady"));
17710
- await this.bindSessionEndpoint();
17711
18439
  const sessionKey = this.snapshot.sessionKey;
18440
+ await this.bindSessionEndpoint();
18441
+ if (this.snapshot.sessionKey !== sessionKey) throw new Error(t("errorChatChanged"));
17712
18442
  const clientMessageId = crypto.randomUUID();
17713
18443
  const request = {
17714
18444
  content: text,
17715
18445
  clientMessageId,
17716
18446
  delivery: "next",
17717
18447
  expectedSessionId: this.snapshot.sessionId,
18448
+ ...this.snapshot.modelConfig?.fixedModel ? { configVersion: this.snapshot.modelConfig.configVersion } : {},
17718
18449
  origin: { type: "endpoint", endpointId: this.turnClaim.endpointId, token: this.turnClaim.token },
17719
18450
  ...browserContexts.length ? { browserContexts } : {},
17720
18451
  ...attachments.length ? { attachments } : {}
@@ -17727,7 +18458,7 @@ ${endpointId}`;
17727
18458
  await writeBrowserOutbox(sessionKey, request);
17728
18459
  outboxStored = true;
17729
18460
  this.update({
17730
- messages: [...this.snapshot.messages, {
18461
+ messages: this.snapshot.runId ? this.snapshot.messages : [...this.snapshot.messages, {
17731
18462
  id: clientMessageId,
17732
18463
  role: "user",
17733
18464
  text,
@@ -17740,7 +18471,7 @@ ${endpointId}`;
17740
18471
  }))
17741
18472
  } : {}
17742
18473
  }],
17743
- streamingText: "",
18474
+ ...this.snapshot.runId ? {} : { streamingText: "" },
17744
18475
  error: void 0
17745
18476
  });
17746
18477
  const response = await json(
@@ -17832,6 +18563,49 @@ ${endpointId}`;
17832
18563
  this.recoveringOutbox = false;
17833
18564
  }
17834
18565
  }
18566
+ async refreshInputs() {
18567
+ const requestId = ++this.inputsRequest;
18568
+ const sessionKey = this.snapshot.sessionKey;
18569
+ if (!sessionKey) return;
18570
+ const result = await json(
18571
+ await gatewayFetch(`/api/sessions/${encodeURIComponent(sessionKey)}/input-state`)
18572
+ );
18573
+ if (this.snapshot.sessionKey !== sessionKey || requestId !== this.inputsRequest) return;
18574
+ this.update({ queuedInputs: (result.payload.inputs ?? []).filter((input) => input.status === "queued") });
18575
+ if (result.payload.activeRunId !== this.snapshot.runId) {
18576
+ await this.reloadMessages();
18577
+ if (this.snapshot.sessionKey !== sessionKey || requestId !== this.inputsRequest) return;
18578
+ if (result.payload.activeRunId) await this.followRun(result.payload.activeRunId);
18579
+ else {
18580
+ if (this.runTopic) this.realtime?.unsubscribe(this.runTopic);
18581
+ this.runTopic = void 0;
18582
+ this.update({ runId: void 0, streamingText: "", stopping: false });
18583
+ }
18584
+ }
18585
+ }
18586
+ async editInput(id, version, content) {
18587
+ const key = this.snapshot.sessionKey;
18588
+ if (!key) return;
18589
+ try {
18590
+ await json(await gatewayFetch(`/api/sessions/${encodeURIComponent(key)}/inputs/${encodeURIComponent(id)}`, {
18591
+ method: "PATCH",
18592
+ headers: { "Content-Type": "application/json" },
18593
+ body: JSON.stringify({ version, content })
18594
+ }));
18595
+ } finally {
18596
+ if (this.snapshot.sessionKey === key) await this.refreshInputs();
18597
+ }
18598
+ }
18599
+ async cancelInput(id, version) {
18600
+ const key = this.snapshot.sessionKey;
18601
+ if (!key) return;
18602
+ try {
18603
+ await json(await gatewayFetch(`/api/sessions/${encodeURIComponent(key)}/inputs/${encodeURIComponent(id)}?version=${version}`, { method: "DELETE" }));
18604
+ if (this.snapshot.sessionKey === key) await this.reloadMessages();
18605
+ } finally {
18606
+ if (this.snapshot.sessionKey === key) await this.refreshInputs();
18607
+ }
18608
+ }
17835
18609
  async abort() {
17836
18610
  const runId = this.snapshot.runId;
17837
18611
  if (!runId || this.snapshot.stopping) return;
@@ -17931,12 +18705,16 @@ ${endpointId}`;
17931
18705
  return void 0;
17932
18706
  }
17933
18707
  async followRun(runId) {
18708
+ const topic = `run:${runId}`;
18709
+ if (this.runTopic === topic) return;
18710
+ const sessionKey = this.snapshot.sessionKey;
17934
18711
  if (this.runTopic) this.realtime?.unsubscribe(this.runTopic);
17935
- this.runTopic = `run:${runId}`;
18712
+ this.runTopic = topic;
17936
18713
  const stored = await chrome.storage.session.get(`${CURSOR_PREFIX}${runId}`);
18714
+ if (this.snapshot.sessionKey !== sessionKey || this.runTopic !== topic) return;
17937
18715
  const cursor = typeof stored[`${CURSOR_PREFIX}${runId}`] === "number" ? stored[`${CURSOR_PREFIX}${runId}`] : void 0;
17938
- this.realtime?.subscribe(this.runTopic, cursor);
17939
- this.update({ runId });
18716
+ this.update({ runId, streamingText: "" });
18717
+ this.realtime?.subscribe(topic, cursor);
17940
18718
  }
17941
18719
  async onRealtimeEvent(topic, seq, event, data) {
17942
18720
  if (topic === "gateway") {
@@ -18124,8 +18902,8 @@ ${endpointId}`;
18124
18902
  }
18125
18903
  }
18126
18904
 
18127
- const MAX_BROWSER_ATTACHMENT_BYTES = 6 * 1024 * 1024;
18128
- const MAX_BROWSER_ATTACHMENTS = 5;
18905
+ const MAX_BROWSER_ATTACHMENT_BYTES = 32 * 1024 * 1024;
18906
+ const MAX_BROWSER_ATTACHMENTS = 10;
18129
18907
  const SUPPORTED_DOCUMENT_MIME_TYPES = /* @__PURE__ */ new Set([
18130
18908
  "application/json",
18131
18909
  "application/pdf",
@@ -18134,7 +18912,30 @@ const SUPPORTED_DOCUMENT_MIME_TYPES = /* @__PURE__ */ new Set([
18134
18912
  "text/markdown",
18135
18913
  "text/plain"
18136
18914
  ]);
18137
- const SUPPORTED_DOCUMENT_EXTENSIONS = [".csv", ".json", ".md", ".pdf", ".txt"];
18915
+ const DOCUMENT_MIMES = {
18916
+ pdf: "application/pdf",
18917
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
18918
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
18919
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
18920
+ csv: "text/csv",
18921
+ tsv: "text/tab-separated-values",
18922
+ json: "application/json",
18923
+ md: "text/markdown",
18924
+ txt: "text/plain",
18925
+ html: "text/html",
18926
+ xml: "text/xml",
18927
+ css: "text/css",
18928
+ js: "text/javascript",
18929
+ ts: "text/plain",
18930
+ jsx: "text/plain",
18931
+ tsx: "text/plain",
18932
+ yaml: "text/plain",
18933
+ yml: "text/plain",
18934
+ py: "text/plain",
18935
+ sh: "text/plain",
18936
+ sql: "text/plain"
18937
+ };
18938
+ const BROWSER_ATTACHMENT_ACCEPT = `image/*,${Object.keys(DOCUMENT_MIMES).map((extension) => `.${extension}`).join(",")}`;
18138
18939
  function encodeBase64(bytes) {
18139
18940
  const chunkSize = 32 * 1024;
18140
18941
  let binary = "";
@@ -18151,13 +18952,13 @@ function assertSize(size) {
18151
18952
  function assertSupportedFile(file) {
18152
18953
  const mimeType = file.type.toLowerCase();
18153
18954
  const name = file.name.toLowerCase();
18154
- if (mimeType.startsWith("image/") || SUPPORTED_DOCUMENT_MIME_TYPES.has(mimeType) || SUPPORTED_DOCUMENT_EXTENSIONS.some((extension) => name.endsWith(extension))) return;
18955
+ if (mimeType.startsWith("image/") || SUPPORTED_DOCUMENT_MIME_TYPES.has(mimeType) || Object.keys(DOCUMENT_MIMES).some((extension) => name.endsWith(`.${extension}`))) return;
18155
18956
  throw new Error(t("errorUnsupportedAttachment"));
18156
18957
  }
18157
18958
  async function fileToBrowserAttachment(file) {
18158
18959
  assertSupportedFile(file);
18159
18960
  assertSize(file.size);
18160
- const mimeType = file.type || "application/octet-stream";
18961
+ const mimeType = file.type || DOCUMENT_MIMES[file.name.split(".").pop()?.toLowerCase() ?? ""] || "application/octet-stream";
18161
18962
  return {
18162
18963
  type: mimeType.startsWith("image/") ? "image" : "file",
18163
18964
  mimeType,
@@ -18167,11 +18968,16 @@ async function fileToBrowserAttachment(file) {
18167
18968
  };
18168
18969
  }
18169
18970
  async function captureVisibleScreenshot() {
18170
- const [activeTab] = await chrome.tabs.query({ active: true, currentWindow: true });
18171
- if (activeTab.id === void 0) throw new Error(t("errorNoActivePage"));
18971
+ const tabPromise = chrome.tabs.query({ active: true, currentWindow: true });
18972
+ const [granted, [activeTab]] = await Promise.all([
18973
+ chrome.permissions.request({ origins: ["<all_urls>"] }),
18974
+ tabPromise
18975
+ ]);
18976
+ if (!granted) throw new Error(t("errorScreenshotPermissionRequired"));
18977
+ if (activeTab?.id === void 0) throw new Error(t("errorNoActivePage"));
18172
18978
  const dataUrl = await runWithTabSiteAccess(activeTab.id, async (accessibleTab) => {
18173
18979
  const [currentTab] = await chrome.tabs.query({ active: true, currentWindow: true });
18174
- if (currentTab.id !== accessibleTab.id) throw new Error(t("errorActiveTabChanged"));
18980
+ if (currentTab?.id !== accessibleTab.id) throw new Error(t("errorActiveTabChanged"));
18175
18981
  return chrome.tabs.captureVisibleTab(accessibleTab.windowId, { format: "png" });
18176
18982
  });
18177
18983
  const separator = dataUrl.indexOf(",");
@@ -18221,101 +19027,107 @@ async function listMentionableTabs(query) {
18221
19027
  return filterMentionableTabs(mentionable, query);
18222
19028
  }
18223
19029
 
18224
- function IconBase({ children, ...props }) {
18225
- return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { "aria-hidden": "true", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", ...props, children });
18226
- }
18227
- function ChevronDownIcon(props) {
18228
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m7 10 5 5 5-5" }) });
18229
- }
18230
- function ArrowDownIcon(props) {
18231
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 5v14M6 13l6 6 6-6" }) });
18232
- }
18233
- function PlusIcon(props) {
18234
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 5v14M5 12h14" }) });
18235
- }
18236
- function SearchIcon(props) {
18237
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18238
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "11", cy: "11", r: "7" }),
18239
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m20 20-4-4" })
18240
- ] });
18241
- }
18242
- function PaperclipIcon(props) {
18243
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m20.5 11.5-8.9 8.9a6 6 0 0 1-8.5-8.5l9.5-9.5a4 4 0 0 1 5.7 5.7l-9.5 9.5A2 2 0 0 1 6 14.8l8.8-8.8" }) });
18244
- }
18245
- function PageIcon(props) {
18246
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18247
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" }),
18248
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 2v6h6M8 13h8M8 17h5" })
18249
- ] });
18250
- }
18251
- function SelectionIcon(props) {
18252
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 4H5a1 1 0 0 0-1 1v3M16 4h3a1 1 0 0 1 1 1v3M20 16v3a1 1 0 0 1-1 1h-3M8 20H5a1 1 0 0 1-1-1v-3M8 9h8M8 13h6" }) });
18253
- }
18254
- function CameraIcon(props) {
18255
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18256
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14.5 4 16 7h3a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h3l1.5-3Z" }),
18257
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "13", r: "3" })
18258
- ] });
18259
- }
18260
- function FileIcon(props) {
18261
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18262
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" }),
18263
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M14 2v6h6" })
18264
- ] });
18265
- }
18266
- function EyeIcon(props) {
18267
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18268
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z" }),
18269
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "2.5" })
18270
- ] });
18271
- }
18272
- function CursorIcon(props) {
18273
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m5 3 14 9-6 2-3 6Z" }) });
18274
- }
18275
- function SendIcon(props) {
18276
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m5 12 7-7 7 7M12 19V5" }) });
18277
- }
18278
- function StopIcon(props) {
18279
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "7", y: "7", width: "10", height: "10", rx: "1.5", fill: "currentColor", stroke: "none" }) });
18280
- }
18281
- function CopyIcon(props) {
18282
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18283
- /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "9", y: "9", width: "11", height: "11", rx: "2" }),
18284
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M15 9V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h3" })
18285
- ] });
18286
- }
18287
- function CheckIcon(props) {
18288
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m5 12 4 4L19 6" }) });
18289
- }
18290
- function CloseIcon(props) {
18291
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "m6 6 12 12M18 6 6 18" }) });
18292
- }
18293
- function SparkleIcon(props) {
18294
- return /* @__PURE__ */ jsxRuntimeExports.jsx(IconBase, { ...props, children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 3c.7 4.4 3.6 7.3 8 8-4.4.7-7.3 3.6-8 8-.7-4.4-3.6-7.3-8-8 4.4-.7 7.3-3.6 8-8Z" }) });
18295
- }
18296
- function AlertIcon(props) {
18297
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18298
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "9" }),
18299
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 8v5M12 16.5h.01" })
18300
- ] });
19030
+ const EMPTY_DRAFT = { text: "", attachments: [], pages: [] };
19031
+ async function readDraft(key) {
19032
+ const db = await openDatabase();
19033
+ try {
19034
+ return await new Promise((resolve, reject) => {
19035
+ const request = db.transaction("drafts").objectStore("drafts").get(key);
19036
+ request.onsuccess = () => resolve(request.result);
19037
+ request.onerror = () => reject(request.error);
19038
+ });
19039
+ } finally {
19040
+ db.close();
19041
+ }
18301
19042
  }
18302
- function SettingsIcon(props) {
18303
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18304
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "3" }),
18305
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.86 2.86-.06-.06A1.7 1.7 0 0 0 15 19.4a1.7 1.7 0 0 0-1 .6 1.7 1.7 0 0 0-.4 1.1V21H9.55v-.1A1.7 1.7 0 0 0 8.5 19.4a1.7 1.7 0 0 0-1.88.34l-.06.06-2.86-2.86.06-.06A1.7 1.7 0 0 0 4.1 15a1.7 1.7 0 0 0-.6-1 1.7 1.7 0 0 0-1.1-.4H2V9.55h.4A1.7 1.7 0 0 0 4.1 8.5a1.7 1.7 0 0 0-.34-1.88l-.06-.06L6.56 3.7l.06.06A1.7 1.7 0 0 0 8.5 4.1a1.7 1.7 0 0 0 1-.6 1.7 1.7 0 0 0 .4-1.1V2h4.05v.4A1.7 1.7 0 0 0 15 4.1a1.7 1.7 0 0 0 1.88-.34l.06-.06 2.86 2.86-.06.06A1.7 1.7 0 0 0 19.4 8.5a1.7 1.7 0 0 0 .6 1 1.7 1.7 0 0 0 1.1.4h.4v4.05h-.4A1.7 1.7 0 0 0 19.4 15Z" })
18306
- ] });
19043
+ async function writeDraft(key, draft) {
19044
+ const db = await openDatabase();
19045
+ try {
19046
+ await new Promise((resolve, reject) => {
19047
+ const tx = db.transaction("drafts", "readwrite");
19048
+ const store = tx.objectStore("drafts");
19049
+ if (!draft.text && !draft.attachments.length && !draft.pages.length) store.delete(key);
19050
+ else store.put(draft, key);
19051
+ tx.oncomplete = () => resolve();
19052
+ tx.onerror = () => reject(tx.error);
19053
+ tx.onabort = () => reject(tx.error);
19054
+ });
19055
+ } finally {
19056
+ db.close();
19057
+ }
18307
19058
  }
18308
- function SunIcon(props) {
18309
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18310
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "4" }),
18311
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 2v2M12 20v2M4.93 4.93l1.42 1.42M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.42-1.42M17.66 6.34l1.41-1.41" })
18312
- ] });
19059
+ class ComposerDrafts {
19060
+ constructor(onError, save = writeDraft, read = readDraft) {
19061
+ this.onError = onError;
19062
+ this.save = save;
19063
+ this.read = read;
19064
+ }
19065
+ onError;
19066
+ save;
19067
+ read;
19068
+ records = /* @__PURE__ */ new Map();
19069
+ listeners = /* @__PURE__ */ new Set();
19070
+ pendingWrites = /* @__PURE__ */ new Map();
19071
+ writing = false;
19072
+ subscribe = (listener) => {
19073
+ this.listeners.add(listener);
19074
+ return () => {
19075
+ this.listeners.delete(listener);
19076
+ };
19077
+ };
19078
+ peek = (key) => this.records.get(key);
19079
+ get = (key) => this.records.get(key) ?? EMPTY_DRAFT;
19080
+ async load(key) {
19081
+ if (this.records.has(key)) return;
19082
+ let draft;
19083
+ try {
19084
+ draft = await this.read(key);
19085
+ } catch (cause) {
19086
+ this.onError(cause);
19087
+ }
19088
+ if (this.records.has(key)) return;
19089
+ this.records.set(key, draft ? { ...draft, pages: draft.pages.map((page) => ({ ...page, stale: true })) } : EMPTY_DRAFT);
19090
+ this.listeners.forEach((listener) => listener());
19091
+ }
19092
+ update(key, update) {
19093
+ const draft = update(this.get(key));
19094
+ this.records.set(key, draft);
19095
+ this.listeners.forEach((listener) => listener());
19096
+ this.pendingWrites.set(key, draft);
19097
+ void this.flush();
19098
+ }
19099
+ async flush() {
19100
+ if (this.writing) return;
19101
+ this.writing = true;
19102
+ try {
19103
+ while (this.pendingWrites.size) {
19104
+ const [key, draft] = this.pendingWrites.entries().next().value;
19105
+ this.pendingWrites.delete(key);
19106
+ try {
19107
+ await this.save(key, draft);
19108
+ } catch (cause) {
19109
+ this.onError(cause);
19110
+ }
19111
+ }
19112
+ } finally {
19113
+ this.writing = false;
19114
+ }
19115
+ }
19116
+ invalidate(tabId) {
19117
+ for (const [key, draft] of this.records) {
19118
+ if (draft.pages.some((page) => page.tabId === tabId && !page.stale)) {
19119
+ this.update(key, (current) => ({ ...current, pages: current.pages.map((page) => page.tabId === tabId ? { ...page, stale: true } : page) }));
19120
+ }
19121
+ }
19122
+ }
18313
19123
  }
18314
- function GlobeIcon(props) {
18315
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(IconBase, { ...props, children: [
18316
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "9" }),
18317
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 12h18M12 3a15 15 0 0 1 0 18M12 3a15 15 0 0 0 0 18" })
18318
- ] });
19124
+ function useComposerDrafts(key, onError) {
19125
+ const store = reactExports.useMemo(() => new ComposerDrafts(onError), []);
19126
+ const draft = reactExports.useSyncExternalStore(store.subscribe, () => store.peek(key));
19127
+ reactExports.useEffect(() => {
19128
+ void store.load(key).catch(onError);
19129
+ }, [store, key]);
19130
+ return { store, draft: draft ?? EMPTY_DRAFT, ready: draft !== void 0, update: (update) => store.update(key, update) };
18319
19131
  }
18320
19132
 
18321
19133
  /* eslint-disable no-bitwise */
@@ -26842,30 +27654,41 @@ const RISK_MESSAGE_KEYS = {
26842
27654
  draft: "riskDraft",
26843
27655
  read: "riskRead"
26844
27656
  };
26845
- const THINKING_MESSAGE_KEYS = {
26846
- off: "thinkingOff",
26847
- minimal: "thinkingMinimal",
26848
- low: "thinkingLow",
26849
- medium: "thinkingMedium",
26850
- high: "thinkingHigh",
26851
- xhigh: "thinkingXhigh",
26852
- max: "thinkingMax",
26853
- ultra: "thinkingUltra"
26854
- };
26855
- function thinkingLabel(level) {
26856
- const key = THINKING_MESSAGE_KEYS[level];
26857
- return key ? t(key) : level;
26858
- }
26859
27657
  function formatFileSize(size) {
26860
27658
  if (typeof size !== "number" || !Number.isFinite(size) || size < 0) return "";
26861
27659
  if (size < 1024) return `${size} B`;
26862
27660
  if (size < 1024 * 1024) return `${Math.round(size / 1024)} KB`;
26863
27661
  return `${(size / (1024 * 1024)).toFixed(1)} MB`;
26864
27662
  }
26865
- function ChatPanel() {
27663
+ function ChatPanel({ gatewayId }) {
26866
27664
  const client = reactExports.useMemo(() => new BrowserChatClient(), []);
26867
27665
  const [snapshot, setSnapshot] = reactExports.useState(EMPTY);
26868
- const [draft, setDraft] = reactExports.useState("");
27666
+ const keyForSession = (sessionKey) => JSON.stringify([gatewayId, sessionKey ?? "new"]);
27667
+ const draftKey = keyForSession(snapshot.sessionKey);
27668
+ const { store: drafts, draft: composerDraft, ready: draftReady, update: updateDraft } = useComposerDrafts(draftKey, (cause) => setError(String(cause)));
27669
+ const draft = composerDraft.text;
27670
+ const attachments = composerDraft.attachments;
27671
+ const pageContexts = composerDraft.pages;
27672
+ const [preview, setPreview] = reactExports.useState();
27673
+ const setDraft = (text) => updateDraft((current) => ({ ...current, text }));
27674
+ const setAttachments = (value) => updateDraft((current) => ({ ...current, attachments: typeof value === "function" ? value(current.attachments) : value }));
27675
+ function addPageContext(page) {
27676
+ updateDraft((current) => ({ ...current, pages: appendPageContext(current.pages, page) }));
27677
+ }
27678
+ const submittingRef = reactExports.useRef(false);
27679
+ const composing = reactExports.useRef(false);
27680
+ const historyWalk = reactExports.useRef(void 0);
27681
+ const [sending, setSending] = reactExports.useState(false);
27682
+ const [processing, setProcessing] = reactExports.useState(false);
27683
+ const [voiceBusy, setVoiceBusy] = reactExports.useState(false);
27684
+ const [cursor, setCursor] = reactExports.useState(0);
27685
+ const [commandsOpen, setCommandsOpen] = reactExports.useState(false);
27686
+ const [commands, setCommands] = reactExports.useState([]);
27687
+ const [commandsLoading, setCommandsLoading] = reactExports.useState(false);
27688
+ const [commandIndex, setCommandIndex] = reactExports.useState(0);
27689
+ const commandRange = commandsOpen ? findCommand(draft, cursor) : void 0;
27690
+ const commandItems = commandRange ? commands.filter((item) => `${item.name} ${item.description}`.toLowerCase().includes(commandRange.query.toLowerCase())).slice(0, 12) : [];
27691
+ const canSend = draftReady && !sending && !processing && !voiceBusy && !snapshot.submitting && !snapshot.pendingDelivery && !snapshot.sessionLoading && !snapshot.stopping && snapshot.endpointReady;
26869
27692
  const [search, setSearch] = reactExports.useState("");
26870
27693
  const [menuOpen, setMenuOpen] = reactExports.useState(false);
26871
27694
  const [toolsOpen, setToolsOpen] = reactExports.useState(false);
@@ -26873,8 +27696,6 @@ function ChatPanel() {
26873
27696
  const [copiedMessageId, setCopiedMessageId] = reactExports.useState("");
26874
27697
  const [clarificationAnswer, setClarificationAnswer] = reactExports.useState("");
26875
27698
  const [clarificationSubmitting, setClarificationSubmitting] = reactExports.useState(false);
26876
- const [pageContext, setPageContext] = reactExports.useState();
26877
- const [attachments, setAttachments] = reactExports.useState([]);
26878
27699
  const [tabMention, setTabMention] = reactExports.useState();
26879
27700
  const [mentionTabs, setMentionTabs] = reactExports.useState([]);
26880
27701
  const [mentionIndex, setMentionIndex] = reactExports.useState(0);
@@ -26908,26 +27729,35 @@ function ChatPanel() {
26908
27729
  setAtBottom(isAtBottom);
26909
27730
  }, []);
26910
27731
  reactExports.useEffect(() => {
27732
+ let active = true;
26911
27733
  const unsubscribe = client.subscribe(setSnapshot);
26912
- void client.start().catch((cause) => setError(cause instanceof Error ? cause.message : String(cause)));
27734
+ void client.start().then(async () => {
27735
+ if (!active) return;
27736
+ const stored = await chrome.storage.session.get(PENDING_CONTEXT_KEY);
27737
+ const pending = stored[PENDING_CONTEXT_KEY];
27738
+ if (!pending || !active) return;
27739
+ const key = keyForSession(client.currentSessionKey);
27740
+ await drafts.load(key);
27741
+ if (!active) return;
27742
+ drafts.update(key, (current) => ({ ...current, pages: appendPageContext(current.pages, pending) }));
27743
+ await chrome.storage.session.remove(PENDING_CONTEXT_KEY);
27744
+ }).catch((cause) => {
27745
+ if (active) setError(cause instanceof Error ? cause.message : String(cause));
27746
+ });
26913
27747
  return () => {
27748
+ active = false;
26914
27749
  unsubscribe();
26915
27750
  client.stop();
26916
27751
  };
26917
27752
  }, [client]);
26918
27753
  reactExports.useEffect(() => {
26919
- void chrome.storage.session.get(PENDING_CONTEXT_KEY).then(async (stored) => {
26920
- const pending = stored[PENDING_CONTEXT_KEY];
26921
- if (pending?.context.kind === "browser_page") setPageContext(pending);
26922
- await chrome.storage.session.remove(PENDING_CONTEXT_KEY);
26923
- });
26924
27754
  const onUpdated = (tabId, change) => {
26925
27755
  if (change.url || change.status === "loading") {
26926
- setPageContext((current) => current?.tabId === tabId ? { ...current, stale: true } : current);
27756
+ drafts.invalidate(tabId);
26927
27757
  }
26928
27758
  };
26929
27759
  const onRemoved = (tabId) => {
26930
- setPageContext((current) => current?.tabId === tabId ? { ...current, stale: true } : current);
27760
+ drafts.invalidate(tabId);
26931
27761
  };
26932
27762
  chrome.tabs.onUpdated.addListener(onUpdated);
26933
27763
  chrome.tabs.onRemoved.addListener(onRemoved);
@@ -26992,53 +27822,148 @@ function ChatPanel() {
26992
27822
  document.removeEventListener("keydown", closeOnEscape);
26993
27823
  };
26994
27824
  }, []);
26995
- async function submit(event) {
27825
+ reactExports.useEffect(() => {
27826
+ historyWalk.current = void 0;
27827
+ mentionRequest.current += 1;
27828
+ setTabMention(void 0);
27829
+ setMentionTabs([]);
27830
+ setCommandsOpen(false);
27831
+ setPreview(void 0);
27832
+ setError("");
27833
+ if (!snapshot.sessionKey) return;
27834
+ let active = true;
27835
+ const refresh = () => {
27836
+ if (active) void client.refreshInputs().catch((cause) => {
27837
+ if (active) setError(String(cause));
27838
+ });
27839
+ };
27840
+ refresh();
27841
+ const timer = setInterval(refresh, 2e3);
27842
+ return () => {
27843
+ active = false;
27844
+ clearInterval(timer);
27845
+ };
27846
+ }, [client, snapshot.sessionKey]);
27847
+ reactExports.useEffect(() => {
27848
+ if (!commandsOpen) return;
27849
+ let active = true;
27850
+ setCommandsLoading(true);
27851
+ void loadComposerCommands(snapshot.sessionKey).then((items) => {
27852
+ if (active) setCommands(items);
27853
+ }).catch((cause) => {
27854
+ if (active) {
27855
+ setCommands([]);
27856
+ setError(String(cause));
27857
+ }
27858
+ }).finally(() => {
27859
+ if (active) setCommandsLoading(false);
27860
+ });
27861
+ return () => {
27862
+ active = false;
27863
+ };
27864
+ }, [commandsOpen, snapshot.sessionKey]);
27865
+ function chooseCommand(item) {
27866
+ if (!commandRange || item.disabled) return;
27867
+ const text = `${draft.slice(0, commandRange.start)}${item.wire}${draft.slice(commandRange.end)}`;
27868
+ setDraft(text);
27869
+ setCommandsOpen(false);
27870
+ const nextCursor = commandRange.start + item.wire.length;
27871
+ requestAnimationFrame(() => {
27872
+ textarea.current?.focus();
27873
+ textarea.current?.setSelectionRange(nextCursor, nextCursor);
27874
+ });
27875
+ }
27876
+ async function submit(event, interrupt = false) {
26996
27877
  event.preventDefault();
26997
- if (!draft.trim() && attachments.length === 0) return;
26998
- const sending = draft;
26999
- const sendingAttachments = attachments;
27000
- setDraft("");
27001
- setAttachments([]);
27878
+ if (!canSend || submittingRef.current || composing.current || tabMention || commandRange || attachmentTask.current) return;
27879
+ const payload = drafts.get(draftKey);
27880
+ if (!payload.text.trim() && !payload.attachments.length) return;
27881
+ if (payload.pages.some((page) => page.stale)) {
27882
+ setError(t("errorAttachedPageChanged"));
27883
+ return;
27884
+ }
27885
+ submittingRef.current = true;
27886
+ setSending(true);
27002
27887
  setError("");
27003
27888
  followingTail.current = true;
27004
27889
  setAtBottom(true);
27890
+ let targetKey = draftKey;
27005
27891
  try {
27006
- if (!snapshot.sessionKey) await client.createSession();
27007
- if (pageContext?.stale) throw new Error(t("errorAttachedPageChanged"));
27008
- await client.send(sending, pageContext ? [pageContext.context] : [], sendingAttachments);
27009
- setPageContext(void 0);
27892
+ if (!snapshot.sessionKey) {
27893
+ await client.createSession();
27894
+ targetKey = keyForSession(client.currentSessionKey);
27895
+ drafts.update(targetKey, () => payload);
27896
+ drafts.update(draftKey, () => EMPTY_DRAFT);
27897
+ }
27898
+ if (interrupt && snapshot.runId) await client.abort();
27899
+ await client.send(payload.text, payload.pages.map((page) => page.context), payload.attachments);
27900
+ historyWalk.current = void 0;
27901
+ drafts.update(targetKey, (current) => ({
27902
+ text: current.text === payload.text ? "" : current.text,
27903
+ attachments: current.attachments.filter((item) => !payload.attachments.includes(item)),
27904
+ pages: current.pages.filter((page) => !payload.pages.some((sent) => sent.context.sourceId === page.context.sourceId))
27905
+ }));
27010
27906
  } catch (cause) {
27011
- setDraft(sending);
27012
- setAttachments(sendingAttachments);
27013
27907
  setError(cause instanceof Error ? cause.message : String(cause));
27908
+ } finally {
27909
+ submittingRef.current = false;
27910
+ setSending(false);
27014
27911
  }
27015
27912
  }
27016
- async function addFiles(files) {
27017
- const incoming = Array.from(files ?? []);
27018
- if (!incoming.length || attachmentTask.current) return;
27913
+ async function updateModelConfig(action) {
27914
+ if (submittingRef.current) return;
27915
+ submittingRef.current = true;
27916
+ setSending(true);
27917
+ try {
27918
+ await action();
27919
+ } catch (cause) {
27920
+ setError(String(cause));
27921
+ throw cause;
27922
+ } finally {
27923
+ submittingRef.current = false;
27924
+ setSending(false);
27925
+ }
27926
+ }
27927
+ async function processAttachmentTask(operation) {
27928
+ if (attachmentTask.current || submittingRef.current || !draftReady || snapshot.sessionLoading) return;
27019
27929
  attachmentTask.current = true;
27930
+ setProcessing(true);
27020
27931
  setError("");
27021
27932
  try {
27022
- const remaining = MAX_BROWSER_ATTACHMENTS - attachments.length;
27023
- if (incoming.length > remaining) throw new Error(t("errorAttachmentLimit", String(MAX_BROWSER_ATTACHMENTS)));
27024
- const added = await Promise.all(incoming.map(fileToBrowserAttachment));
27025
- setAttachments((current) => [...current, ...added]);
27933
+ await operation();
27026
27934
  } catch (cause) {
27027
27935
  setError(cause instanceof Error ? cause.message : String(cause));
27028
27936
  } finally {
27029
27937
  attachmentTask.current = false;
27030
- if (fileInput.current) fileInput.current.value = "";
27938
+ setProcessing(false);
27031
27939
  }
27032
27940
  }
27941
+ async function addFiles(files) {
27942
+ const incoming = Array.from(files ?? []);
27943
+ if (!incoming.length) return;
27944
+ await processAttachmentTask(async () => {
27945
+ const remaining = MAX_BROWSER_ATTACHMENTS - drafts.get(draftKey).attachments.length;
27946
+ const failures = [];
27947
+ if (incoming.length > remaining) failures.push(t("errorAttachmentLimit", String(MAX_BROWSER_ATTACHMENTS)));
27948
+ const added = [];
27949
+ for (const file of incoming.slice(0, remaining)) {
27950
+ try {
27951
+ added.push(await fileToBrowserAttachment(file));
27952
+ } catch (cause) {
27953
+ failures.push(`${file.name}: ${cause instanceof Error ? cause.message : String(cause)}`);
27954
+ }
27955
+ }
27956
+ setAttachments((current) => [...current, ...added]);
27957
+ if (fileInput.current) fileInput.current.value = "";
27958
+ if (failures.length) setError(failures.join("\n"));
27959
+ });
27960
+ }
27033
27961
  async function addScreenshot() {
27034
- setError("");
27035
- try {
27036
- if (attachments.length >= MAX_BROWSER_ATTACHMENTS) throw new Error(t("errorAttachmentLimit", String(MAX_BROWSER_ATTACHMENTS)));
27962
+ await processAttachmentTask(async () => {
27963
+ if (drafts.get(draftKey).attachments.length >= MAX_BROWSER_ATTACHMENTS) throw new Error(t("errorAttachmentLimit", String(MAX_BROWSER_ATTACHMENTS)));
27037
27964
  const screenshot = await captureVisibleScreenshot();
27038
27965
  setAttachments((current) => [...current, screenshot]);
27039
- } catch (cause) {
27040
- setError(cause instanceof Error ? cause.message : String(cause));
27041
- }
27966
+ });
27042
27967
  }
27043
27968
  async function runControlAction(action) {
27044
27969
  setError("");
@@ -27048,14 +27973,15 @@ function ChatPanel() {
27048
27973
  setError(cause instanceof Error ? cause.message : String(cause));
27049
27974
  }
27050
27975
  }
27051
- const selectedModel = snapshot.models.find((model) => model.id === snapshot.modelConfig?.model);
27052
- const thinkingOptions = selectedModel?.thinking?.options ?? [];
27053
27976
  async function attachPage(mode) {
27977
+ if (attachmentTask.current || submittingRef.current || !draftReady) return false;
27978
+ attachmentTask.current = true;
27979
+ setProcessing(true);
27054
27980
  setError("");
27055
27981
  try {
27056
27982
  const tabId = await activeTabId();
27057
27983
  if (tabId === void 0) throw new Error(t("errorNoActivePage"));
27058
- setPageContext({
27984
+ addPageContext({
27059
27985
  context: await captureTabWithPermission(tabId, mode),
27060
27986
  tabId,
27061
27987
  source: mode === "selection" ? "current_selection" : "current_page"
@@ -27064,6 +27990,9 @@ function ChatPanel() {
27064
27990
  } catch (cause) {
27065
27991
  setError(cause instanceof Error ? cause.message : String(cause));
27066
27992
  return false;
27993
+ } finally {
27994
+ attachmentTask.current = false;
27995
+ setProcessing(false);
27067
27996
  }
27068
27997
  }
27069
27998
  async function startQuickPrompt(prompt, mode) {
@@ -27081,8 +28010,10 @@ function ChatPanel() {
27081
28010
  setError(cause instanceof Error ? cause.message : String(cause));
27082
28011
  }
27083
28012
  }
27084
- async function refreshPageContext() {
27085
- if (!pageContext) return;
28013
+ async function refreshPageContext(pageContext) {
28014
+ if (attachmentTask.current || submittingRef.current) return;
28015
+ attachmentTask.current = true;
28016
+ setProcessing(true);
27086
28017
  setError("");
27087
28018
  try {
27088
28019
  const mode = pageContext.context.selection ? "selection" : "page";
@@ -27090,14 +28021,20 @@ function ChatPanel() {
27090
28021
  pageContext.tabId,
27091
28022
  mode
27092
28023
  );
27093
- setPageContext({ ...pageContext, context, stale: false });
28024
+ addPageContext({ ...pageContext, context, stale: false });
27094
28025
  } catch (cause) {
27095
28026
  setError(cause instanceof Error ? cause.message : String(cause));
28027
+ } finally {
28028
+ attachmentTask.current = false;
28029
+ setProcessing(false);
27096
28030
  }
27097
28031
  }
27098
- async function updateTabMention(value, cursor) {
28032
+ async function updateTabMention(value, cursor2) {
28033
+ setCursor(cursor2);
28034
+ setCommandsOpen(Boolean(findCommand(value, cursor2)));
28035
+ setCommandIndex(0);
27099
28036
  const requestId = ++mentionRequest.current;
27100
- const mention = findTabMention(value, cursor);
28037
+ const mention = findTabMention(value, cursor2);
27101
28038
  setTabMention(mention);
27102
28039
  setMentionIndex(0);
27103
28040
  if (!mention) {
@@ -27117,7 +28054,9 @@ function ChatPanel() {
27117
28054
  }
27118
28055
  async function chooseMentionedTab(tab) {
27119
28056
  const mention = tabMention;
27120
- if (!mention) return;
28057
+ if (!mention || attachmentTask.current || submittingRef.current) return;
28058
+ attachmentTask.current = true;
28059
+ setProcessing(true);
27121
28060
  mentionRequest.current += 1;
27122
28061
  setTabMention(void 0);
27123
28062
  setMentionTabs([]);
@@ -27125,7 +28064,7 @@ function ChatPanel() {
27125
28064
  try {
27126
28065
  const context = await captureTabWithPermission(tab.id, "page");
27127
28066
  const nextDraft = removeTabMention(draft, mention);
27128
- setPageContext({ context, tabId: tab.id, source: "tab_mention" });
28067
+ addPageContext({ context, tabId: tab.id, source: "tab_mention" });
27129
28068
  setDraft(nextDraft);
27130
28069
  requestAnimationFrame(() => {
27131
28070
  textarea.current?.focus();
@@ -27133,6 +28072,9 @@ function ChatPanel() {
27133
28072
  });
27134
28073
  } catch (cause) {
27135
28074
  setError(cause instanceof Error ? cause.message : String(cause));
28075
+ } finally {
28076
+ attachmentTask.current = false;
28077
+ setProcessing(false);
27136
28078
  }
27137
28079
  }
27138
28080
  async function runSearch(value) {
@@ -27155,6 +28097,7 @@ function ChatPanel() {
27155
28097
  const connectionText = snapshot.endpointReady ? t("ready") : snapshot.connection === "connected" ? t("wakingUp") : snapshot.connection === "connecting" || snapshot.connection === "reconnecting" ? t("connecting") : t("offline");
27156
28098
  const connectionProblem = !snapshot.endpointReady && (snapshot.connection === "error" || snapshot.connection === "disconnected");
27157
28099
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "chat", children: [
28100
+ preview ? /* @__PURE__ */ jsxRuntimeExports.jsx(ComposerPreviewDialog, { preview, onClose: () => setPreview(void 0) }) : null,
27158
28101
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "chat-toolbar", children: [
27159
28102
  /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { ref: sessionTrigger, className: "session-trigger", "aria-expanded": menuOpen, onClick: () => setMenuOpen((open) => !open), children: [
27160
28103
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "session-trigger-copy", children: [
@@ -27166,7 +28109,7 @@ function ChatPanel() {
27166
28109
  ] }),
27167
28110
  /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDownIcon, { className: "icon-sm" })
27168
28111
  ] }),
27169
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "icon-button", type: "button", "aria-label": t("startNewChat"), title: t("newChat"), disabled: snapshot.submitting, onClick: () => void runControlAction(() => client.createSession()), children: /* @__PURE__ */ jsxRuntimeExports.jsx(PlusIcon, {}) })
28112
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "icon-button", type: "button", "aria-label": t("startNewChat"), title: t("newChat"), disabled: sending || snapshot.submitting || snapshot.sessionLoading, onClick: () => void runControlAction(() => client.createSession()), children: /* @__PURE__ */ jsxRuntimeExports.jsx(PlusIcon, {}) })
27170
28113
  ] }),
27171
28114
  menuOpen ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "session-menu", ref: sessionMenu, children: [
27172
28115
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "session-menu-header", children: [
@@ -27178,7 +28121,7 @@ function ChatPanel() {
27178
28121
  /* @__PURE__ */ jsxRuntimeExports.jsx("input", { value: search, onChange: (event) => void runSearch(event.target.value), placeholder: t("searchChats"), autoFocus: true })
27179
28122
  ] }),
27180
28123
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "session-list", children: [
27181
- snapshot.sessions.map((session) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { disabled: snapshot.submitting, className: session.key === snapshot.sessionKey ? "active" : "", onClick: () => {
28124
+ snapshot.sessions.map((session) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { disabled: sending || snapshot.submitting || snapshot.sessionLoading, className: session.key === snapshot.sessionKey ? "active" : "", onClick: () => {
27182
28125
  setMenuOpen(false);
27183
28126
  void runControlAction(() => client.openSession(session.key));
27184
28127
  }, children: [
@@ -27264,6 +28207,9 @@ function ChatPanel() {
27264
28207
  {
27265
28208
  className: `composer${draggingFiles ? " dragging" : ""}`,
27266
28209
  onSubmit: submit,
28210
+ onKeyDown: (event) => {
28211
+ if (event.key === "Enter" && event.target instanceof HTMLInputElement) event.preventDefault();
28212
+ },
27267
28213
  onDragEnter: (event) => {
27268
28214
  if (event.dataTransfer.types.includes("Files")) setDraggingFiles(true);
27269
28215
  },
@@ -27334,13 +28280,15 @@ function ChatPanel() {
27334
28280
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", disabled: clarificationSubmitting, onClick: () => void respondToClarification("cancel"), children: t("cancelTask") })
27335
28281
  ] })
27336
28282
  ] }) : null,
28283
+ processing ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "composer-notice", role: "status", children: t("processingAttachments") }) : null,
28284
+ snapshot.queuedInputs?.map((input) => /* @__PURE__ */ jsxRuntimeExports.jsx(QueuedInput, { input, onSave: (content, version) => client.editInput(input.id, version, content), onCancel: () => client.cancelInput(input.id, input.version) }, `${snapshot.sessionKey}:${input.id}`)),
27337
28285
  snapshot.pendingDelivery ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "composer-notice", role: "status", children: t("queuedMessageNotice") }) : null,
27338
28286
  error || !connectionProblem && snapshot.error ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "composer-error", role: "alert", children: [
27339
28287
  /* @__PURE__ */ jsxRuntimeExports.jsx(AlertIcon, {}),
27340
28288
  error || snapshot.error
27341
28289
  ] }) : null,
27342
- pageContext || attachments.length ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "composer-contexts", children: [
27343
- pageContext ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `context-chip${pageContext.stale ? " stale" : ""}`, children: [
28290
+ pageContexts.length || attachments.length ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "composer-contexts", children: [
28291
+ pageContexts.map((pageContext) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `context-chip${pageContext.stale ? " stale" : ""}`, children: [
27344
28292
  pageContext.context.selection ? /* @__PURE__ */ jsxRuntimeExports.jsx(SelectionIcon, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(PageIcon, {}),
27345
28293
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
27346
28294
  /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: pageContext.source === "tab_mention" ? t("tab") : pageContext.context.selection ? t("selection") : t("page") }),
@@ -27349,15 +28297,21 @@ function ChatPanel() {
27349
28297
  " · ",
27350
28298
  pageContext.context.title
27351
28299
  ] }),
27352
- pageContext.stale ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => void refreshPageContext(), children: t("refresh") }) : null,
27353
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "chip-remove", "aria-label": t("removePageContext"), onClick: () => setPageContext(void 0), children: /* @__PURE__ */ jsxRuntimeExports.jsx(CloseIcon, {}) })
27354
- ] }) : null,
28300
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", onClick: () => setPreview({ title: pageContext.context.title, text: `${pageContext.context.url}
28301
+
28302
+ ${pageContext.context.selection ?? pageContext.context.text ?? ""}` }), children: t("preview") }),
28303
+ pageContext.context.truncated ? /* @__PURE__ */ jsxRuntimeExports.jsx("small", { children: t("truncated") }) : null,
28304
+ pageContext.stale ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", disabled: processing, onClick: () => void refreshPageContext(pageContext), children: t("refresh") }) : null,
28305
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "chip-remove", "aria-label": t("removePageContext"), onClick: () => updateDraft((current) => ({ ...current, pages: current.pages.filter((page) => page.context.sourceId !== pageContext.context.sourceId) })), children: /* @__PURE__ */ jsxRuntimeExports.jsx(CloseIcon, {}) })
28306
+ ] }, pageContext.context.sourceId)),
27355
28307
  attachments.map((attachment, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "attachment-chip", children: [
27356
- attachment.type === "image" ? /* @__PURE__ */ jsxRuntimeExports.jsx(CameraIcon, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(FileIcon, {}),
28308
+ attachment.type === "image" ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "attachment-thumbnail", "aria-label": t("preview"), onClick: () => setPreview({ title: attachment.name, image: `data:${attachment.mimeType};base64,${attachment.data}` }), children: /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: `data:${attachment.mimeType};base64,${attachment.data}`, alt: attachment.name }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(FileIcon, {}),
27357
28309
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
27358
28310
  /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: attachment.type === "image" ? t("image") : t("file") }),
27359
28311
  " · ",
27360
- attachment.name
28312
+ attachment.name,
28313
+ " · ",
28314
+ formatFileSize(attachment.size)
27361
28315
  ] }),
27362
28316
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "chip-remove", "aria-label": t("removeAttachment", attachment.name), onClick: () => setAttachments((current) => current.filter((_, candidate) => candidate !== index)), children: /* @__PURE__ */ jsxRuntimeExports.jsx(CloseIcon, {}) })
27363
28317
  ] }, `${attachment.name}-${index}`))
@@ -27369,7 +28323,7 @@ function ChatPanel() {
27369
28323
  className: "file-input",
27370
28324
  type: "file",
27371
28325
  multiple: true,
27372
- accept: "image/*,.pdf,text/plain,text/markdown,.json,.csv",
28326
+ accept: BROWSER_ATTACHMENT_ACCEPT,
27373
28327
  onChange: (event) => void addFiles(event.target.files)
27374
28328
  }
27375
28329
  ),
@@ -27394,22 +28348,71 @@ function ChatPanel() {
27394
28348
  },
27395
28349
  tab.id
27396
28350
  )) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "tab-mention-empty", children: t("noMatchingTabs") }) }) : null,
28351
+ commandRange ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "tab-mention-menu", role: "listbox", "aria-label": t("commandsAndSkills"), children: [
28352
+ commandsLoading ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "message-skeleton", "aria-label": t("loadingCommands"), children: [
28353
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", {}),
28354
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", {}),
28355
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", {})
28356
+ ] }) : commandItems.map((item, index) => /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", role: "option", "aria-selected": index === commandIndex, disabled: item.disabled, title: item.disabled ? t("skillUnavailable") : item.description, className: index === commandIndex ? "selected" : "", onMouseDown: (event) => {
28357
+ event.preventDefault();
28358
+ chooseCommand(item);
28359
+ }, children: [
28360
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: item.name }),
28361
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("small", { children: [
28362
+ item.description,
28363
+ item.disabled ? ` · ${t("skillUnavailable")}` : ""
28364
+ ] })
28365
+ ] }, item.id)),
28366
+ !commandsLoading && !commandItems.length ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "tab-mention-empty", children: t("noMatchingCommands") }) : null
28367
+ ] }) : null,
27397
28368
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "composer-input-row", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
27398
28369
  "textarea",
27399
28370
  {
27400
28371
  ref: textarea,
27401
28372
  value: draft,
27402
28373
  onChange: (event) => {
28374
+ historyWalk.current = void 0;
27403
28375
  setDraft(event.target.value);
27404
28376
  void updateTabMention(event.target.value, event.target.selectionStart);
27405
28377
  },
27406
28378
  onSelect: (event) => void updateTabMention(event.currentTarget.value, event.currentTarget.selectionStart),
27407
28379
  onPaste: (event) => {
27408
- if (!event.clipboardData.files.length) return;
27409
- event.preventDefault();
27410
- void addFiles(event.clipboardData.files);
28380
+ if (event.clipboardData.files.length) {
28381
+ event.preventDefault();
28382
+ void addFiles(event.clipboardData.files);
28383
+ return;
28384
+ }
28385
+ const pasted = classifyPastedText(event.clipboardData.getData("text/plain"));
28386
+ if (pasted) {
28387
+ event.preventDefault();
28388
+ void addFiles([new File([pasted.text], pasted.name, { type: pasted.mimeType })]);
28389
+ }
28390
+ },
28391
+ onCompositionStart: () => {
28392
+ composing.current = true;
28393
+ },
28394
+ onCompositionEnd: () => {
28395
+ composing.current = false;
27411
28396
  },
27412
28397
  onKeyDown: (event) => {
28398
+ if (composing.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;
28399
+ if (commandRange) {
28400
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
28401
+ event.preventDefault();
28402
+ if (commandItems.length) setCommandIndex((current) => (current + (event.key === "ArrowDown" ? 1 : -1) + commandItems.length) % commandItems.length);
28403
+ return;
28404
+ }
28405
+ if (event.key === "Escape") {
28406
+ event.preventDefault();
28407
+ setCommandsOpen(false);
28408
+ return;
28409
+ }
28410
+ if (event.key === "Enter" || event.key === "Tab") {
28411
+ event.preventDefault();
28412
+ if (commandItems[commandIndex]) chooseCommand(commandItems[commandIndex]);
28413
+ return;
28414
+ }
28415
+ }
27413
28416
  if (tabMention) {
27414
28417
  if (event.key === "ArrowDown" && mentionTabs.length) {
27415
28418
  event.preventDefault();
@@ -27435,18 +28438,35 @@ function ChatPanel() {
27435
28438
  return;
27436
28439
  }
27437
28440
  }
28441
+ if ((event.key === "ArrowUp" || event.key === "ArrowDown") && !event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey) {
28442
+ const input = event.currentTarget;
28443
+ const walk = historyWalk.current;
28444
+ if (walk || event.key === "ArrowUp" && input.selectionStart === 0 && input.selectionEnd === 0) {
28445
+ const history = walk ?? { items: snapshot.messages.filter((message) => message.role === "user" && message.text.trim()).map((message) => message.text).reverse(), index: -1, draft };
28446
+ const index = event.key === "ArrowUp" ? Math.min(history.index + 1, history.items.length - 1) : history.index - 1;
28447
+ if (index >= 0 || walk) {
28448
+ event.preventDefault();
28449
+ historyWalk.current = index >= 0 ? { ...history, index } : void 0;
28450
+ setDraft(index >= 0 ? history.items[index] : history.draft);
28451
+ requestAnimationFrame(() => textarea.current?.setSelectionRange(0, 0));
28452
+ return;
28453
+ }
28454
+ }
28455
+ }
27438
28456
  if (event.key === "Enter" && !event.shiftKey) {
27439
28457
  event.preventDefault();
27440
- event.currentTarget.form?.requestSubmit();
28458
+ if ((event.ctrlKey || event.metaKey) && snapshot.runId) void submit(event, true);
28459
+ else event.currentTarget.form?.requestSubmit();
27441
28460
  }
27442
28461
  },
27443
28462
  onBlur: () => {
28463
+ setCommandsOpen(false);
27444
28464
  mentionRequest.current += 1;
27445
28465
  setTabMention(void 0);
27446
28466
  setMentionTabs([]);
27447
28467
  },
27448
28468
  placeholder: t("composerPlaceholder"),
27449
- disabled: snapshot.submitting,
28469
+ disabled: !draftReady || sending || snapshot.submitting || snapshot.sessionLoading || processing,
27450
28470
  rows: 1
27451
28471
  }
27452
28472
  ) }),
@@ -27545,11 +28565,19 @@ function ChatPanel() {
27545
28565
  ] })
27546
28566
  ] }),
27547
28567
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "composer-toolbar-end", children: [
27548
- snapshot.modelConfig ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "model-controls", children: [
27549
- /* @__PURE__ */ jsxRuntimeExports.jsx("select", { "aria-label": t("model"), title: t("sessionModel"), value: snapshot.modelConfig.model, disabled: Boolean(snapshot.runId), onChange: (event) => void runControlAction(() => client.updateModel(event.target.value)), children: snapshot.models.map((model) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: model.id, children: model.name }, model.id)) }),
27550
- thinkingOptions.length ? /* @__PURE__ */ jsxRuntimeExports.jsx("select", { "aria-label": t("thinkingLevel"), title: t("thinkingLevel"), value: snapshot.modelConfig.thinkingLevel, disabled: Boolean(snapshot.runId), onChange: (event) => void runControlAction(() => client.updateThinking(event.target.value)), children: thinkingOptions.map((level) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: level, children: thinkingLabel(level) }, level)) }) : null
27551
- ] }) : null,
27552
- snapshot.runId ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "send-button stop-button", "aria-label": t("stopResponse"), title: snapshot.stopping ? t("stoppingResponse") : t("stopResponse"), disabled: snapshot.stopping, onClick: () => void runControlAction(() => client.abort()), children: /* @__PURE__ */ jsxRuntimeExports.jsx(StopIcon, {}) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "send-button", type: "submit", "aria-label": t("sendMessage"), title: t("sendMessage"), disabled: snapshot.submitting || snapshot.pendingDelivery || snapshot.sessionLoading || !draft.trim() && attachments.length === 0 || !snapshot.endpointReady || Boolean(tabMention), children: /* @__PURE__ */ jsxRuntimeExports.jsx(SendIcon, {}) })
28568
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ModelControls, { models: snapshot.models, config: snapshot.modelConfig, disabled: sending || processing || voiceBusy || snapshot.submitting || snapshot.sessionLoading || Boolean(snapshot.runId), onModel: (model) => updateModelConfig(async () => {
28569
+ if (!client.currentSessionKey) {
28570
+ const payload = drafts.get(draftKey);
28571
+ await client.createSession();
28572
+ drafts.update(keyForSession(client.currentSessionKey), () => payload);
28573
+ drafts.update(draftKey, () => EMPTY_DRAFT);
28574
+ }
28575
+ await client.updateModel(model);
28576
+ }), onThinking: (level) => updateModelConfig(() => client.updateThinking(level)) }, draftKey),
28577
+ /* @__PURE__ */ jsxRuntimeExports.jsx(VoiceInput, { disabled: sending || processing || snapshot.submitting || snapshot.sessionLoading || !draftReady || !snapshot.endpointReady, onBusy: setVoiceBusy, onTranscript: (text) => updateDraft((current) => ({ ...current, text: current.text.trim() ? `${current.text} ${text}` : text })) }, `voice:${draftKey}`),
28578
+ snapshot.runId && (draft.trim() || attachments.length) ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "composer-icon-button", disabled: !canSend, title: t("interruptSend"), "aria-label": t("interruptSend"), onClick: (event) => void submit(event, true), children: /* @__PURE__ */ jsxRuntimeExports.jsx(SendIcon, {}) }) : null,
28579
+ snapshot.runId ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "send-button stop-button", "aria-label": t("stopResponse"), title: snapshot.stopping ? t("stoppingResponse") : t("stopResponse"), disabled: snapshot.stopping, onClick: () => void runControlAction(() => client.abort()), children: /* @__PURE__ */ jsxRuntimeExports.jsx(StopIcon, {}) }) : null,
28580
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "send-button", type: "submit", "aria-label": snapshot.runId ? t("queueMessage") : t("sendMessage"), title: snapshot.runId ? t("queueMessage") : t("sendMessage"), disabled: !canSend || !draft.trim() && attachments.length === 0 || Boolean(tabMention) || Boolean(commandRange), children: /* @__PURE__ */ jsxRuntimeExports.jsx(SendIcon, {}) })
27553
28581
  ] })
27554
28582
  ] })
27555
28583
  ]
@@ -27809,7 +28837,7 @@ function SidePanelApp({ initialLocale, initialTheme }) {
27809
28837
  invitation ? /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: () => void cancelPairing(), disabled: state === "pairing", children: t("cancelPairing") }) : null
27810
28838
  ] })
27811
28839
  ] }) : null
27812
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChatPanel, {})
28840
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChatPanel, { gatewayId: profile.gatewayId }, profile.gatewayId)
27813
28841
  ] });
27814
28842
  }
27815
28843
 
@@ -27820,5 +28848,5 @@ const [locale, theme] = await Promise.all([
27820
28848
  loadSidePanelThemePreference()
27821
28849
  ]);
27822
28850
  clientExports.createRoot(root).render(
27823
- /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.StrictMode, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(SidePanelApp, { initialLocale: locale, initialTheme: theme }) })
28851
+ /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.StrictMode, { children: new URLSearchParams(location.search).has("microphone-permission") ? /* @__PURE__ */ jsxRuntimeExports.jsx(MicrophonePermission, {}) : /* @__PURE__ */ jsxRuntimeExports.jsx(SidePanelApp, { initialLocale: locale, initialTheme: theme }) })
27824
28852
  );