@agent-native/core 0.113.0 → 0.114.0

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 (1508) hide show
  1. package/agent-native.eject.json +164 -0
  2. package/corpus/README.md +3 -3
  3. package/corpus/core/CHANGELOG.md +11 -0
  4. package/corpus/core/README.md +61 -174
  5. package/corpus/core/agent-native.eject.json +164 -0
  6. package/corpus/core/docs/content/agent-native-toolkit.mdx +38 -0
  7. package/corpus/core/docs/content/package-lifecycle.mdx +54 -4
  8. package/corpus/core/package.json +15 -2
  9. package/corpus/core/src/cli/eject.ts +1594 -0
  10. package/corpus/core/src/cli/index.ts +16 -0
  11. package/corpus/core/src/client/index.ts +4 -0
  12. package/corpus/core/src/client/resources/McpConnectionSuggestion.tsx +8 -2
  13. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +7 -2
  14. package/corpus/core/src/client/resources/ResourcesPanel.tsx +17 -3
  15. package/corpus/core/src/client/resources/index.ts +19 -0
  16. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +9 -1
  17. package/corpus/core/src/client/resources/runtime.ts +1 -0
  18. package/corpus/core/src/client/setup-connections/catalog.ts +6 -0
  19. package/corpus/core/src/client/setup-connections/index.ts +1 -0
  20. package/corpus/core/src/client/setup-connections/runtime.ts +1 -0
  21. package/corpus/core/src/connections/catalog.ts +41 -24
  22. package/corpus/core/src/connections/index.ts +1 -0
  23. package/corpus/core/src/connections/runtime.ts +1 -0
  24. package/corpus/core/src/eject/provider-api-definitions.ts +193 -0
  25. package/corpus/core/src/eject/remote-mcp-presets.ts +137 -0
  26. package/corpus/core/src/eject/setup-readiness-ui.tsx +29 -0
  27. package/corpus/core/src/eject/workspace-connections.ts +92 -0
  28. package/corpus/core/src/index.ts +1 -0
  29. package/corpus/core/src/integrations/adapter-overrides.ts +12 -0
  30. package/corpus/core/src/integrations/eject/messaging-adapters.ts +85 -0
  31. package/corpus/core/src/integrations/index.ts +5 -1
  32. package/corpus/core/src/integrations/plugin.ts +34 -12
  33. package/corpus/core/src/integrations/runtime.ts +1 -0
  34. package/corpus/core/src/integrations/types.ts +3 -1
  35. package/corpus/core/src/package-lifecycle/eject-manifest.ts +345 -0
  36. package/corpus/core/src/package-lifecycle/index.ts +1 -0
  37. package/corpus/core/src/provider-api/index.ts +46 -12
  38. package/corpus/core/src/provider-api/runtime.ts +1 -0
  39. package/corpus/core/src/setup-connections/index.ts +1 -0
  40. package/corpus/core/src/shared/merge-by-id.ts +14 -0
  41. package/corpus/core/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +15 -13
  42. package/corpus/core/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  43. package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +9 -6
  44. package/corpus/core/src/templates/default/AGENTS.md +6 -5
  45. package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +15 -13
  46. package/corpus/core/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  47. package/corpus/core/src/templates/headless/AGENTS.md +5 -4
  48. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +15 -13
  49. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +3 -2
  50. package/corpus/core/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  51. package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +9 -6
  52. package/corpus/core/src/templates/workspace-core/AGENTS.md +4 -4
  53. package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -4
  54. package/corpus/core/src/vite/client.ts +25 -2
  55. package/corpus/templates/analytics/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  56. package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +9 -6
  57. package/corpus/templates/assets/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  58. package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +9 -6
  59. package/corpus/templates/brain/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  60. package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +9 -6
  61. package/corpus/templates/brain/actions/remediate-legacy-capture-audiences.ts +167 -0
  62. package/corpus/templates/brain/app/lib/brain-chat-readiness.ts +6 -0
  63. package/corpus/templates/brain/app/routes/_index.tsx +8 -0
  64. package/corpus/templates/brain/app/routes.ts +1 -0
  65. package/corpus/templates/brain/changelog/2026-07-19-ask-starts-immediately-when-builder-s-managed-ai-connection-.md +6 -0
  66. package/corpus/templates/brain/changelog/2026-07-19-slack-and-granola-backfills-now-process-captures-concurrentl.md +6 -0
  67. package/corpus/templates/brain/changelog/2026-07-19-source-review-settings-now-control-knowledge-approval.md +6 -0
  68. package/corpus/templates/brain/changelog/2026-07-19-the-manage-agent-page-now-opens-correctly-from-brain-navigat.md +6 -0
  69. package/corpus/templates/brain/server/db/schema.ts +3 -0
  70. package/corpus/templates/brain/server/lib/brain.ts +41 -2
  71. package/corpus/templates/brain/server/lib/connectors.ts +379 -113
  72. package/corpus/templates/brain/server/lib/meeting-audience.ts +38 -0
  73. package/corpus/templates/brain/server/plugins/db.ts +10 -0
  74. package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +6 -4
  75. package/corpus/templates/calendar/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  76. package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +9 -6
  77. package/corpus/templates/chat/.agents/skills/agent-native-docs/SKILL.md +15 -13
  78. package/corpus/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  79. package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +9 -6
  80. package/corpus/templates/chat/AGENTS.md +3 -2
  81. package/corpus/templates/clips/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  82. package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +9 -6
  83. package/corpus/templates/content/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  84. package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +9 -6
  85. package/corpus/templates/design/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  86. package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +9 -6
  87. package/corpus/templates/dispatch/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  88. package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +9 -6
  89. package/corpus/templates/forms/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  90. package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +9 -6
  91. package/corpus/templates/macros/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  92. package/corpus/templates/mail/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  93. package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +9 -6
  94. package/corpus/templates/plan/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  95. package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +9 -6
  96. package/corpus/templates/slides/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  97. package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +9 -6
  98. package/corpus/templates/tasks/.agents/skills/agent-native-docs/SKILL.md +15 -13
  99. package/corpus/templates/tasks/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  100. package/corpus/templates/tasks/.agents/skills/self-modifying-code/SKILL.md +9 -6
  101. package/corpus/toolkit/CHANGELOG.md +6 -0
  102. package/corpus/toolkit/agent-native.eject.json +411 -0
  103. package/corpus/toolkit/package.json +5 -2
  104. package/dist/cli/eject.d.ts +21 -0
  105. package/dist/cli/eject.d.ts.map +1 -0
  106. package/dist/cli/eject.js +1145 -0
  107. package/dist/cli/eject.js.map +1 -0
  108. package/dist/cli/index.js +15 -0
  109. package/dist/cli/index.js.map +1 -1
  110. package/dist/client/index.d.ts +1 -1
  111. package/dist/client/index.d.ts.map +1 -1
  112. package/dist/client/index.js +1 -1
  113. package/dist/client/index.js.map +1 -1
  114. package/dist/client/resources/McpConnectionSuggestion.d.ts +4 -3
  115. package/dist/client/resources/McpConnectionSuggestion.d.ts.map +1 -1
  116. package/dist/client/resources/McpConnectionSuggestion.js +3 -3
  117. package/dist/client/resources/McpConnectionSuggestion.js.map +1 -1
  118. package/dist/client/resources/McpIntegrationDialog.d.ts +4 -3
  119. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  120. package/dist/client/resources/McpIntegrationDialog.js +2 -2
  121. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  122. package/dist/client/resources/ResourcesPanel.d.ts +4 -1
  123. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  124. package/dist/client/resources/ResourcesPanel.js +9 -7
  125. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  126. package/dist/client/resources/index.d.ts +3 -0
  127. package/dist/client/resources/index.d.ts.map +1 -1
  128. package/dist/client/resources/index.js +3 -0
  129. package/dist/client/resources/index.js.map +1 -1
  130. package/dist/client/resources/mcp-integration-catalog.d.ts +2 -1
  131. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  132. package/dist/client/resources/mcp-integration-catalog.js +6 -2
  133. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  134. package/dist/client/resources/runtime.d.ts +2 -0
  135. package/dist/client/resources/runtime.d.ts.map +1 -0
  136. package/dist/client/resources/runtime.js +2 -0
  137. package/dist/client/resources/runtime.js.map +1 -0
  138. package/dist/client/setup-connections/catalog.d.ts +2 -0
  139. package/dist/client/setup-connections/catalog.d.ts.map +1 -0
  140. package/dist/client/setup-connections/catalog.js +7 -0
  141. package/dist/client/setup-connections/catalog.js.map +1 -0
  142. package/dist/client/setup-connections/index.d.ts +1 -0
  143. package/dist/client/setup-connections/index.d.ts.map +1 -1
  144. package/dist/client/setup-connections/index.js +1 -0
  145. package/dist/client/setup-connections/index.js.map +1 -1
  146. package/dist/client/setup-connections/runtime.d.ts +2 -0
  147. package/dist/client/setup-connections/runtime.d.ts.map +1 -0
  148. package/dist/client/setup-connections/runtime.js +2 -0
  149. package/dist/client/setup-connections/runtime.js.map +1 -0
  150. package/dist/collab/awareness.d.ts +2 -2
  151. package/dist/collab/awareness.d.ts.map +1 -1
  152. package/dist/collab/routes.d.ts +1 -1
  153. package/dist/connections/catalog.d.ts +5 -3
  154. package/dist/connections/catalog.d.ts.map +1 -1
  155. package/dist/connections/catalog.js +16 -8
  156. package/dist/connections/catalog.js.map +1 -1
  157. package/dist/connections/index.d.ts +1 -1
  158. package/dist/connections/index.d.ts.map +1 -1
  159. package/dist/connections/index.js +1 -1
  160. package/dist/connections/index.js.map +1 -1
  161. package/dist/connections/runtime.d.ts +2 -0
  162. package/dist/connections/runtime.d.ts.map +1 -0
  163. package/dist/connections/runtime.js +2 -0
  164. package/dist/connections/runtime.js.map +1 -0
  165. package/dist/eject/provider-api-definitions.d.ts +34 -0
  166. package/dist/eject/provider-api-definitions.d.ts.map +1 -0
  167. package/dist/eject/provider-api-definitions.js +88 -0
  168. package/dist/eject/provider-api-definitions.js.map +1 -0
  169. package/dist/eject/remote-mcp-presets.d.ts +17 -0
  170. package/dist/eject/remote-mcp-presets.d.ts.map +1 -0
  171. package/dist/eject/remote-mcp-presets.js +81 -0
  172. package/dist/eject/remote-mcp-presets.js.map +1 -0
  173. package/dist/eject/setup-readiness-ui.d.ts +7 -0
  174. package/dist/eject/setup-readiness-ui.d.ts.map +1 -0
  175. package/dist/eject/setup-readiness-ui.js +13 -0
  176. package/dist/eject/setup-readiness-ui.js.map +1 -0
  177. package/dist/eject/workspace-connections.d.ts +11 -0
  178. package/dist/eject/workspace-connections.d.ts.map +1 -0
  179. package/dist/eject/workspace-connections.js +47 -0
  180. package/dist/eject/workspace-connections.js.map +1 -0
  181. package/dist/index.d.ts +1 -1
  182. package/dist/index.d.ts.map +1 -1
  183. package/dist/index.js +1 -1
  184. package/dist/index.js.map +1 -1
  185. package/dist/integrations/adapter-overrides.d.ts +3 -0
  186. package/dist/integrations/adapter-overrides.d.ts.map +1 -0
  187. package/dist/integrations/adapter-overrides.js +7 -0
  188. package/dist/integrations/adapter-overrides.js.map +1 -0
  189. package/dist/integrations/eject/messaging-adapters.d.ts +12 -0
  190. package/dist/integrations/eject/messaging-adapters.d.ts.map +1 -0
  191. package/dist/integrations/eject/messaging-adapters.js +49 -0
  192. package/dist/integrations/eject/messaging-adapters.js.map +1 -0
  193. package/dist/integrations/index.d.ts +3 -2
  194. package/dist/integrations/index.d.ts.map +1 -1
  195. package/dist/integrations/index.js +2 -1
  196. package/dist/integrations/index.js.map +1 -1
  197. package/dist/integrations/plugin.d.ts +32 -1
  198. package/dist/integrations/plugin.d.ts.map +1 -1
  199. package/dist/integrations/plugin.js +19 -12
  200. package/dist/integrations/plugin.js.map +1 -1
  201. package/dist/integrations/runtime.d.ts +2 -0
  202. package/dist/integrations/runtime.d.ts.map +1 -0
  203. package/dist/integrations/runtime.js +2 -0
  204. package/dist/integrations/runtime.js.map +1 -0
  205. package/dist/integrations/types.d.ts +3 -1
  206. package/dist/integrations/types.d.ts.map +1 -1
  207. package/dist/integrations/types.js.map +1 -1
  208. package/dist/notifications/routes.d.ts +3 -3
  209. package/dist/package-lifecycle/eject-manifest.d.ts +43 -0
  210. package/dist/package-lifecycle/eject-manifest.d.ts.map +1 -0
  211. package/dist/package-lifecycle/eject-manifest.js +219 -0
  212. package/dist/package-lifecycle/eject-manifest.js.map +1 -0
  213. package/dist/package-lifecycle/index.d.ts +1 -0
  214. package/dist/package-lifecycle/index.d.ts.map +1 -1
  215. package/dist/package-lifecycle/index.js +1 -0
  216. package/dist/package-lifecycle/index.js.map +1 -1
  217. package/dist/progress/routes.d.ts +1 -1
  218. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  219. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  220. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  221. package/dist/provider-api/index.d.ts +6 -2
  222. package/dist/provider-api/index.d.ts.map +1 -1
  223. package/dist/provider-api/index.js +25 -12
  224. package/dist/provider-api/index.js.map +1 -1
  225. package/dist/provider-api/runtime.d.ts +2 -0
  226. package/dist/provider-api/runtime.d.ts.map +1 -0
  227. package/dist/provider-api/runtime.js +2 -0
  228. package/dist/provider-api/runtime.js.map +1 -0
  229. package/dist/secrets/routes.d.ts +9 -9
  230. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  231. package/dist/server/transcribe-voice.d.ts +1 -1
  232. package/dist/setup-connections/index.d.ts +1 -1
  233. package/dist/setup-connections/index.d.ts.map +1 -1
  234. package/dist/setup-connections/index.js +1 -1
  235. package/dist/setup-connections/index.js.map +1 -1
  236. package/dist/shared/merge-by-id.d.ts +5 -0
  237. package/dist/shared/merge-by-id.d.ts.map +1 -0
  238. package/dist/shared/merge-by-id.js +7 -0
  239. package/dist/shared/merge-by-id.js.map +1 -0
  240. package/dist/templates/chat/.agents/skills/agent-native-docs/SKILL.md +15 -13
  241. package/dist/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  242. package/dist/templates/chat/.agents/skills/self-modifying-code/SKILL.md +9 -6
  243. package/dist/templates/chat/AGENTS.md +3 -2
  244. package/dist/templates/default/.agents/skills/agent-native-docs/SKILL.md +15 -13
  245. package/dist/templates/default/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  246. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +9 -6
  247. package/dist/templates/default/AGENTS.md +6 -5
  248. package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +15 -13
  249. package/dist/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  250. package/dist/templates/headless/AGENTS.md +5 -4
  251. package/dist/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +15 -13
  252. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +3 -2
  253. package/dist/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  254. package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +9 -6
  255. package/dist/templates/workspace-core/AGENTS.md +4 -4
  256. package/dist/templates/workspace-root/AGENTS.md +4 -4
  257. package/dist/vite/client.d.ts.map +1 -1
  258. package/dist/vite/client.js +22 -2
  259. package/dist/vite/client.js.map +1 -1
  260. package/docs/content/agent-native-toolkit.mdx +38 -0
  261. package/docs/content/package-lifecycle.mdx +54 -4
  262. package/package.json +15 -2
  263. package/src/a2a/agent-card.ts +83 -0
  264. package/src/a2a/artifact-response.ts +1648 -0
  265. package/src/a2a/auth-policy.ts +66 -0
  266. package/src/a2a/caller-auth.ts +102 -0
  267. package/src/a2a/client.ts +876 -0
  268. package/src/a2a/correlation.ts +50 -0
  269. package/src/a2a/handlers.ts +1285 -0
  270. package/src/a2a/index.ts +50 -0
  271. package/src/a2a/invoke.ts +348 -0
  272. package/src/a2a/response-text.ts +53 -0
  273. package/src/a2a/server.ts +586 -0
  274. package/src/a2a/task-store.ts +917 -0
  275. package/src/a2a/types.ts +221 -0
  276. package/src/a2a-claims.ts +52 -0
  277. package/src/action-change-marker.ts +74 -0
  278. package/src/action-ui.ts +38 -0
  279. package/src/action.ts +1406 -0
  280. package/src/adapters/cli/index.ts +6 -0
  281. package/src/adapters/cli/registry.ts +73 -0
  282. package/src/adapters/cli/shell-adapter.ts +92 -0
  283. package/src/adapters/cli/types.ts +27 -0
  284. package/src/agent/action-continuation-guidance.ts +38 -0
  285. package/src/agent/app-model-defaults.ts +201 -0
  286. package/src/agent/context-xray/actions/context-evict.ts +56 -0
  287. package/src/agent/context-xray/actions/context-manifest-get.ts +75 -0
  288. package/src/agent/context-xray/actions/context-pin.ts +56 -0
  289. package/src/agent/context-xray/actions/context-preview-get.ts +134 -0
  290. package/src/agent/context-xray/actions/context-report.ts +75 -0
  291. package/src/agent/context-xray/actions/context-restore.ts +51 -0
  292. package/src/agent/context-xray/actions/errors.ts +40 -0
  293. package/src/agent/context-xray/apply-directives.ts +137 -0
  294. package/src/agent/context-xray/directives-store.ts +178 -0
  295. package/src/agent/context-xray/identity.ts +37 -0
  296. package/src/agent/context-xray/manifest.ts +284 -0
  297. package/src/agent/context-xray/migrations.ts +32 -0
  298. package/src/agent/context-xray/plugin.ts +23 -0
  299. package/src/agent/context-xray/schema.ts +17 -0
  300. package/src/agent/context-xray/segments.ts +194 -0
  301. package/src/agent/context-xray/tokenize.ts +93 -0
  302. package/src/agent/default-model.ts +5 -0
  303. package/src/agent/durable-background.ts +547 -0
  304. package/src/agent/engine/ai-sdk-engine.ts +565 -0
  305. package/src/agent/engine/anthropic-engine.ts +320 -0
  306. package/src/agent/engine/builder-engine.ts +1110 -0
  307. package/src/agent/engine/builder-gateway-headers.ts +44 -0
  308. package/src/agent/engine/builtin.ts +122 -0
  309. package/src/agent/engine/context-directives-transform.ts +73 -0
  310. package/src/agent/engine/continuation-dispatch-retry.ts +168 -0
  311. package/src/agent/engine/credential-errors.ts +69 -0
  312. package/src/agent/engine/index.ts +54 -0
  313. package/src/agent/engine/openai-compatible-endpoint.ts +25 -0
  314. package/src/agent/engine/output-tokens.ts +181 -0
  315. package/src/agent/engine/provider-env-vars.ts +69 -0
  316. package/src/agent/engine/registry.ts +853 -0
  317. package/src/agent/engine/tool-call-journal-seed.ts +85 -0
  318. package/src/agent/engine/translate-ai-sdk.ts +407 -0
  319. package/src/agent/engine/translate-anthropic.ts +611 -0
  320. package/src/agent/engine/types.ts +310 -0
  321. package/src/agent/harness/acp-adapter.ts +893 -0
  322. package/src/agent/harness/acp-builtin.ts +70 -0
  323. package/src/agent/harness/ai-sdk-adapter.ts +509 -0
  324. package/src/agent/harness/background.ts +365 -0
  325. package/src/agent/harness/builtin.ts +50 -0
  326. package/src/agent/harness/index.ts +86 -0
  327. package/src/agent/harness/lifecycle.ts +498 -0
  328. package/src/agent/harness/registry.ts +97 -0
  329. package/src/agent/harness/runner.ts +182 -0
  330. package/src/agent/harness/store.ts +466 -0
  331. package/src/agent/harness/translate.ts +130 -0
  332. package/src/agent/harness/types.ts +129 -0
  333. package/src/agent/index.ts +32 -0
  334. package/src/agent/loop-settings.ts +186 -0
  335. package/src/agent/model-config.ts +363 -0
  336. package/src/agent/observational-memory/compactor.ts +78 -0
  337. package/src/agent/observational-memory/config.ts +78 -0
  338. package/src/agent/observational-memory/index.ts +79 -0
  339. package/src/agent/observational-memory/internal-run.ts +89 -0
  340. package/src/agent/observational-memory/message-text.ts +49 -0
  341. package/src/agent/observational-memory/migrations.ts +44 -0
  342. package/src/agent/observational-memory/observer.ts +128 -0
  343. package/src/agent/observational-memory/plugin.ts +35 -0
  344. package/src/agent/observational-memory/prompts.ts +53 -0
  345. package/src/agent/observational-memory/read.ts +131 -0
  346. package/src/agent/observational-memory/reflector.ts +119 -0
  347. package/src/agent/observational-memory/schema.ts +48 -0
  348. package/src/agent/observational-memory/store.ts +275 -0
  349. package/src/agent/observational-memory/types.ts +65 -0
  350. package/src/agent/processors.ts +209 -0
  351. package/src/agent/production-agent.ts +8254 -0
  352. package/src/agent/run-loop-with-resume.ts +394 -0
  353. package/src/agent/run-manager.ts +1825 -0
  354. package/src/agent/run-ownership.ts +75 -0
  355. package/src/agent/run-store.ts +2601 -0
  356. package/src/agent/runtime-context.ts +169 -0
  357. package/src/agent/thread-data-builder.ts +1584 -0
  358. package/src/agent/tool-call-journal.ts +368 -0
  359. package/src/agent/tool-error-redaction.ts +52 -0
  360. package/src/agent/tool-result-images.ts +178 -0
  361. package/src/agent/tool-search.ts +376 -0
  362. package/src/agent/types.ts +372 -0
  363. package/src/agent-native/index.ts +198 -0
  364. package/src/agent-web/config.ts +253 -0
  365. package/src/agent-web/generator.ts +382 -0
  366. package/src/agent-web/index.ts +36 -0
  367. package/src/appearance/actions/change-appearance.ts +35 -0
  368. package/src/application-state/emitter.ts +81 -0
  369. package/src/application-state/handlers.ts +121 -0
  370. package/src/application-state/index.ts +44 -0
  371. package/src/application-state/script-helpers.ts +155 -0
  372. package/src/application-state/store.ts +274 -0
  373. package/src/assets/branding/agent-native.icon/Assets/background.svg +3 -0
  374. package/src/assets/branding/agent-native.icon/Assets/foreground.svg +12 -0
  375. package/src/assets/branding/agent-native.icon/icon.json +49 -0
  376. package/src/assets/branding/favicon.png +0 -0
  377. package/src/assets/branding/favicon.svg +1 -0
  378. package/src/assets/branding/icon-on-dark.svg +10 -0
  379. package/src/assets/branding/icon-on-light.svg +10 -0
  380. package/src/assets/branding/mac-app-icon.svg +1 -0
  381. package/src/assets/branding/slack-bot/agent-native-1024.png +0 -0
  382. package/src/assets/branding/slack-bot/agent-native-512.png +0 -0
  383. package/src/assets/branding/tray-icon.svg +6 -0
  384. package/src/assets/branding/wordmark-on-dark.svg +21 -0
  385. package/src/assets/branding/wordmark-on-light.svg +21 -0
  386. package/src/assets/fonts/LICENSE_LIBERATION +96 -0
  387. package/src/assets/fonts/LICENSE_NOTO_NASKH_ARABIC +93 -0
  388. package/src/assets/fonts/LiberationSans-Bold.ttf +0 -0
  389. package/src/assets/fonts/LiberationSans-Regular.ttf +0 -0
  390. package/src/assets/fonts/NotoNaskhArabic-Variable.ttf +0 -0
  391. package/src/audit/actions/export-audit-events.ts +162 -0
  392. package/src/audit/actions/get-audit-event.ts +24 -0
  393. package/src/audit/actions/list-audit-events.ts +66 -0
  394. package/src/audit/cleanup-job.ts +100 -0
  395. package/src/audit/config.ts +90 -0
  396. package/src/audit/index.ts +43 -0
  397. package/src/audit/record.ts +180 -0
  398. package/src/audit/redact.ts +109 -0
  399. package/src/audit/store.ts +346 -0
  400. package/src/audit/types.ts +141 -0
  401. package/src/automation/index.ts +802 -0
  402. package/src/brand-kit/brand-signals.ts +124 -0
  403. package/src/brand-kit/fig/index.ts +64 -0
  404. package/src/brand-kit/index.ts +72 -0
  405. package/src/brand-kit/types.ts +113 -0
  406. package/src/browser-sessions/actions.ts +302 -0
  407. package/src/browser-sessions/routes.ts +234 -0
  408. package/src/browser-sessions/store.ts +679 -0
  409. package/src/browser-sessions/types.ts +84 -0
  410. package/src/changelog/parse.ts +328 -0
  411. package/src/chat-threads/emitter.ts +22 -0
  412. package/src/chat-threads/schema.ts +43 -0
  413. package/src/chat-threads/store.ts +1311 -0
  414. package/src/checkpoints/index.ts +15 -0
  415. package/src/checkpoints/service.ts +142 -0
  416. package/src/checkpoints/store.ts +153 -0
  417. package/src/cli/add.ts +425 -0
  418. package/src/cli/agent.ts +424 -0
  419. package/src/cli/agents.ts +122 -0
  420. package/src/cli/app-skill.ts +1455 -0
  421. package/src/cli/atomic-json-file.ts +324 -0
  422. package/src/cli/audit-agent-web.ts +324 -0
  423. package/src/cli/changelog.ts +199 -0
  424. package/src/cli/claude-code-participant.ts +515 -0
  425. package/src/cli/code-agent-commands.ts +270 -0
  426. package/src/cli/code-agent-connector.ts +1109 -0
  427. package/src/cli/code-agent-executor.ts +1970 -0
  428. package/src/cli/code-agent-output-smoother.ts +140 -0
  429. package/src/cli/code-agent-runs.concurrent-worker.ts +33 -0
  430. package/src/cli/code-agent-runs.ts +548 -0
  431. package/src/cli/code.ts +1805 -0
  432. package/src/cli/codex-cli-participant.ts +496 -0
  433. package/src/cli/connect.ts +2714 -0
  434. package/src/cli/content-local.ts +442 -0
  435. package/src/cli/context-xray-local.ts +2193 -0
  436. package/src/cli/create-workspace.ts +30 -0
  437. package/src/cli/create.ts +2247 -0
  438. package/src/cli/deploy-build.ts +50 -0
  439. package/src/cli/design-connect.ts +3187 -0
  440. package/src/cli/doctor.ts +472 -0
  441. package/src/cli/eject.ts +1594 -0
  442. package/src/cli/eval.ts +134 -0
  443. package/src/cli/gateway-helpers.ts +70 -0
  444. package/src/cli/index.ts +1217 -0
  445. package/src/cli/info.ts +105 -0
  446. package/src/cli/invoke.ts +288 -0
  447. package/src/cli/mcp-config-writers.ts +857 -0
  448. package/src/cli/mcp.ts +594 -0
  449. package/src/cli/migrate.ts +1813 -0
  450. package/src/cli/migration-codemod.ts +646 -0
  451. package/src/cli/multi-frontier-runs.ts +1952 -0
  452. package/src/cli/package-lifecycle.ts +823 -0
  453. package/src/cli/plan-blocks.ts +143 -0
  454. package/src/cli/plan-local.ts +3189 -0
  455. package/src/cli/plan-publish-store.ts +138 -0
  456. package/src/cli/pr-visual-recap-workflow.ts +2 -0
  457. package/src/cli/recap.ts +5 -0
  458. package/src/cli/setup-agents.ts +120 -0
  459. package/src/cli/skills-content/assets-skill.ts +83 -0
  460. package/src/cli/skills-content/canvas.ts +153 -0
  461. package/src/cli/skills-content/connection.ts +52 -0
  462. package/src/cli/skills-content/content-skill.ts +120 -0
  463. package/src/cli/skills-content/design-exploration-skill.ts +118 -0
  464. package/src/cli/skills-content/design-visual-edit-skill.ts +228 -0
  465. package/src/cli/skills-content/document-quality.ts +196 -0
  466. package/src/cli/skills-content/exemplar.ts +69 -0
  467. package/src/cli/skills-content/help.ts +77 -0
  468. package/src/cli/skills-content/index.ts +22 -0
  469. package/src/cli/skills-content/local-files.ts +100 -0
  470. package/src/cli/skills-content/plan-setup-auth.ts +80 -0
  471. package/src/cli/skills-content/visual-plan-skill.ts +493 -0
  472. package/src/cli/skills-content/visual-recap-skill.ts +553 -0
  473. package/src/cli/skills-content/visualize-repo-skill.ts +93 -0
  474. package/src/cli/skills-content/wireframe.ts +349 -0
  475. package/src/cli/skills.ts +4340 -0
  476. package/src/cli/sync-builder-starter-manifest.ts +631 -0
  477. package/src/cli/telemetry.ts +136 -0
  478. package/src/cli/templates-meta.ts +269 -0
  479. package/src/cli/upgrade.ts +956 -0
  480. package/src/cli/visualize-repo.ts +715 -0
  481. package/src/cli/workspace-dev.ts +1486 -0
  482. package/src/cli/workspacify.ts +258 -0
  483. package/src/client/AgentAskPopover.tsx +89 -0
  484. package/src/client/AgentChatHome.tsx +66 -0
  485. package/src/client/AgentNative.tsx +161 -0
  486. package/src/client/AgentNativeEmbedded.tsx +288 -0
  487. package/src/client/AgentNativeFrame.tsx +151 -0
  488. package/src/client/AgentPanel.tsx +3437 -0
  489. package/src/client/AgentTaskCard.tsx +273 -0
  490. package/src/client/AppearancePicker.tsx +79 -0
  491. package/src/client/AssistantChat.tsx +5793 -0
  492. package/src/client/ClientOnly.tsx +22 -0
  493. package/src/client/CommandMenu.tsx +726 -0
  494. package/src/client/ConnectBuilderCard.tsx +390 -0
  495. package/src/client/DefaultSpinner.tsx +47 -0
  496. package/src/client/ErrorBoundary.tsx +359 -0
  497. package/src/client/ErrorReportActions.tsx +69 -0
  498. package/src/client/FeedbackButton.tsx +553 -0
  499. package/src/client/HighlightedCodeBlock.tsx +173 -0
  500. package/src/client/IframeEmbed.tsx +151 -0
  501. package/src/client/KeepTabOpenNotice.tsx +181 -0
  502. package/src/client/MultiTabAssistantChat.tsx +2576 -0
  503. package/src/client/NewWorkspaceAppFlow.tsx +525 -0
  504. package/src/client/PoweredByBadge.tsx +284 -0
  505. package/src/client/RunStuckBanner.tsx +431 -0
  506. package/src/client/StarfieldBackground.tsx +466 -0
  507. package/src/client/Turnstile.tsx +125 -0
  508. package/src/client/active-run-state.ts +139 -0
  509. package/src/client/agent-chat/index.ts +279 -0
  510. package/src/client/agent-chat-adapter.ts +3692 -0
  511. package/src/client/agent-chat.ts +1233 -0
  512. package/src/client/agent-engine-key.ts +122 -0
  513. package/src/client/agent-page/AgentContextTab.tsx +487 -0
  514. package/src/client/agent-page/AgentEmptyState.tsx +44 -0
  515. package/src/client/agent-page/AgentJobsTab.tsx +441 -0
  516. package/src/client/agent-page/AgentTabFrame.tsx +39 -0
  517. package/src/client/agent-page/AgentTabsPage.tsx +1096 -0
  518. package/src/client/agent-page/types.ts +18 -0
  519. package/src/client/agent-page/use-jobs.ts +150 -0
  520. package/src/client/agent-sidebar-state.ts +166 -0
  521. package/src/client/analytics/index.ts +40 -0
  522. package/src/client/analytics-session.ts +70 -0
  523. package/src/client/analytics.ts +1757 -0
  524. package/src/client/api-path.ts +124 -0
  525. package/src/client/app-providers.tsx +230 -0
  526. package/src/client/appearance.ts +130 -0
  527. package/src/client/application-state.ts +154 -0
  528. package/src/client/assistant-ui-recovery.tsx +205 -0
  529. package/src/client/automation/index.ts +4 -0
  530. package/src/client/automation.ts +22 -0
  531. package/src/client/blocks/AiEditableField.tsx +30 -0
  532. package/src/client/blocks/BlockView.tsx +145 -0
  533. package/src/client/blocks/RegistryBlockDataProvider.tsx +121 -0
  534. package/src/client/blocks/SchemaBlockEditor.tsx +457 -0
  535. package/src/client/blocks/agent.ts +95 -0
  536. package/src/client/blocks/code-block-direction.ts +21 -0
  537. package/src/client/blocks/index.ts +308 -0
  538. package/src/client/blocks/library/AnnotatedCodeBlock.tsx +710 -0
  539. package/src/client/blocks/library/ApiEndpointBlock.tsx +1120 -0
  540. package/src/client/blocks/library/DataModelBlock.tsx +775 -0
  541. package/src/client/blocks/library/DiffBlock.tsx +1854 -0
  542. package/src/client/blocks/library/FileTreeBlock.tsx +868 -0
  543. package/src/client/blocks/library/HighlightedCode.tsx +260 -0
  544. package/src/client/blocks/library/JsonExplorerBlock.tsx +608 -0
  545. package/src/client/blocks/library/MermaidBlock.tsx +362 -0
  546. package/src/client/blocks/library/OpenApiSpecBlock.tsx +998 -0
  547. package/src/client/blocks/library/annotated-code.config.ts +101 -0
  548. package/src/client/blocks/library/annotation-rail.tsx +1020 -0
  549. package/src/client/blocks/library/api-endpoint.config.ts +207 -0
  550. package/src/client/blocks/library/block-copy.ts +399 -0
  551. package/src/client/blocks/library/callout.config.ts +57 -0
  552. package/src/client/blocks/library/callout.tsx +149 -0
  553. package/src/client/blocks/library/checklist.config.ts +56 -0
  554. package/src/client/blocks/library/checklist.tsx +208 -0
  555. package/src/client/blocks/library/code-filename-label.tsx +59 -0
  556. package/src/client/blocks/library/code-highlight.tsx +178 -0
  557. package/src/client/blocks/library/code-tabs.config.ts +70 -0
  558. package/src/client/blocks/library/code-tabs.tsx +606 -0
  559. package/src/client/blocks/library/code.config.ts +69 -0
  560. package/src/client/blocks/library/code.tsx +440 -0
  561. package/src/client/blocks/library/columns.config.ts +85 -0
  562. package/src/client/blocks/library/columns.tsx +334 -0
  563. package/src/client/blocks/library/data-model.config.ts +152 -0
  564. package/src/client/blocks/library/dev-doc-ui.tsx +191 -0
  565. package/src/client/blocks/library/diagram.config.ts +222 -0
  566. package/src/client/blocks/library/diagram.tsx +958 -0
  567. package/src/client/blocks/library/diff.config.ts +115 -0
  568. package/src/client/blocks/library/file-tree.config.ts +92 -0
  569. package/src/client/blocks/library/html.config.ts +79 -0
  570. package/src/client/blocks/library/html.tsx +258 -0
  571. package/src/client/blocks/library/json-explorer.config.ts +74 -0
  572. package/src/client/blocks/library/mermaid.config.ts +49 -0
  573. package/src/client/blocks/library/narrow-container.tsx +37 -0
  574. package/src/client/blocks/library/openapi-spec.config.ts +66 -0
  575. package/src/client/blocks/library/question-form.config.ts +127 -0
  576. package/src/client/blocks/library/question-form.tsx +944 -0
  577. package/src/client/blocks/library/sanitize-html.ts +343 -0
  578. package/src/client/blocks/library/server-specs.ts +338 -0
  579. package/src/client/blocks/library/specs.tsx +308 -0
  580. package/src/client/blocks/library/table.config.ts +61 -0
  581. package/src/client/blocks/library/table.tsx +449 -0
  582. package/src/client/blocks/library/tabs.config.ts +96 -0
  583. package/src/client/blocks/library/tabs.tsx +665 -0
  584. package/src/client/blocks/library/wireframe-icons.ts +210 -0
  585. package/src/client/blocks/library/wireframe-kit.tsx +1850 -0
  586. package/src/client/blocks/library/wireframe.config.ts +539 -0
  587. package/src/client/blocks/library/wireframe.tsx +535 -0
  588. package/src/client/blocks/mdx.ts +364 -0
  589. package/src/client/blocks/provider.tsx +51 -0
  590. package/src/client/blocks/registry.ts +83 -0
  591. package/src/client/blocks/schema-form/introspect.ts +201 -0
  592. package/src/client/blocks/server.ts +170 -0
  593. package/src/client/blocks/types.ts +415 -0
  594. package/src/client/browser-session-bridge.ts +548 -0
  595. package/src/client/browser-tab-id.ts +36 -0
  596. package/src/client/builder-frame.ts +194 -0
  597. package/src/client/builder-mark.tsx +21 -0
  598. package/src/client/changelog/Changelog.tsx +333 -0
  599. package/src/client/changelog/index.ts +1 -0
  600. package/src/client/chat/ChatHistoryList.tsx +7 -0
  601. package/src/client/chat/attachment-adapters.ts +426 -0
  602. package/src/client/chat/connectors.ts +1579 -0
  603. package/src/client/chat/index.ts +160 -0
  604. package/src/client/chat/markdown-renderer.tsx +693 -0
  605. package/src/client/chat/message-components.tsx +1326 -0
  606. package/src/client/chat/repo-helpers.ts +281 -0
  607. package/src/client/chat/run-recovery.tsx +994 -0
  608. package/src/client/chat/runtime.ts +1810 -0
  609. package/src/client/chat/tool-call-display.tsx +1270 -0
  610. package/src/client/chat/tool-render-registry.tsx +144 -0
  611. package/src/client/chat/use-agent-chat-lifecycle-tracking.ts +74 -0
  612. package/src/client/chat/use-reconnect-reader-owner.ts +28 -0
  613. package/src/client/chat/widgets/DataChartRenderer.tsx +242 -0
  614. package/src/client/chat/widgets/DataChartWidget.tsx +51 -0
  615. package/src/client/chat/widgets/DataInsightsWidget.tsx +59 -0
  616. package/src/client/chat/widgets/DataTableWidget.tsx +240 -0
  617. package/src/client/chat/widgets/InlineExtensionWidget.tsx +60 -0
  618. package/src/client/chat/widgets/builtin-tool-renderers.tsx +213 -0
  619. package/src/client/chat/widgets/data-widget-types.ts +1 -0
  620. package/src/client/chat/widgets/inline-extension-result.ts +86 -0
  621. package/src/client/chat-model-groups.ts +171 -0
  622. package/src/client/chat-thread-url.ts +11 -0
  623. package/src/client/chat-view-transition.ts +213 -0
  624. package/src/client/client-action.ts +21 -0
  625. package/src/client/client-bootstrap.ts +7 -0
  626. package/src/client/client-surface.ts +41 -0
  627. package/src/client/clipboard.ts +89 -0
  628. package/src/client/code-agent-chat-adapter.ts +537 -0
  629. package/src/client/collab/index.ts +60 -0
  630. package/src/client/command-navigation/index.ts +15 -0
  631. package/src/client/comments-review/index.ts +1 -0
  632. package/src/client/components/AgentPresenceChip.tsx +4 -0
  633. package/src/client/components/ApiKeySettings.tsx +309 -0
  634. package/src/client/components/CodeAgentIndicator.tsx +59 -0
  635. package/src/client/components/CodeRequiredDialog.tsx +470 -0
  636. package/src/client/components/LiveCursorOverlay.tsx +5 -0
  637. package/src/client/components/MissingKeyCard.tsx +61 -0
  638. package/src/client/components/PresenceBar.tsx +4 -0
  639. package/src/client/components/RecentEditHighlights.tsx +4 -0
  640. package/src/client/components/RemoteSelectionRings.tsx +5 -0
  641. package/src/client/components/icons/AgentNativeIcon.tsx +46 -0
  642. package/src/client/components/ui/dialog.tsx +1 -0
  643. package/src/client/components/ui/dropdown-menu.tsx +1 -0
  644. package/src/client/components/ui/hover-card.tsx +1 -0
  645. package/src/client/components/ui/message-scroller.tsx +131 -0
  646. package/src/client/components/ui/popover.tsx +1 -0
  647. package/src/client/components/ui/sheet.tsx +1 -0
  648. package/src/client/components/ui/tooltip.tsx +1 -0
  649. package/src/client/composer/index.ts +11 -0
  650. package/src/client/composer/runtime-adapters.tsx +123 -0
  651. package/src/client/composer/use-mention-search.ts +7 -0
  652. package/src/client/composer/wired-components.tsx +53 -0
  653. package/src/client/context-xray/ContextMeter.tsx +124 -0
  654. package/src/client/context-xray/ContextSegmentRow.tsx +27 -0
  655. package/src/client/context-xray/ContextTreemap.tsx +24 -0
  656. package/src/client/context-xray/ContextXRayPanel.tsx +59 -0
  657. package/src/client/context-xray/SegmentProvenancePopover.tsx +18 -0
  658. package/src/client/context-xray/format.ts +27 -0
  659. package/src/client/conversation/AgentConversation.tsx +555 -0
  660. package/src/client/conversation/code-agent-transcript.ts +283 -0
  661. package/src/client/conversation/index.ts +22 -0
  662. package/src/client/conversation/types.ts +85 -0
  663. package/src/client/conversation/use-near-bottom-autoscroll.ts +278 -0
  664. package/src/client/create-query-client.ts +86 -0
  665. package/src/client/db-admin/DataGrid.tsx +551 -0
  666. package/src/client/db-admin/DbAdminPage.tsx +218 -0
  667. package/src/client/db-admin/DevDatabaseLink.tsx +16 -0
  668. package/src/client/db-admin/EditableCell.tsx +451 -0
  669. package/src/client/db-admin/FilterBar.tsx +203 -0
  670. package/src/client/db-admin/ResultsGrid.tsx +114 -0
  671. package/src/client/db-admin/RowSidePanel.tsx +337 -0
  672. package/src/client/db-admin/SqlEditor.tsx +840 -0
  673. package/src/client/db-admin/TableBrowser.tsx +212 -0
  674. package/src/client/db-admin/TableEditor.tsx +760 -0
  675. package/src/client/db-admin/cell-format.ts +246 -0
  676. package/src/client/db-admin/changeset.ts +296 -0
  677. package/src/client/db-admin/export-utils.ts +78 -0
  678. package/src/client/db-admin/index.ts +33 -0
  679. package/src/client/db-admin/sql-storage.ts +134 -0
  680. package/src/client/db-admin/storage.ts +68 -0
  681. package/src/client/db-admin/useAgentSync.ts +226 -0
  682. package/src/client/db-admin/useDbAdmin.ts +294 -0
  683. package/src/client/dev-overlay/DevOverlay.tsx +576 -0
  684. package/src/client/dev-overlay/builtins.ts +40 -0
  685. package/src/client/dev-overlay/index.ts +23 -0
  686. package/src/client/dev-overlay/registry.ts +67 -0
  687. package/src/client/dev-overlay/types.ts +64 -0
  688. package/src/client/dev-overlay/use-dev-option.ts +78 -0
  689. package/src/client/dev-overlay/use-dev-overlay-shortcut.ts +31 -0
  690. package/src/client/dynamic-suggestions.ts +455 -0
  691. package/src/client/embed-auth.ts +544 -0
  692. package/src/client/embed.ts +54 -0
  693. package/src/client/error-capture.ts +690 -0
  694. package/src/client/error-format.ts +211 -0
  695. package/src/client/error-reporting.ts +100 -0
  696. package/src/client/extensions/AgentNativeExtensionFrame.e2e-host.tsx +136 -0
  697. package/src/client/extensions/AgentNativeExtensionFrame.tsx +708 -0
  698. package/src/client/extensions/EmbeddedExtension.tsx +604 -0
  699. package/src/client/extensions/ExtensionEditor.tsx +450 -0
  700. package/src/client/extensions/ExtensionQueryErrorState.tsx +47 -0
  701. package/src/client/extensions/ExtensionSlot.tsx +345 -0
  702. package/src/client/extensions/ExtensionViewer.tsx +1502 -0
  703. package/src/client/extensions/ExtensionViewerPage.tsx +32 -0
  704. package/src/client/extensions/ExtensionsListPage.tsx +432 -0
  705. package/src/client/extensions/ExtensionsSidebarSection.tsx +1259 -0
  706. package/src/client/extensions/InlineExtensionFrame.tsx +660 -0
  707. package/src/client/extensions/agent-native-extension-runtime.ts +495 -0
  708. package/src/client/extensions/delete-extension.ts +76 -0
  709. package/src/client/extensions/extension-load-error.ts +26 -0
  710. package/src/client/extensions/extension-order.ts +49 -0
  711. package/src/client/extensions/extension-popularity.ts +61 -0
  712. package/src/client/extensions/iframe-bridge.ts +377 -0
  713. package/src/client/extensions/index.ts +84 -0
  714. package/src/client/extensions/portable-extension.ts +712 -0
  715. package/src/client/feature-flags/FeatureFlagsPanel.tsx +519 -0
  716. package/src/client/feature-flags/helpers.ts +77 -0
  717. package/src/client/feature-flags/index.ts +13 -0
  718. package/src/client/feature-flags/types.ts +29 -0
  719. package/src/client/feature-flags/use-feature-flag.ts +26 -0
  720. package/src/client/feedback-context.ts +90 -0
  721. package/src/client/frame-protocol.ts +168 -0
  722. package/src/client/frame.ts +329 -0
  723. package/src/client/guided-questions.tsx +1152 -0
  724. package/src/client/history/VersionHistoryPanel.tsx +175 -0
  725. package/src/client/history/index.ts +21 -0
  726. package/src/client/history/use-history.ts +114 -0
  727. package/src/client/hooks/index.ts +46 -0
  728. package/src/client/host/index.ts +170 -0
  729. package/src/client/host-bridge.ts +1272 -0
  730. package/src/client/host-tools.ts +190 -0
  731. package/src/client/i18n.tsx +798 -0
  732. package/src/client/index.ts +370 -0
  733. package/src/client/integrations/IntegrationCard.tsx +186 -0
  734. package/src/client/integrations/IntegrationsPanel.tsx +633 -0
  735. package/src/client/integrations/api.ts +295 -0
  736. package/src/client/integrations/index.ts +29 -0
  737. package/src/client/integrations/useIntegrationStatus.ts +52 -0
  738. package/src/client/mcp-app-host.ts +720 -0
  739. package/src/client/mcp-apps/McpAppRenderer.tsx +760 -0
  740. package/src/client/navigation/index.ts +54 -0
  741. package/src/client/notifications/NotificationsBell.tsx +383 -0
  742. package/src/client/notifications/index.ts +1 -0
  743. package/src/client/observability/ObservabilityDashboard.tsx +890 -0
  744. package/src/client/observability/ThumbsFeedback.tsx +160 -0
  745. package/src/client/observability/index.ts +20 -0
  746. package/src/client/observability/useObservability.ts +266 -0
  747. package/src/client/onboarding/OnboardingBanner.tsx +73 -0
  748. package/src/client/onboarding/OnboardingPanel.tsx +1036 -0
  749. package/src/client/onboarding/SetupButton.tsx +64 -0
  750. package/src/client/onboarding/index.ts +25 -0
  751. package/src/client/onboarding/use-onboarding.ts +164 -0
  752. package/src/client/onboarding/use-preview-mode.ts +40 -0
  753. package/src/client/org/InvitationBanner.tsx +148 -0
  754. package/src/client/org/OrgSwitcher.tsx +754 -0
  755. package/src/client/org/RequireActiveOrg.tsx +337 -0
  756. package/src/client/org/TeamPage.tsx +1432 -0
  757. package/src/client/org/hooks.ts +359 -0
  758. package/src/client/org/index.ts +70 -0
  759. package/src/client/org/workspace-app-links.ts +367 -0
  760. package/src/client/org-team/index.ts +1 -0
  761. package/src/client/progress/RunsTray.tsx +680 -0
  762. package/src/client/progress/index.ts +1 -0
  763. package/src/client/require-session.tsx +142 -0
  764. package/src/client/resources/BuiltinCapabilityDetail.tsx +258 -0
  765. package/src/client/resources/McpConnectionSuggestion.tsx +276 -0
  766. package/src/client/resources/McpIntegrationDialog.tsx +808 -0
  767. package/src/client/resources/McpServerDetail.tsx +230 -0
  768. package/src/client/resources/ResourceEditor.tsx +872 -0
  769. package/src/client/resources/ResourceTree.tsx +860 -0
  770. package/src/client/resources/ResourcesPanel.tsx +1960 -0
  771. package/src/client/resources/index.ts +25 -0
  772. package/src/client/resources/mcp-integration-catalog.ts +771 -0
  773. package/src/client/resources/mcp-integration-logos.ts +108 -0
  774. package/src/client/resources/runtime.ts +1 -0
  775. package/src/client/resources/use-builtin-capabilities.ts +113 -0
  776. package/src/client/resources/use-mcp-servers.ts +217 -0
  777. package/src/client/resources/use-resources.ts +402 -0
  778. package/src/client/review/ReviewCommentComposer.tsx +123 -0
  779. package/src/client/review/ReviewStatusBadge.tsx +39 -0
  780. package/src/client/review/ReviewThreadPanel.tsx +625 -0
  781. package/src/client/review/index.ts +38 -0
  782. package/src/client/review/use-review.ts +186 -0
  783. package/src/client/route-chunk-recovery/index.ts +1 -0
  784. package/src/client/route-chunk-recovery.ts +331 -0
  785. package/src/client/route-state.ts +455 -0
  786. package/src/client/route-warmup.tsx +543 -0
  787. package/src/client/session-replay-iframe.e2e-host.tsx +100 -0
  788. package/src/client/session-replay.ts +3417 -0
  789. package/src/client/settings/AgentsSection.tsx +441 -0
  790. package/src/client/settings/AutomationsSection.tsx +516 -0
  791. package/src/client/settings/BackgroundAgentSection.tsx +189 -0
  792. package/src/client/settings/BrowserSection.tsx +110 -0
  793. package/src/client/settings/DemoModeSection.tsx +48 -0
  794. package/src/client/settings/SecretsSection.tsx +875 -0
  795. package/src/client/settings/SettingsPanel.tsx +3162 -0
  796. package/src/client/settings/SettingsSection.tsx +283 -0
  797. package/src/client/settings/SettingsTabsPage.tsx +592 -0
  798. package/src/client/settings/UsageSection.tsx +401 -0
  799. package/src/client/settings/VoiceTranscriptionSection.tsx +960 -0
  800. package/src/client/settings/agent-settings-search.ts +220 -0
  801. package/src/client/settings/index.ts +27 -0
  802. package/src/client/settings/useBuilderStatus.ts +1055 -0
  803. package/src/client/setup-connections/BuilderConnectCard.tsx +89 -0
  804. package/src/client/setup-connections/ProviderReadinessBadge.tsx +73 -0
  805. package/src/client/setup-connections/SetupConnectionsPage.tsx +94 -0
  806. package/src/client/setup-connections/catalog.ts +6 -0
  807. package/src/client/setup-connections/index.ts +47 -0
  808. package/src/client/setup-connections/runtime.ts +1 -0
  809. package/src/client/sharing/ShareButton.tsx +1748 -0
  810. package/src/client/sharing/ShareDialog.tsx +846 -0
  811. package/src/client/sharing/VisibilityBadge.tsx +4 -0
  812. package/src/client/sharing/index.ts +2 -0
  813. package/src/client/sse-event-processor.ts +2002 -0
  814. package/src/client/terminal/AgentTerminal.tsx +488 -0
  815. package/src/client/terminal/index.ts +7 -0
  816. package/src/client/theme.ts +27 -0
  817. package/src/client/tombstone/agent-presence-chip.ts +17 -0
  818. package/src/client/tombstone/editor.ts +265 -0
  819. package/src/client/tombstone/live-cursor-overlay.ts +21 -0
  820. package/src/client/tombstone/presence-bar.ts +17 -0
  821. package/src/client/tombstone/recent-edit-highlights.ts +17 -0
  822. package/src/client/tombstone/remote-selection-rings.ts +21 -0
  823. package/src/client/tombstone/rich-markdown-editor.ts +265 -0
  824. package/src/client/tombstone/ui-dialog.ts +49 -0
  825. package/src/client/tombstone/ui-dropdown-menu.ts +69 -0
  826. package/src/client/tombstone/ui-hover-card.ts +21 -0
  827. package/src/client/tombstone/ui-popover.ts +25 -0
  828. package/src/client/tombstone/ui-sheet.ts +49 -0
  829. package/src/client/tombstone/ui-tooltip.ts +29 -0
  830. package/src/client/tombstone/visual-style-controls.ts +61 -0
  831. package/src/client/tool-cells/BashCell.tsx +164 -0
  832. package/src/client/tool-cells/EditCell.tsx +303 -0
  833. package/src/client/tool-cells/FilesChangedSummary.tsx +208 -0
  834. package/src/client/tool-cells/WriteCell.tsx +145 -0
  835. package/src/client/tool-cells/index.ts +7 -0
  836. package/src/client/tool-display.ts +41 -0
  837. package/src/client/track.ts +57 -0
  838. package/src/client/transcription/BuilderTranscriptionCta.tsx +120 -0
  839. package/src/client/transcription/use-live-transcription.ts +292 -0
  840. package/src/client/ui/index.ts +55 -0
  841. package/src/client/uploads/index.ts +6 -0
  842. package/src/client/uploads/upload-editor-image.ts +79 -0
  843. package/src/client/uploads/use-upload-resource.ts +24 -0
  844. package/src/client/url-scrub.ts +51 -0
  845. package/src/client/use-action.ts +766 -0
  846. package/src/client/use-agent-chat-context.ts +63 -0
  847. package/src/client/use-agent-chat-home-handoff.ts +207 -0
  848. package/src/client/use-agent-chat.ts +53 -0
  849. package/src/client/use-agent-engine-configured.ts +211 -0
  850. package/src/client/use-avatar.ts +127 -0
  851. package/src/client/use-change-version.ts +150 -0
  852. package/src/client/use-chat-models.ts +252 -0
  853. package/src/client/use-chat-threads.ts +1262 -0
  854. package/src/client/use-db-sync.ts +1112 -0
  855. package/src/client/use-demo-mode-status.ts +30 -0
  856. package/src/client/use-dev-mode.ts +177 -0
  857. package/src/client/use-external-value.ts +40 -0
  858. package/src/client/use-pausing-interval.ts +65 -0
  859. package/src/client/use-pinch-zoom.ts +202 -0
  860. package/src/client/use-run-stuck-detection.ts +427 -0
  861. package/src/client/use-send-to-agent-chat.ts +85 -0
  862. package/src/client/use-session.ts +114 -0
  863. package/src/client/utils.ts +7 -0
  864. package/src/client/visual-style-controls.tsx +1 -0
  865. package/src/client/vite-dev-recovery-script.ts +226 -0
  866. package/src/client/voice-provider-status.ts +44 -0
  867. package/src/client/widgets/index.ts +41 -0
  868. package/src/code-agents/background-controller.ts +398 -0
  869. package/src/code-agents/background-run.ts +156 -0
  870. package/src/code-agents/index.ts +95 -0
  871. package/src/code-agents/prompt-attachments.ts +39 -0
  872. package/src/code-agents/transcript-normalizer.ts +750 -0
  873. package/src/code-agents/transcript-order.ts +73 -0
  874. package/src/coding-tools/index.ts +742 -0
  875. package/src/coding-tools/run-code.ts +1662 -0
  876. package/src/coding-tools/sandbox/adapter.ts +82 -0
  877. package/src/coding-tools/sandbox/background.ts +406 -0
  878. package/src/coding-tools/sandbox/executions-store.ts +517 -0
  879. package/src/coding-tools/sandbox/index.ts +132 -0
  880. package/src/coding-tools/sandbox/local-child-process-adapter.ts +217 -0
  881. package/src/collab/agent-identity.ts +5 -0
  882. package/src/collab/agent-presence.ts +341 -0
  883. package/src/collab/awareness-store.ts +196 -0
  884. package/src/collab/awareness.ts +317 -0
  885. package/src/collab/client-struct.ts +366 -0
  886. package/src/collab/client.ts +1205 -0
  887. package/src/collab/emitter.ts +31 -0
  888. package/src/collab/follow-mode.ts +107 -0
  889. package/src/collab/index.ts +151 -0
  890. package/src/collab/json-to-yjs.ts +504 -0
  891. package/src/collab/presence.ts +207 -0
  892. package/src/collab/recent-edits.ts +215 -0
  893. package/src/collab/routes.ts +191 -0
  894. package/src/collab/storage.ts +228 -0
  895. package/src/collab/struct-routes.ts +133 -0
  896. package/src/collab/text-to-yjs.ts +79 -0
  897. package/src/collab/undo.ts +432 -0
  898. package/src/collab/xml-ops.ts +68 -0
  899. package/src/collab/ydoc-manager.ts +544 -0
  900. package/src/command-navigation/actions.ts +100 -0
  901. package/src/command-navigation/index.ts +12 -0
  902. package/src/comments-review/index.ts +1 -0
  903. package/src/connections/catalog.ts +451 -0
  904. package/src/connections/index.ts +48 -0
  905. package/src/connections/reader.ts +911 -0
  906. package/src/connections/runtime.ts +1 -0
  907. package/src/credentials/index.ts +187 -0
  908. package/src/data-programs/actions.ts +335 -0
  909. package/src/data-programs/contract.ts +284 -0
  910. package/src/data-programs/execute.ts +693 -0
  911. package/src/data-programs/index.ts +72 -0
  912. package/src/data-programs/schema.ts +148 -0
  913. package/src/data-programs/store.ts +755 -0
  914. package/src/data-widgets/index.ts +376 -0
  915. package/src/db/client.ts +1627 -0
  916. package/src/db/create-get-db.ts +579 -0
  917. package/src/db/ddl-guard.ts +302 -0
  918. package/src/db/drizzle-config.ts +190 -0
  919. package/src/db/ensure-additive-columns.ts +430 -0
  920. package/src/db/index.ts +57 -0
  921. package/src/db/migrations.ts +691 -0
  922. package/src/db/request-telemetry.ts +154 -0
  923. package/src/db/runtime-diagnostics.ts +331 -0
  924. package/src/db/schema.ts +121 -0
  925. package/src/db/widen-columns.ts +75 -0
  926. package/src/db-admin/agent-tools.ts +202 -0
  927. package/src/db-admin/index.ts +26 -0
  928. package/src/db-admin/operations.ts +803 -0
  929. package/src/db-admin/routes.ts +174 -0
  930. package/src/db-admin/types.ts +110 -0
  931. package/src/demo/browser-state.ts +47 -0
  932. package/src/demo/fetch-interceptor.ts +175 -0
  933. package/src/demo/redact.ts +575 -0
  934. package/src/deploy/build.ts +3935 -0
  935. package/src/deploy/immutable-assets.ts +65 -0
  936. package/src/deploy/route-discovery.ts +458 -0
  937. package/src/deploy/workspace-core.ts +282 -0
  938. package/src/deploy/workspace-deploy.ts +1488 -0
  939. package/src/eject/provider-api-definitions.ts +193 -0
  940. package/src/eject/remote-mcp-presets.ts +137 -0
  941. package/src/eject/setup-readiness-ui.tsx +29 -0
  942. package/src/eject/workspace-connections.ts +92 -0
  943. package/src/embedding/agent.ts +159 -0
  944. package/src/embedding/bridge.ts +297 -0
  945. package/src/embedding/index.ts +43 -0
  946. package/src/embedding/protocol.ts +185 -0
  947. package/src/embedding/react.tsx +319 -0
  948. package/src/embeddings/index.ts +233 -0
  949. package/src/eval/agent-runner.ts +210 -0
  950. package/src/eval/define-eval.ts +55 -0
  951. package/src/eval/index.ts +49 -0
  952. package/src/eval/report.ts +75 -0
  953. package/src/eval/runner.ts +369 -0
  954. package/src/eval/scorer.ts +244 -0
  955. package/src/eval/types.ts +188 -0
  956. package/src/event-bus/bus.ts +140 -0
  957. package/src/event-bus/index.ts +3 -0
  958. package/src/event-bus/registry.ts +79 -0
  959. package/src/event-bus/types.ts +29 -0
  960. package/src/extensions/actions.ts +1636 -0
  961. package/src/extensions/change-marker.ts +54 -0
  962. package/src/extensions/content-patch.ts +468 -0
  963. package/src/extensions/fetch-tool.ts +461 -0
  964. package/src/extensions/html-shell.ts +803 -0
  965. package/src/extensions/local.ts +462 -0
  966. package/src/extensions/path.ts +40 -0
  967. package/src/extensions/proxy-security.ts +171 -0
  968. package/src/extensions/routes.ts +1080 -0
  969. package/src/extensions/schema.ts +269 -0
  970. package/src/extensions/session-replay-iframe.ts +131 -0
  971. package/src/extensions/slots/routes.ts +126 -0
  972. package/src/extensions/slots/schema.ts +87 -0
  973. package/src/extensions/slots/store.ts +450 -0
  974. package/src/extensions/store.ts +1347 -0
  975. package/src/extensions/theme.ts +114 -0
  976. package/src/extensions/url-safety.ts +316 -0
  977. package/src/extensions/web-content.ts +663 -0
  978. package/src/extensions/web-search-tool.ts +437 -0
  979. package/src/feature-flags/a2a-action-route.ts +56 -0
  980. package/src/feature-flags/actions/get-feature-flags.ts +24 -0
  981. package/src/feature-flags/actions/list-feature-flags.ts +69 -0
  982. package/src/feature-flags/actions/set-feature-flag.ts +89 -0
  983. package/src/feature-flags/index.ts +21 -0
  984. package/src/feature-flags/permissions.ts +53 -0
  985. package/src/feature-flags/plugin.ts +44 -0
  986. package/src/feature-flags/registry.ts +96 -0
  987. package/src/feature-flags/store.ts +176 -0
  988. package/src/file-upload/actions/upload-image.ts +213 -0
  989. package/src/file-upload/builder.ts +431 -0
  990. package/src/file-upload/index.ts +24 -0
  991. package/src/file-upload/pre-upload-attachments.ts +305 -0
  992. package/src/file-upload/registry.ts +125 -0
  993. package/src/file-upload/types.ts +89 -0
  994. package/src/guards/db-tool-scoping.ts +129 -0
  995. package/src/guards/index.ts +23 -0
  996. package/src/guards/no-drizzle-push.ts +116 -0
  997. package/src/guards/no-env-credentials.ts +229 -0
  998. package/src/guards/no-env-mutation.ts +114 -0
  999. package/src/guards/no-localhost-fallback.ts +124 -0
  1000. package/src/guards/no-unscoped-credentials.ts +227 -0
  1001. package/src/guards/no-unscoped-queries.ts +834 -0
  1002. package/src/guards/scan-utils.ts +105 -0
  1003. package/src/guards/types.ts +30 -0
  1004. package/src/history/actions/create-resource-version.ts +92 -0
  1005. package/src/history/actions/get-resource-version.ts +60 -0
  1006. package/src/history/actions/list-resource-history.ts +53 -0
  1007. package/src/history/actions/list-resource-versions.ts +43 -0
  1008. package/src/history/actions/restore-resource-version.ts +96 -0
  1009. package/src/history/index.ts +26 -0
  1010. package/src/history/registry.ts +111 -0
  1011. package/src/history/store.ts +379 -0
  1012. package/src/history/types.ts +69 -0
  1013. package/src/index.browser.ts +155 -0
  1014. package/src/index.ts +408 -0
  1015. package/src/ingestion/docx.ts +158 -0
  1016. package/src/ingestion/figma-node-to-html.ts +1860 -0
  1017. package/src/ingestion/figma.ts +548 -0
  1018. package/src/ingestion/index.ts +107 -0
  1019. package/src/ingestion/media.ts +264 -0
  1020. package/src/ingestion/notion.ts +121 -0
  1021. package/src/ingestion/office.ts +213 -0
  1022. package/src/ingestion/orchestration.ts +160 -0
  1023. package/src/ingestion/pptx.ts +331 -0
  1024. package/src/ingestion/selection.ts +81 -0
  1025. package/src/ingestion/website.ts +151 -0
  1026. package/src/integrations/a2a-continuation-marker.ts +2 -0
  1027. package/src/integrations/a2a-continuation-processor.ts +888 -0
  1028. package/src/integrations/a2a-continuations-store.ts +643 -0
  1029. package/src/integrations/adapter-overrides.ts +12 -0
  1030. package/src/integrations/adapters/discord.ts +364 -0
  1031. package/src/integrations/adapters/email.ts +1164 -0
  1032. package/src/integrations/adapters/google-docs.ts +371 -0
  1033. package/src/integrations/adapters/index.ts +29 -0
  1034. package/src/integrations/adapters/microsoft-teams.ts +413 -0
  1035. package/src/integrations/adapters/slack.ts +1914 -0
  1036. package/src/integrations/adapters/telegram.ts +386 -0
  1037. package/src/integrations/adapters/whatsapp.ts +358 -0
  1038. package/src/integrations/awaiting-input-store.ts +130 -0
  1039. package/src/integrations/catalog.ts +571 -0
  1040. package/src/integrations/computer-supervision-store.ts +423 -0
  1041. package/src/integrations/computer-supervision.ts +213 -0
  1042. package/src/integrations/config-store.ts +131 -0
  1043. package/src/integrations/controls-store.ts +187 -0
  1044. package/src/integrations/eject/messaging-adapters.ts +85 -0
  1045. package/src/integrations/google-docs-poller.ts +671 -0
  1046. package/src/integrations/identity-links-store.ts +210 -0
  1047. package/src/integrations/identity.ts +196 -0
  1048. package/src/integrations/index.ts +220 -0
  1049. package/src/integrations/installations-store.ts +677 -0
  1050. package/src/integrations/integration-memory.ts +187 -0
  1051. package/src/integrations/internal-token.ts +106 -0
  1052. package/src/integrations/pending-tasks-retry-job.ts +286 -0
  1053. package/src/integrations/pending-tasks-store.ts +468 -0
  1054. package/src/integrations/plugin.ts +3048 -0
  1055. package/src/integrations/remote-commands-store.ts +690 -0
  1056. package/src/integrations/remote-devices-store.ts +492 -0
  1057. package/src/integrations/remote-json-safety.ts +127 -0
  1058. package/src/integrations/remote-push-delivery-job.ts +48 -0
  1059. package/src/integrations/remote-push-delivery.ts +370 -0
  1060. package/src/integrations/remote-push-store.ts +664 -0
  1061. package/src/integrations/remote-retry-job.ts +55 -0
  1062. package/src/integrations/remote-run-events-store.ts +222 -0
  1063. package/src/integrations/remote-types.ts +186 -0
  1064. package/src/integrations/runtime.ts +1 -0
  1065. package/src/integrations/scope-store.ts +498 -0
  1066. package/src/integrations/slack-manifest.ts +78 -0
  1067. package/src/integrations/slack-oauth.ts +344 -0
  1068. package/src/integrations/task-queue-stats.ts +144 -0
  1069. package/src/integrations/thread-mapping-store.ts +134 -0
  1070. package/src/integrations/types.ts +485 -0
  1071. package/src/integrations/usage-budget-store.ts +906 -0
  1072. package/src/integrations/webhook-delivery.ts +55 -0
  1073. package/src/integrations/webhook-handler.ts +1939 -0
  1074. package/src/jobs/actions/list-recurring-jobs.ts +105 -0
  1075. package/src/jobs/actions/manage-recurring-job.ts +85 -0
  1076. package/src/jobs/cron.ts +126 -0
  1077. package/src/jobs/index.ts +9 -0
  1078. package/src/jobs/scheduler.ts +703 -0
  1079. package/src/jobs/tools.ts +367 -0
  1080. package/src/local-artifacts/index.ts +1372 -0
  1081. package/src/localization/actions/get-localization-preference.ts +24 -0
  1082. package/src/localization/actions/set-localization-preference.ts +32 -0
  1083. package/src/localization/default-messages.ts +1009 -0
  1084. package/src/localization/index.ts +2 -0
  1085. package/src/localization/server.ts +131 -0
  1086. package/src/localization/shared.ts +208 -0
  1087. package/src/mcp/actions/call-mcp-tool.ts +19 -0
  1088. package/src/mcp/actions/create-org-service-token.ts +87 -0
  1089. package/src/mcp/actions/list-mcp-tools.ts +18 -0
  1090. package/src/mcp/actions/list-org-service-tokens.ts +44 -0
  1091. package/src/mcp/actions/revoke-org-service-token.ts +30 -0
  1092. package/src/mcp/actions/service-token-access.ts +121 -0
  1093. package/src/mcp/ask-app-inline-tasks.ts +125 -0
  1094. package/src/mcp/build-server.ts +2356 -0
  1095. package/src/mcp/builtin-tools.ts +1506 -0
  1096. package/src/mcp/connect-route.ts +1460 -0
  1097. package/src/mcp/connect-store.ts +680 -0
  1098. package/src/mcp/embed-app.ts +2051 -0
  1099. package/src/mcp/embed-route.ts +84 -0
  1100. package/src/mcp/external-agent-policy.ts +18 -0
  1101. package/src/mcp/index.ts +47 -0
  1102. package/src/mcp/oauth-route.ts +1016 -0
  1103. package/src/mcp/oauth-store.ts +581 -0
  1104. package/src/mcp/oauth-token.ts +227 -0
  1105. package/src/mcp/org-directory.ts +343 -0
  1106. package/src/mcp/route-paths.ts +34 -0
  1107. package/src/mcp/screen-memory-stdio.ts +290 -0
  1108. package/src/mcp/server.ts +507 -0
  1109. package/src/mcp/stdio.ts +328 -0
  1110. package/src/mcp/workspace-resolve.ts +261 -0
  1111. package/src/mcp-client/app-api.ts +136 -0
  1112. package/src/mcp-client/app-result.ts +64 -0
  1113. package/src/mcp-client/builtin-capabilities.ts +146 -0
  1114. package/src/mcp-client/builtin-store.ts +93 -0
  1115. package/src/mcp-client/config.ts +281 -0
  1116. package/src/mcp-client/errors.ts +54 -0
  1117. package/src/mcp-client/hub-client.ts +172 -0
  1118. package/src/mcp-client/hub-routes.ts +198 -0
  1119. package/src/mcp-client/index.ts +504 -0
  1120. package/src/mcp-client/manager.ts +968 -0
  1121. package/src/mcp-client/oauth-client.ts +462 -0
  1122. package/src/mcp-client/oauth-routes.ts +307 -0
  1123. package/src/mcp-client/remote-store.ts +674 -0
  1124. package/src/mcp-client/remote-url.ts +97 -0
  1125. package/src/mcp-client/routes.ts +1093 -0
  1126. package/src/mcp-client/screen-memory-local.ts +461 -0
  1127. package/src/mcp-client/tool-policy.ts +182 -0
  1128. package/src/mcp-client/visibility.ts +56 -0
  1129. package/src/mcp-client/workspace-servers.ts +379 -0
  1130. package/src/navigation/index.ts +170 -0
  1131. package/src/notifications/actions.ts +151 -0
  1132. package/src/notifications/channels.ts +410 -0
  1133. package/src/notifications/index.ts +22 -0
  1134. package/src/notifications/registry.ts +216 -0
  1135. package/src/notifications/routes.ts +106 -0
  1136. package/src/notifications/store.ts +236 -0
  1137. package/src/notifications/types.ts +52 -0
  1138. package/src/oauth-tokens/google-refresh.ts +175 -0
  1139. package/src/oauth-tokens/index.ts +15 -0
  1140. package/src/oauth-tokens/store.ts +384 -0
  1141. package/src/observability/cleanup-job.ts +124 -0
  1142. package/src/observability/evals.ts +507 -0
  1143. package/src/observability/experiments.ts +359 -0
  1144. package/src/observability/feedback.ts +332 -0
  1145. package/src/observability/hosted-model-experiment.ts +118 -0
  1146. package/src/observability/index.ts +62 -0
  1147. package/src/observability/plugin.ts +31 -0
  1148. package/src/observability/routes.ts +431 -0
  1149. package/src/observability/sentiment.ts +261 -0
  1150. package/src/observability/store.ts +1347 -0
  1151. package/src/observability/traces.ts +789 -0
  1152. package/src/observability/tracing.ts +157 -0
  1153. package/src/observability/tracking-identity.ts +55 -0
  1154. package/src/observability/types.ts +211 -0
  1155. package/src/onboarding/default-steps.ts +428 -0
  1156. package/src/onboarding/index.ts +22 -0
  1157. package/src/onboarding/plugin.ts +262 -0
  1158. package/src/onboarding/registry.ts +47 -0
  1159. package/src/onboarding/types.ts +96 -0
  1160. package/src/org/accept-pending.ts +98 -0
  1161. package/src/org/auto-join-domain.ts +113 -0
  1162. package/src/org/context.ts +614 -0
  1163. package/src/org/free-email-providers.ts +124 -0
  1164. package/src/org/handlers.ts +1153 -0
  1165. package/src/org/index.ts +63 -0
  1166. package/src/org/migrations.ts +105 -0
  1167. package/src/org/permissions.ts +30 -0
  1168. package/src/org/plugin.ts +255 -0
  1169. package/src/org/schema.ts +28 -0
  1170. package/src/org/types.ts +57 -0
  1171. package/src/org-team/index.ts +1 -0
  1172. package/src/package-lifecycle/deprecated-imports.ts +179 -0
  1173. package/src/package-lifecycle/eject-manifest.ts +345 -0
  1174. package/src/package-lifecycle/index.ts +7 -0
  1175. package/src/package-lifecycle/manifest.ts +120 -0
  1176. package/src/package-lifecycle/migration-manifest.ts +159 -0
  1177. package/src/package-lifecycle/migration-message.ts +6 -0
  1178. package/src/package-lifecycle/tombstone.ts +58 -0
  1179. package/src/package-lifecycle/upgrade-error.ts +37 -0
  1180. package/src/private-blob/index.ts +18 -0
  1181. package/src/private-blob/registry.ts +242 -0
  1182. package/src/private-blob/types.ts +55 -0
  1183. package/src/progress/actions.ts +179 -0
  1184. package/src/progress/index.ts +16 -0
  1185. package/src/progress/registry.ts +127 -0
  1186. package/src/progress/routes.ts +89 -0
  1187. package/src/progress/store.ts +353 -0
  1188. package/src/progress/types.ts +59 -0
  1189. package/src/provider-api/actions/custom-provider-registration.ts +197 -0
  1190. package/src/provider-api/actions/delete-staged-dataset.ts +5 -0
  1191. package/src/provider-api/actions/github-repo-files.ts +266 -0
  1192. package/src/provider-api/actions/list-staged-datasets.ts +5 -0
  1193. package/src/provider-api/actions/provider-api-audit.ts +88 -0
  1194. package/src/provider-api/actions/provider-api.ts +460 -0
  1195. package/src/provider-api/actions/query-staged-dataset.ts +5 -0
  1196. package/src/provider-api/actions/staged-datasets.ts +359 -0
  1197. package/src/provider-api/corpus-jobs-store.ts +522 -0
  1198. package/src/provider-api/corpus-jobs.ts +1817 -0
  1199. package/src/provider-api/custom-registry.ts +456 -0
  1200. package/src/provider-api/github-repo.ts +675 -0
  1201. package/src/provider-api/gong.ts +108 -0
  1202. package/src/provider-api/index.ts +5444 -0
  1203. package/src/provider-api/quota-governor.ts +562 -0
  1204. package/src/provider-api/runtime.ts +1 -0
  1205. package/src/provider-api/staged-datasets-aggregate.ts +304 -0
  1206. package/src/provider-api/staged-datasets-store.ts +441 -0
  1207. package/src/provider-api/staging.ts +483 -0
  1208. package/src/resources/agents.ts +65 -0
  1209. package/src/resources/emitter.ts +54 -0
  1210. package/src/resources/handlers.ts +728 -0
  1211. package/src/resources/index.ts +4 -0
  1212. package/src/resources/metadata.ts +252 -0
  1213. package/src/resources/script-helpers.ts +160 -0
  1214. package/src/resources/store.ts +1618 -0
  1215. package/src/review/actions/consume-review-feedback.ts +48 -0
  1216. package/src/review/actions/create-review-comment.ts +108 -0
  1217. package/src/review/actions/delete-review-comment.ts +67 -0
  1218. package/src/review/actions/get-review-feedback.ts +49 -0
  1219. package/src/review/actions/list-review-comments.ts +87 -0
  1220. package/src/review/actions/reply-review-comment.ts +118 -0
  1221. package/src/review/actions/resolve-review-thread.ts +94 -0
  1222. package/src/review/actions/send-review-thread-to-agent.ts +62 -0
  1223. package/src/review/actions/set-review-status.ts +58 -0
  1224. package/src/review/identity.ts +101 -0
  1225. package/src/review/index.ts +45 -0
  1226. package/src/review/mentions.ts +44 -0
  1227. package/src/review/registry.ts +111 -0
  1228. package/src/review/store.ts +997 -0
  1229. package/src/review/types.ts +101 -0
  1230. package/src/router/index.ts +24 -0
  1231. package/src/scripts/agent-engines/list-agent-engines.ts +130 -0
  1232. package/src/scripts/agent-engines/manage-agent-engine.ts +197 -0
  1233. package/src/scripts/agent-engines/set-agent-engine.ts +84 -0
  1234. package/src/scripts/agent-engines/test-agent-engine.ts +174 -0
  1235. package/src/scripts/call-agent.ts +792 -0
  1236. package/src/scripts/chat/index.ts +8 -0
  1237. package/src/scripts/chat/open-chat.ts +56 -0
  1238. package/src/scripts/chat/search-chats.ts +121 -0
  1239. package/src/scripts/core-scripts.ts +22 -0
  1240. package/src/scripts/db/check-scoping.ts +212 -0
  1241. package/src/scripts/db/exec.ts +791 -0
  1242. package/src/scripts/db/index.ts +19 -0
  1243. package/src/scripts/db/migrate-encrypt-credentials.ts +223 -0
  1244. package/src/scripts/db/migrate-encrypt-oauth-tokens.ts +213 -0
  1245. package/src/scripts/db/migrate-user-api-keys.ts +257 -0
  1246. package/src/scripts/db/patch.ts +820 -0
  1247. package/src/scripts/db/query.ts +299 -0
  1248. package/src/scripts/db/reset-dev-owner.ts +285 -0
  1249. package/src/scripts/db/safety.ts +286 -0
  1250. package/src/scripts/db/schema.ts +364 -0
  1251. package/src/scripts/db/scoping.ts +341 -0
  1252. package/src/scripts/db/sqlite-client.ts +78 -0
  1253. package/src/scripts/db/tool-mode.ts +23 -0
  1254. package/src/scripts/db/tool-schemas.ts +31 -0
  1255. package/src/scripts/db/wipe-leaked-builder-keys.ts +201 -0
  1256. package/src/scripts/dev/index.ts +296 -0
  1257. package/src/scripts/dev/list-files.ts +119 -0
  1258. package/src/scripts/dev/read-file.ts +77 -0
  1259. package/src/scripts/dev/search-files.ts +151 -0
  1260. package/src/scripts/dev/shell.ts +111 -0
  1261. package/src/scripts/dev/write-file.ts +56 -0
  1262. package/src/scripts/dev-session.ts +96 -0
  1263. package/src/scripts/docs/index.ts +8 -0
  1264. package/src/scripts/docs/search.ts +274 -0
  1265. package/src/scripts/docs/source-search.ts +363 -0
  1266. package/src/scripts/index.ts +15 -0
  1267. package/src/scripts/manage-agent-loop-settings.ts +84 -0
  1268. package/src/scripts/parse-args.ts +46 -0
  1269. package/src/scripts/resources/delete-memory.ts +67 -0
  1270. package/src/scripts/resources/delete.ts +69 -0
  1271. package/src/scripts/resources/effective.ts +71 -0
  1272. package/src/scripts/resources/index.ts +18 -0
  1273. package/src/scripts/resources/list.ts +103 -0
  1274. package/src/scripts/resources/migrate-learnings.ts +40 -0
  1275. package/src/scripts/resources/read.ts +107 -0
  1276. package/src/scripts/resources/save-memory.ts +99 -0
  1277. package/src/scripts/resources/write.ts +158 -0
  1278. package/src/scripts/runner.ts +339 -0
  1279. package/src/scripts/utils.ts +130 -0
  1280. package/src/search/index.ts +413 -0
  1281. package/src/search-utils/index.ts +112 -0
  1282. package/src/secrets/crypto.ts +228 -0
  1283. package/src/secrets/index.ts +65 -0
  1284. package/src/secrets/onboarding.ts +101 -0
  1285. package/src/secrets/register-framework-secrets.ts +228 -0
  1286. package/src/secrets/register.ts +129 -0
  1287. package/src/secrets/routes.ts +697 -0
  1288. package/src/secrets/schema.ts +52 -0
  1289. package/src/secrets/storage.ts +561 -0
  1290. package/src/secrets/substitution.ts +305 -0
  1291. package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +150 -0
  1292. package/src/server/action-change.ts +66 -0
  1293. package/src/server/action-discovery.ts +774 -0
  1294. package/src/server/action-routes.ts +608 -0
  1295. package/src/server/agent-access.ts +72 -0
  1296. package/src/server/agent-chat/action-filters-a2a.ts +339 -0
  1297. package/src/server/agent-chat/browser-team-tools.ts +510 -0
  1298. package/src/server/agent-chat/context-tools.ts +599 -0
  1299. package/src/server/agent-chat/framework-prompts.ts +467 -0
  1300. package/src/server/agent-chat/lazy-fs.ts +11 -0
  1301. package/src/server/agent-chat/mcp-glue.ts +91 -0
  1302. package/src/server/agent-chat/plugin-options.ts +379 -0
  1303. package/src/server/agent-chat/process-run-failure.ts +62 -0
  1304. package/src/server/agent-chat/prompt-resources.ts +1004 -0
  1305. package/src/server/agent-chat/recurring-jobs-runtime.ts +80 -0
  1306. package/src/server/agent-chat/request-surface.ts +71 -0
  1307. package/src/server/agent-chat/run-code-tools.ts +67 -0
  1308. package/src/server/agent-chat/script-entries.ts +796 -0
  1309. package/src/server/agent-chat/shared-thread.ts +301 -0
  1310. package/src/server/agent-chat/skill-frontmatter.ts +41 -0
  1311. package/src/server/agent-chat-plugin.ts +5930 -0
  1312. package/src/server/agent-discovery.ts +706 -0
  1313. package/src/server/agent-engine-api-key-route.ts +236 -0
  1314. package/src/server/agent-run-context.ts +204 -0
  1315. package/src/server/agent-teams-run-queue.ts +344 -0
  1316. package/src/server/agent-teams.ts +2257 -0
  1317. package/src/server/agents-bundle.ts +464 -0
  1318. package/src/server/analytics.ts +105 -0
  1319. package/src/server/app-base-path.ts +79 -0
  1320. package/src/server/app-name.ts +53 -0
  1321. package/src/server/app-url.ts +184 -0
  1322. package/src/server/attachment-actions.ts +276 -0
  1323. package/src/server/attribution.ts +214 -0
  1324. package/src/server/auth-marketing.ts +260 -0
  1325. package/src/server/auth-plugin.ts +27 -0
  1326. package/src/server/auth.ts +3803 -0
  1327. package/src/server/better-auth-instance.ts +1197 -0
  1328. package/src/server/builder-browser.ts +1701 -0
  1329. package/src/server/builder-design-systems.ts +668 -0
  1330. package/src/server/builder-space.ts +176 -0
  1331. package/src/server/captcha.ts +63 -0
  1332. package/src/server/capture-error.ts +64 -0
  1333. package/src/server/cli-capture.ts +141 -0
  1334. package/src/server/collab-plugin.ts +391 -0
  1335. package/src/server/complete-text.ts +231 -0
  1336. package/src/server/cookie-namespace.ts +159 -0
  1337. package/src/server/core-routes-plugin.ts +4122 -0
  1338. package/src/server/cors-origins.ts +60 -0
  1339. package/src/server/create-server.ts +268 -0
  1340. package/src/server/credential-provider.ts +1202 -0
  1341. package/src/server/csrf.ts +229 -0
  1342. package/src/server/date-utils.ts +42 -0
  1343. package/src/server/deep-link.ts +119 -0
  1344. package/src/server/derived-secret.ts +32 -0
  1345. package/src/server/design-token-utils.ts +1032 -0
  1346. package/src/server/desktop-sso.ts +83 -0
  1347. package/src/server/edge.ts +69 -0
  1348. package/src/server/email-actions.ts +218 -0
  1349. package/src/server/email-template.ts +192 -0
  1350. package/src/server/email-templates.ts +148 -0
  1351. package/src/server/email.ts +219 -0
  1352. package/src/server/embed-route.ts +283 -0
  1353. package/src/server/embed-session.ts +805 -0
  1354. package/src/server/embedded.ts +252 -0
  1355. package/src/server/entry-server.tsx +154 -0
  1356. package/src/server/framework-request-handler.ts +848 -0
  1357. package/src/server/fusion-app.ts +447 -0
  1358. package/src/server/google-auth-mode.ts +34 -0
  1359. package/src/server/google-auth-plugin.ts +496 -0
  1360. package/src/server/google-oauth-credentials.ts +128 -0
  1361. package/src/server/google-oauth.ts +972 -0
  1362. package/src/server/google-realtime-session.ts +182 -0
  1363. package/src/server/h3-helpers.ts +174 -0
  1364. package/src/server/http-response-telemetry.ts +396 -0
  1365. package/src/server/identity-sso-store.ts +304 -0
  1366. package/src/server/identity-sso.ts +514 -0
  1367. package/src/server/index.ts +593 -0
  1368. package/src/server/missing-key.ts +36 -0
  1369. package/src/server/oauth-helpers.ts +44 -0
  1370. package/src/server/oauth-public-origin.ts +44 -0
  1371. package/src/server/oauth-return-url.ts +120 -0
  1372. package/src/server/og-fonts-data.ts +15 -0
  1373. package/src/server/og-fonts.ts +75 -0
  1374. package/src/server/onboarding-html.ts +4040 -0
  1375. package/src/server/open-route.ts +311 -0
  1376. package/src/server/org-admin.ts +28 -0
  1377. package/src/server/poll-events.ts +88 -0
  1378. package/src/server/poll.ts +1224 -0
  1379. package/src/server/prompts/framework-core-compact.ts +107 -0
  1380. package/src/server/prompts/framework-core.ts +143 -0
  1381. package/src/server/prompts/index.ts +16 -0
  1382. package/src/server/prompts/model-overlays.ts +54 -0
  1383. package/src/server/prompts/shared-rules.ts +104 -0
  1384. package/src/server/realtime-voice.ts +967 -0
  1385. package/src/server/recap-image-route.ts +242 -0
  1386. package/src/server/recap-image-store.ts +185 -0
  1387. package/src/server/request-context.ts +416 -0
  1388. package/src/server/request-origin.ts +67 -0
  1389. package/src/server/resources-plugin.ts +119 -0
  1390. package/src/server/schema-prompt.ts +429 -0
  1391. package/src/server/scoped-key-storage.ts +223 -0
  1392. package/src/server/security-headers.ts +170 -0
  1393. package/src/server/self-dispatch.ts +224 -0
  1394. package/src/server/sentry-config.ts +73 -0
  1395. package/src/server/sentry-plugin.ts +138 -0
  1396. package/src/server/sentry.ts +444 -0
  1397. package/src/server/short-lived-token.ts +170 -0
  1398. package/src/server/social-og-image.ts +454 -0
  1399. package/src/server/sse.ts +102 -0
  1400. package/src/server/ssr-handler.ts +466 -0
  1401. package/src/server/transcribe-voice.ts +1076 -0
  1402. package/src/server/voice-providers-status.ts +118 -0
  1403. package/src/server/workspace-oauth.ts +31 -0
  1404. package/src/server/workspace-provider-oauth.ts +970 -0
  1405. package/src/session-replay-iframe-protocol.ts +58 -0
  1406. package/src/settings/handlers.ts +44 -0
  1407. package/src/settings/index.ts +34 -0
  1408. package/src/settings/org-settings.ts +83 -0
  1409. package/src/settings/script-helpers.ts +24 -0
  1410. package/src/settings/store.ts +247 -0
  1411. package/src/settings/user-settings.ts +47 -0
  1412. package/src/setup-connections/index.ts +90 -0
  1413. package/src/setup-connections/onboarding.ts +47 -0
  1414. package/src/shared/agent-access.ts +112 -0
  1415. package/src/shared/agent-chat.ts +124 -0
  1416. package/src/shared/agent-env.ts +46 -0
  1417. package/src/shared/agent-readable-resource.ts +111 -0
  1418. package/src/shared/agent-sidebar-url.ts +39 -0
  1419. package/src/shared/cache-control.ts +25 -0
  1420. package/src/shared/cloudflare-globals.d.ts +34 -0
  1421. package/src/shared/context-xray.ts +172 -0
  1422. package/src/shared/embed-auth.ts +6 -0
  1423. package/src/shared/index.ts +83 -0
  1424. package/src/shared/llm-connection.ts +42 -0
  1425. package/src/shared/markdown-block-split.ts +129 -0
  1426. package/src/shared/mcp-connect-content.ts +130 -0
  1427. package/src/shared/mcp-embed-headers.ts +175 -0
  1428. package/src/shared/mcp-integration-config.ts +101 -0
  1429. package/src/shared/merge-by-id.ts +14 -0
  1430. package/src/shared/oauth-state.ts +39 -0
  1431. package/src/shared/reasoning-effort.ts +228 -0
  1432. package/src/shared/route-warmup-config.ts +128 -0
  1433. package/src/shared/runtime.ts +31 -0
  1434. package/src/shared/social-meta.ts +82 -0
  1435. package/src/shared/streaming-text-smoothing.ts +184 -0
  1436. package/src/shared/truncate.ts +13 -0
  1437. package/src/shared/workspace-app-audience.ts +177 -0
  1438. package/src/shared/workspace-app-id.ts +54 -0
  1439. package/src/sharing/access.ts +585 -0
  1440. package/src/sharing/actions/create-agent-resource-link.ts +91 -0
  1441. package/src/sharing/actions/extension-change.ts +22 -0
  1442. package/src/sharing/actions/list-resource-shares.ts +93 -0
  1443. package/src/sharing/actions/set-resource-visibility.ts +91 -0
  1444. package/src/sharing/actions/share-resource.ts +322 -0
  1445. package/src/sharing/actions/unshare-resource.ts +72 -0
  1446. package/src/sharing/index.ts +35 -0
  1447. package/src/sharing/registry.ts +201 -0
  1448. package/src/sharing/schema.ts +99 -0
  1449. package/src/styles/agent-conversation.css +766 -0
  1450. package/src/styles/agent-native.css +938 -0
  1451. package/src/styles/blocks.css +1717 -0
  1452. package/src/styles/chat-history-list.css +1 -0
  1453. package/src/styles/rich-markdown-editor.css +1 -0
  1454. package/src/tailwind.preset.ts +140 -0
  1455. package/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +15 -13
  1456. package/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  1457. package/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +9 -6
  1458. package/src/templates/chat/AGENTS.md +3 -2
  1459. package/src/templates/default/.agents/skills/agent-native-docs/SKILL.md +15 -13
  1460. package/src/templates/default/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  1461. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +9 -6
  1462. package/src/templates/default/AGENTS.md +6 -5
  1463. package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +15 -13
  1464. package/src/templates/headless/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  1465. package/src/templates/headless/AGENTS.md +5 -4
  1466. package/src/templates/workspace-core/.agents/skills/agent-native-docs/SKILL.md +15 -13
  1467. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +3 -2
  1468. package/src/templates/workspace-core/.agents/skills/customizing-agent-native/SKILL.md +63 -20
  1469. package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +9 -6
  1470. package/src/templates/workspace-core/AGENTS.md +4 -4
  1471. package/src/templates/workspace-root/AGENTS.md +4 -4
  1472. package/src/terminal/cli-registry.ts +57 -0
  1473. package/src/terminal/index.ts +22 -0
  1474. package/src/terminal/pty-server.ts +389 -0
  1475. package/src/terminal/terminal-plugin.ts +265 -0
  1476. package/src/testing.ts +23 -0
  1477. package/src/tracking/index.ts +10 -0
  1478. package/src/tracking/providers.ts +422 -0
  1479. package/src/tracking/registry.ts +102 -0
  1480. package/src/tracking/route.ts +108 -0
  1481. package/src/tracking/types.ts +16 -0
  1482. package/src/transcription/builder-transcription.ts +118 -0
  1483. package/src/triggers/actions/list-automations.ts +99 -0
  1484. package/src/triggers/actions/manage-automation.ts +78 -0
  1485. package/src/triggers/actions.ts +333 -0
  1486. package/src/triggers/condition-evaluator.ts +163 -0
  1487. package/src/triggers/dispatcher.ts +536 -0
  1488. package/src/triggers/index.ts +13 -0
  1489. package/src/triggers/routes.ts +322 -0
  1490. package/src/triggers/types.ts +42 -0
  1491. package/src/types/pglite.d.ts +26 -0
  1492. package/src/usage/store.ts +698 -0
  1493. package/src/vite/action-types-plugin.ts +481 -0
  1494. package/src/vite/agent-web-plugin.ts +78 -0
  1495. package/src/vite/agents-bundle-plugin.ts +185 -0
  1496. package/src/vite/client.ts +3039 -0
  1497. package/src/vite/index.ts +21 -0
  1498. package/src/voice/index.ts +18 -0
  1499. package/src/voice/voice-cleanup-prompt.ts +38 -0
  1500. package/src/voice/voice-context.ts +217 -0
  1501. package/src/voice/voice-replacements.ts +75 -0
  1502. package/src/workspace-connections/credentials.ts +603 -0
  1503. package/src/workspace-connections/index.ts +72 -0
  1504. package/src/workspace-connections/lifecycle.ts +43 -0
  1505. package/src/workspace-connections/store.ts +1943 -0
  1506. package/src/workspace-files/index.ts +18 -0
  1507. package/src/workspace-files/store.ts +339 -0
  1508. package/src/workspace-files/tool.ts +266 -0
@@ -0,0 +1,3437 @@
1
+ /**
2
+ * AgentPanel — unified agent component with chat, CLI, and workspace modes.
3
+ *
4
+ * A self-contained panel with no layout opinions — drop it into a sidebar,
5
+ * popover, dialog, full page, or any container. It fills its parent via
6
+ * flex and min-h-0.
7
+ *
8
+ * Features:
9
+ * - Chat mode: assistant-ui powered chat with tool calls
10
+ * - CLI mode: embedded xterm.js terminal (dev mode only)
11
+ * - Toggle between modes via header buttons
12
+ *
13
+ * Usage:
14
+ * // In a sidebar
15
+ * <div style={{ width: 380 }}><AgentPanel /></div>
16
+ *
17
+ * // In a popover
18
+ * <Popover><AgentPanel suggestions={[...]} /></Popover>
19
+ *
20
+ * // Full page chat surface
21
+ * <AgentChatSurface mode="page" className="h-screen" />
22
+ */
23
+
24
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
25
+ import {
26
+ IconMessageCircle,
27
+ IconMessageDots,
28
+ IconTerminal2,
29
+ IconSettings,
30
+ IconLayoutSidebarRightCollapse,
31
+ IconLayoutGrid,
32
+ IconCheck,
33
+ IconPlus,
34
+ IconX,
35
+ IconDotsVertical,
36
+ IconHistory,
37
+ IconArrowsMaximize,
38
+ IconArrowsMinimize,
39
+ IconExternalLink,
40
+ } from "@tabler/icons-react";
41
+ import React, {
42
+ useState,
43
+ useEffect,
44
+ useRef,
45
+ useCallback,
46
+ useMemo,
47
+ lazy,
48
+ Suspense,
49
+ startTransition,
50
+ } from "react";
51
+
52
+ import type { AgentRun } from "../progress/types.js";
53
+ import {
54
+ DropdownMenu,
55
+ DropdownMenuContent,
56
+ DropdownMenuItem,
57
+ DropdownMenuSeparator,
58
+ DropdownMenuShortcut,
59
+ DropdownMenuTrigger,
60
+ } from "./components/ui/dropdown-menu.js";
61
+ import {
62
+ Tooltip,
63
+ TooltipContent,
64
+ TooltipProvider,
65
+ TooltipTrigger,
66
+ normalizeTooltipText,
67
+ } from "./components/ui/tooltip.js";
68
+ import { ErrorReportActions } from "./ErrorReportActions.js";
69
+ import { FeedbackButton } from "./FeedbackButton.js";
70
+ import { RunsTrayMenuItem } from "./progress/RunsTray.js";
71
+ import { ShareButton } from "./sharing/ShareButton.js";
72
+ // Lazy-load the full assistant-ui chat stack (tiptap composer + react-markdown +
73
+ // assistant-ui + zod block schemas) so it is NOT in the static import closure of
74
+ // every page. The header/tab chrome renders immediately; chat streams in once the
75
+ // chunk lands (~650-700 KB gzip saved from the critical path).
76
+ const MultiTabAssistantChatLazy = lazy(() =>
77
+ import("./MultiTabAssistantChat.js").then((m) => ({
78
+ default: m.MultiTabAssistantChat,
79
+ })),
80
+ );
81
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
82
+ import { Link, useLocation, useNavigate } from "react-router";
83
+
84
+ import type { AgentChatSurfaceKind } from "./agent-chat-adapter.js";
85
+ import {
86
+ consumeAgentSidebarUrlOpenOverride,
87
+ dispatchAgentSidebarStateChange,
88
+ getInitialAgentSidebarOpen,
89
+ setAgentSidebarOpenPreference,
90
+ subscribeAgentSidebarUrlChanges,
91
+ SIDEBAR_STATE_CHANGE_EVENT,
92
+ type AgentSidebarStateChangeDetail,
93
+ } from "./agent-sidebar-state.js";
94
+ import { trackEvent } from "./analytics.js";
95
+ import { agentNativePath, appPath } from "./api-path.js";
96
+ import { assistantUiRecoverableRenderErrorKind } from "./assistant-ui-recovery.js";
97
+ import type { AssistantChatProps } from "./AssistantChat.js";
98
+ import { shouldParentFrameOwnAgentPanel } from "./builder-frame.js";
99
+ import {
100
+ AGENT_CHAT_VIEW_TRANSITION_CLASS,
101
+ getAgentChatViewTransitionStyle,
102
+ } from "./chat-view-transition.js";
103
+ import { RealtimeVoiceModeProvider } from "./composer/index.js";
104
+ import {
105
+ getFramePostMessageTargetOrigin,
106
+ isTrustedFrameMessage,
107
+ } from "./frame.js";
108
+ import { useT } from "./i18n.js";
109
+ import type {
110
+ MultiTabAssistantChatHeaderProps,
111
+ MultiTabAssistantChatProps,
112
+ } from "./MultiTabAssistantChat.js";
113
+ import { recoverFromStaleChunkError } from "./route-chunk-recovery.js";
114
+ import { AgentNativeRouteWarmup } from "./route-warmup.js";
115
+ import { withBuilderConnectTrackingParams } from "./settings/useBuilderStatus.js";
116
+ import { useScreenRefreshKey } from "./use-db-sync.js";
117
+ import { useDevMode } from "./use-dev-mode.js";
118
+ import { cn } from "./utils.js";
119
+
120
+ // Lazy-load AgentTerminal to avoid bundling xterm.js when not needed
121
+ const AgentTerminal = lazy(() =>
122
+ import("./terminal/index.js").then((m) => ({ default: m.AgentTerminal })),
123
+ );
124
+
125
+ const AGENT_PANEL_PREPARE_EVENT = "agent-panel:prepare";
126
+ const AGENT_PANEL_SET_MODE_EVENT = "agent-panel:set-mode";
127
+ const AGENT_PANEL_OPEN_SETTINGS_EVENT = "agent-panel:open-settings";
128
+
129
+ function settingsRouteHashForSection(section?: string | null): string {
130
+ const normalized = section?.replace(/^#/, "").toLowerCase() ?? "";
131
+ if (normalized === "voice") return "#voice";
132
+ if (
133
+ normalized.startsWith("secrets") ||
134
+ normalized.includes("api") ||
135
+ normalized === "integrations" ||
136
+ normalized === "email" ||
137
+ normalized === "browser"
138
+ ) {
139
+ return "#connections";
140
+ }
141
+ if (
142
+ normalized === "account" ||
143
+ normalized === "workspace" ||
144
+ normalized === "workspace-settings" ||
145
+ normalized === "organization" ||
146
+ normalized === "org" ||
147
+ normalized === "hosting" ||
148
+ normalized === "database" ||
149
+ normalized === "uploads" ||
150
+ normalized === "auth" ||
151
+ normalized === "demo-mode"
152
+ ) {
153
+ return "#workspace";
154
+ }
155
+ return "#agent";
156
+ }
157
+ const AGENT_CHAT_RUNNING_EVENT = "agentNative.chatRunning";
158
+
159
+ function parentFrameTargetOrigin(): string {
160
+ return getFramePostMessageTargetOrigin() ?? window.location.origin;
161
+ }
162
+
163
+ // Lazy-load ResourcesPanel to avoid bundling when not needed
164
+ const ResourcesPanel = lazy(() =>
165
+ import("./resources/ResourcesPanel.js").then((m) => ({
166
+ default: m.ResourcesPanel,
167
+ })),
168
+ );
169
+
170
+ // Lazy-load SettingsPanel to avoid bundling when not needed
171
+ const SettingsPanel = lazy(() =>
172
+ import("./settings/index.js").then((m) => ({
173
+ default: m.SettingsPanel,
174
+ })),
175
+ );
176
+
177
+ // Lazy-load OnboardingPanel — only pulled in when onboarding is active.
178
+ const OnboardingPanel = lazy(() =>
179
+ import("./onboarding/OnboardingPanel.js").then((m) => ({
180
+ default: m.OnboardingPanel,
181
+ })),
182
+ );
183
+
184
+ // Lazy-load SetupButton — the header entry-point that re-opens the
185
+ // onboarding panel after the user has dismissed it.
186
+ const SetupButton = lazy(() =>
187
+ import("./onboarding/SetupButton.js").then((m) => ({
188
+ default: m.SetupButton,
189
+ })),
190
+ );
191
+
192
+ // The setup/onboarding checklist that used to appear above chat is disabled
193
+ // for every app — setup (AI engine, image/video gen, asset storage, email,
194
+ // GitHub, etc.) is surfaced in better places (the settings panel and the
195
+ // per-feature setup affordances). Keep this off; do not re-enable globally.
196
+ const SHOW_ONBOARDING = false;
197
+
198
+ const CLI_STORAGE_KEY = "agent-native-cli-command";
199
+ const CLI_DEFAULT = "claude";
200
+ const EXEC_MODE_KEY = "agent-native-exec-mode";
201
+ type ExecMode = "build" | "plan";
202
+ type PanelMode = "chat" | "cli" | "resources" | "settings";
203
+ export function normalizeAgentPanelModeForSurface(
204
+ mode: PanelMode,
205
+ allowSettingsMode: boolean,
206
+ ): PanelMode {
207
+ return mode === "settings" && !allowSettingsMode ? "chat" : mode;
208
+ }
209
+ const AGENT_PANEL_FONT_FAMILY =
210
+ 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
211
+ const AGENT_PANEL_ROOT_STYLE = {
212
+ fontFamily: AGENT_PANEL_FONT_FAMILY,
213
+ fontSize: 13,
214
+ lineHeight: 1.2,
215
+ } satisfies React.CSSProperties;
216
+ type AgentPanelStyle = React.CSSProperties & {
217
+ "--agent-sidebar-background"?: string;
218
+ "--agent-sidebar-closed-transform"?: string;
219
+ "--agent-sidebar-inner-closed-transform"?: string;
220
+ "--agent-sidebar-width"?: string;
221
+ };
222
+ const AGENT_PANEL_HEADER_CLASS =
223
+ "agent-native-shell-topbar relative z-[240] flex h-12 shrink-0 items-center justify-between gap-2";
224
+ const AGENT_PANEL_HEADER_STYLE = {
225
+ paddingLeft: 8,
226
+ paddingRight: 8,
227
+ } satisfies React.CSSProperties;
228
+ const AGENT_PANEL_CONTROL_STYLE = {
229
+ fontSize: 12,
230
+ lineHeight: 1,
231
+ } satisfies React.CSSProperties;
232
+ const ACTIVATE_KEYS = new Set(["Enter", " "]);
233
+
234
+ interface AvailableCli {
235
+ command: string;
236
+ label: string;
237
+ available: boolean;
238
+ }
239
+
240
+ function useAvailableClis() {
241
+ const [clis, setClis] = useState<AvailableCli[]>([]);
242
+ useEffect(() => {
243
+ // Try to fetch available CLIs — endpoint is provided by the terminal plugin.
244
+ // Returns 404 gracefully when the plugin isn't loaded.
245
+ fetch(agentNativePath("/_agent-native/available-clis"))
246
+ .then((r) => (r.ok ? r.json() : []))
247
+ .then((data) => setClis(Array.isArray(data) ? data : []))
248
+ .catch(() => {});
249
+ }, []);
250
+ return clis;
251
+ }
252
+
253
+ function useCliSelection(keyPrefix: string) {
254
+ const cliKey = `${CLI_STORAGE_KEY}${keyPrefix}`;
255
+ const [selected, setSelected] = useState(CLI_DEFAULT);
256
+ useEffect(() => {
257
+ try {
258
+ const saved = localStorage.getItem(cliKey);
259
+ if (saved) setSelected(saved);
260
+ } catch {}
261
+ }, [cliKey]);
262
+ const select = (cmd: string) => {
263
+ setSelected(cmd);
264
+ try {
265
+ localStorage.setItem(cliKey, cmd);
266
+ } catch {}
267
+ };
268
+ return [selected, select] as const;
269
+ }
270
+
271
+ // ─── Settings panel components moved to ./settings/ ────────────────────────
272
+
273
+ function IconTooltip({
274
+ content,
275
+ children,
276
+ }: {
277
+ content: string;
278
+ children: React.ReactNode;
279
+ }) {
280
+ return (
281
+ <TooltipPrimitive.Provider delayDuration={250}>
282
+ <TooltipPrimitive.Root>
283
+ <TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
284
+ <TooltipPrimitive.Portal>
285
+ <TooltipPrimitive.Content
286
+ side="bottom"
287
+ sideOffset={8}
288
+ className="z-[300] overflow-hidden rounded-md border border-border bg-popover px-2 py-1 text-[11px] text-foreground shadow-md"
289
+ >
290
+ {normalizeTooltipText(content)}
291
+ <TooltipPrimitive.Arrow className="fill-popover" />
292
+ </TooltipPrimitive.Content>
293
+ </TooltipPrimitive.Portal>
294
+ </TooltipPrimitive.Root>
295
+ </TooltipPrimitive.Provider>
296
+ );
297
+ }
298
+
299
+ // AgentSettingsPopover and AgentsSection moved to ./settings/
300
+
301
+ // ─── ChatLoadingSkeleton ─────────────────────────────────────────────────────
302
+ // Renders the sidebar header chrome immediately while the lazy assistant-ui
303
+ // chunk is in flight. Matches the composer-area height so layout does not
304
+ // shift when the real chat surface mounts.
305
+ type ChatHeaderRenderer = (
306
+ props: MultiTabAssistantChatHeaderProps,
307
+ ) => React.ReactNode;
308
+
309
+ function ChatLoadingSkeleton({
310
+ renderHeader,
311
+ centerComposerWhenEmpty = false,
312
+ composerSlot,
313
+ composerAreaClassName,
314
+ composerLayoutVariant = "default",
315
+ }: {
316
+ renderHeader?: ChatHeaderRenderer;
317
+ centerComposerWhenEmpty?: boolean;
318
+ composerSlot?: React.ReactNode;
319
+ composerAreaClassName?: string;
320
+ composerLayoutVariant?: AssistantChatProps["composerLayoutVariant"];
321
+ }) {
322
+ // Provide empty no-op implementations so renderHeader can render the real
323
+ // tab/mode buttons without needing actual chat state.
324
+ const noop = useCallback(() => {}, []);
325
+ const noopStr = useCallback((_id: string) => {}, []);
326
+ const stubProps: MultiTabAssistantChatHeaderProps = {
327
+ tabs: [],
328
+ activeTabId: "",
329
+ activeTabMessageCount: 0,
330
+ setActiveTabId: noopStr,
331
+ addTab: noop,
332
+ closeTab: noopStr,
333
+ closeOtherTabs: noopStr,
334
+ closeAllTabs: noop,
335
+ clearActiveTab: noop,
336
+ showHistory: false,
337
+ tabCount: 0,
338
+ toggleHistory: noop,
339
+ };
340
+ if (centerComposerWhenEmpty) {
341
+ return (
342
+ <div className="flex flex-col flex-1 min-h-0">
343
+ {renderHeader ? renderHeader(stubProps) : null}
344
+ <div
345
+ data-agent-empty-state="centered"
346
+ className="relative flex flex-1 flex-col h-full min-h-0 text-foreground"
347
+ >
348
+ <div className="agent-chat-scroll flex-1 overflow-y-auto overflow-x-hidden min-h-0">
349
+ <div className="agent-empty-state sr-only" aria-busy="true">
350
+ Loading chat...
351
+ </div>
352
+ </div>
353
+ {composerSlot}
354
+ <div className="agent-composer-stack">
355
+ <div
356
+ className={cn(
357
+ "agent-composer-area shrink-0 px-3 py-2",
358
+ composerLayoutVariant !== "default" &&
359
+ `agent-composer-area--${composerLayoutVariant}`,
360
+ composerAreaClassName,
361
+ )}
362
+ >
363
+ <div
364
+ className={cn(
365
+ "agent-composer-root flex flex-col rounded-lg border border-input bg-muted/45 transition-colors",
366
+ composerLayoutVariant !== "default" &&
367
+ `agent-composer-root--${composerLayoutVariant}`,
368
+ )}
369
+ >
370
+ <div className="px-3 pt-3">
371
+ <div className="h-5 w-3/5 rounded bg-muted animate-pulse motion-reduce:animate-none" />
372
+ </div>
373
+ <div className="mt-auto flex items-center gap-2 px-3 py-2">
374
+ <div className="h-5 w-5 rounded bg-muted animate-pulse motion-reduce:animate-none" />
375
+ <div className="ml-auto h-4 w-28 rounded bg-muted animate-pulse motion-reduce:animate-none" />
376
+ <div className="h-7 w-7 rounded-md bg-muted animate-pulse motion-reduce:animate-none" />
377
+ </div>
378
+ </div>
379
+ </div>
380
+ </div>
381
+ </div>
382
+ </div>
383
+ );
384
+ }
385
+ return (
386
+ <div className="flex flex-col flex-1 min-h-0">
387
+ {renderHeader ? renderHeader(stubProps) : null}
388
+ {/* Composer-shaped placeholder keeps layout stable during chunk load */}
389
+ <div className="mt-auto shrink-0 border-t border-border p-3">
390
+ <div className="h-16 rounded-xl bg-muted/40 animate-pulse motion-reduce:animate-none" />
391
+ </div>
392
+ </div>
393
+ );
394
+ }
395
+
396
+ export function getAgentPanelChatTabGroups(
397
+ tabs: MultiTabAssistantChatHeaderProps["tabs"],
398
+ activeTabId: string,
399
+ ) {
400
+ const activeTab = tabs.find((t) => t.id === activeTabId);
401
+ const focusParentId = activeTab?.parentThreadId || activeTabId;
402
+ const childTabs = tabs.filter((t) => t.parentThreadId === focusParentId);
403
+ const mainTabs = tabs.filter((t) => !t.parentThreadId);
404
+
405
+ return {
406
+ activeTab,
407
+ childTabs,
408
+ focusParentId,
409
+ hasSubTabs: childTabs.length > 0,
410
+ mainTabs,
411
+ };
412
+ }
413
+
414
+ export function shouldShowAgentPanelChatTabBar(
415
+ tabs: MultiTabAssistantChatHeaderProps["tabs"],
416
+ activeTabId: string,
417
+ ) {
418
+ const { hasSubTabs, mainTabs } = getAgentPanelChatTabGroups(
419
+ tabs,
420
+ activeTabId,
421
+ );
422
+ return mainTabs.length > 1 || hasSubTabs;
423
+ }
424
+
425
+ export function shouldShowAgentPanelSidebarChatTabs(
426
+ tabs: MultiTabAssistantChatHeaderProps["tabs"],
427
+ ) {
428
+ return tabs.filter((tab) => !tab.parentThreadId).length > 1;
429
+ }
430
+
431
+ export function shouldShowAgentPanelPageNewChatButton(
432
+ tabs: MultiTabAssistantChatHeaderProps["tabs"],
433
+ activeTabId: string,
434
+ activeTabMessageCount: number,
435
+ ) {
436
+ if (!activeTabId) return false;
437
+ if (activeTabMessageCount > 0) return true;
438
+
439
+ const activeTab = tabs.find((tab) => tab.id === activeTabId);
440
+ return activeTab?.status === "running" || activeTab?.status === "completed";
441
+ }
442
+
443
+ export function shouldShowAgentPanelCliTabBar(cliTabs: string[]) {
444
+ return cliTabs.length > 1;
445
+ }
446
+
447
+ export function shouldShowAgentPanelModeButtons(isSidebar: boolean) {
448
+ return !isSidebar;
449
+ }
450
+
451
+ export function shouldShowAgentPanelFullViewAction(
452
+ agentPageHref: string | undefined,
453
+ mode: PanelMode,
454
+ ) {
455
+ return (
456
+ Boolean(agentPageHref) && (mode === "resources" || mode === "settings")
457
+ );
458
+ }
459
+
460
+ // ─── AgentPanel ─────────────────────────────────────────────────────────────
461
+
462
+ export interface AgentPanelCodeAccess {
463
+ /** Whether this surface can safely edit source and run shell commands. */
464
+ enabled: boolean;
465
+ /** Heading shown when code access is unavailable. */
466
+ unavailableTitle?: string;
467
+ /** Detail copy shown when code access is unavailable. */
468
+ unavailableDescription?: string;
469
+ /** Optional CTA label for the unavailable state. */
470
+ unavailableCtaLabel?: string;
471
+ /** Optional CTA URL for the unavailable state. */
472
+ unavailableCtaHref?: string;
473
+ /** Optional secondary CTA label, usually for Builder cloud code changes. */
474
+ unavailableSecondaryCtaLabel?: string;
475
+ /** Optional secondary CTA URL, usually the Builder connect URL. */
476
+ unavailableSecondaryCtaHref?: string;
477
+ /** @deprecated Chat stays available when code access is unavailable. */
478
+ unavailableComposerPlaceholder?: string;
479
+ }
480
+
481
+ function useBuilderConnectUrl() {
482
+ const [connectUrl, setConnectUrl] = useState<string | null>(null);
483
+ const [configured, setConfigured] = useState(false);
484
+
485
+ useEffect(() => {
486
+ let cancelled = false;
487
+ // Track previous configured state so we only fanout the
488
+ // `agent-engine:configured-changed` event on a real false→true
489
+ // transition. Without this, every `/builder/status` response with
490
+ // `configured: true` dispatched the event, our own `onConfigured`
491
+ // listener caught it (because we both fire AND listen on the same
492
+ // global), refresh fired again, and we'd loop forever.
493
+ let lastConfigured = false;
494
+ const refresh = () => {
495
+ fetch(agentNativePath("/_agent-native/builder/status"))
496
+ .then((res) => (res.ok ? res.json() : null))
497
+ .then((data) => {
498
+ if (cancelled || !data) return;
499
+ if (data.cliAuthUrl || data.connectUrl) {
500
+ setConnectUrl(data.cliAuthUrl || data.connectUrl);
501
+ }
502
+ const nextConfigured = !!data.configured;
503
+ setConfigured(nextConfigured);
504
+ if (nextConfigured && !lastConfigured) {
505
+ lastConfigured = true;
506
+ // Tell other listeners (the agent panel's "Use Builder" CTA
507
+ // lives in a different React tree than the connect-flow popup
508
+ // poller, so a fresh status read here is the only thing that
509
+ // flips its UI). Dispatch only on transition so listeners
510
+ // that share this hook don't bounce the event back here.
511
+ window.dispatchEvent(
512
+ new CustomEvent("agent-engine:configured-changed", {
513
+ detail: { source: "builder-status" },
514
+ }),
515
+ );
516
+ } else if (!nextConfigured) {
517
+ lastConfigured = false;
518
+ }
519
+ })
520
+ .catch(() => {});
521
+ };
522
+ refresh();
523
+ // The "Use Builder" CTA opens Builder in a `<a target="_blank">` tab
524
+ // (not a popup), so the previous one-shot fetch never noticed the
525
+ // connect succeeded when the user came back to the original tab.
526
+ const onFocus = () => refresh();
527
+ const onVisibility = () => {
528
+ if (document.visibilityState === "visible") refresh();
529
+ };
530
+ const onConfigured = (e: Event) => {
531
+ // Ignore our own dispatch — refresh() already wrote the new state.
532
+ // Other dispatchers (the connect-flow popup poller, an external
533
+ // tab that completed connect, etc.) get the refresh they need.
534
+ const detail = (e as CustomEvent).detail as
535
+ | { source?: string }
536
+ | undefined;
537
+ if (detail?.source === "builder-status") return;
538
+ refresh();
539
+ };
540
+ window.addEventListener("focus", onFocus);
541
+ document.addEventListener("visibilitychange", onVisibility);
542
+ window.addEventListener("agent-engine:configured-changed", onConfigured);
543
+ let channel: BroadcastChannel | null = null;
544
+ try {
545
+ channel = new BroadcastChannel(`builder-connect:${window.location.host}`);
546
+ channel.onmessage = (e: MessageEvent) => {
547
+ const data = e.data as { type?: string } | undefined;
548
+ if (data?.type === "builder-connect-success") refresh();
549
+ };
550
+ } catch {
551
+ // BroadcastChannel missing — focus/visibility refresh still covers it.
552
+ }
553
+ const onMessage = (e: MessageEvent) => {
554
+ if (e.origin !== window.location.origin) return;
555
+ const data = e.data as { type?: string } | undefined;
556
+ if (data?.type === "builder-connect-success") refresh();
557
+ };
558
+ window.addEventListener("message", onMessage);
559
+ return () => {
560
+ cancelled = true;
561
+ window.removeEventListener("focus", onFocus);
562
+ document.removeEventListener("visibilitychange", onVisibility);
563
+ window.removeEventListener(
564
+ "agent-engine:configured-changed",
565
+ onConfigured,
566
+ );
567
+ window.removeEventListener("message", onMessage);
568
+ channel?.close();
569
+ };
570
+ }, []);
571
+
572
+ return { connectUrl, configured };
573
+ }
574
+
575
+ export interface AgentPanelProps extends Omit<
576
+ AssistantChatProps,
577
+ "onSwitchToCli"
578
+ > {
579
+ /** Initial mode. Default: "chat" */
580
+ defaultMode?: "chat" | "cli";
581
+ /** CSS class for the outer container */
582
+ className?: string;
583
+ /** Inline styles for the outer container. */
584
+ style?: React.CSSProperties;
585
+ /** Called when the user clicks the collapse button. If provided, a collapse button appears in the header. */
586
+ onCollapse?: () => void;
587
+ /** Whether the panel is currently in fullscreen (Claude-style centered) mode. */
588
+ isFullscreen?: boolean;
589
+ /** Called when the user clicks the maximize/minimize button. If provided, the button appears next to the collapse button. */
590
+ onToggleFullscreen?: () => void;
591
+ /** URL of the app being developed (shown as "Open app in new tab" in settings). Set by frame. */
592
+ devAppUrl?: string;
593
+ /** Namespace for localStorage keys — used to isolate chat state per app in the frame. */
594
+ storageKey?: string;
595
+ /** Restore the previously active chat thread on mount. Default: true. */
596
+ restoreActiveThread?: boolean;
597
+ /** Ambient resource context rendered as a composer chip. */
598
+ scope?: import("./use-chat-threads.js").ChatThreadScope | null;
599
+ /** @deprecated Scope context now appears inside the composer. */
600
+ showScopeBadge?: MultiTabAssistantChatProps["showScopeBadge"];
601
+ /** Stable browser tab id used for tab-scoped app-state context. */
602
+ browserTabId?: string;
603
+ /** Keep chat thread selection in URL state. */
604
+ threadUrlSync?: MultiTabAssistantChatProps["threadUrlSync"];
605
+ /** Optional notice rendered below the main header while Chat mode is active. */
606
+ chatNotice?: React.ReactNode;
607
+ /** Show the chat thread tab row when the panel header is hidden. Default: true. */
608
+ showTabBar?: boolean;
609
+ /** Show a compact New chat action in page chat when the main header is hidden. */
610
+ showPageNewChatButton?: boolean;
611
+ /** Allow the sidebar settings view to render inside this panel. Default: true. */
612
+ allowSettingsMode?: boolean;
613
+ /** Optional link shown in Resources and Settings modes for the full Agent page. */
614
+ agentPageHref?: string;
615
+ /** Capability gate for source edits and CLI access. */
616
+ codeAccess?: AgentPanelCodeAccess;
617
+ }
618
+
619
+ function useClientOnly() {
620
+ const [mounted, setMounted] = useState(false);
621
+ useEffect(() => setMounted(true), []);
622
+ return mounted;
623
+ }
624
+
625
+ const DESKTOP_CODE_SURFACE_QUERY_PARAM = "_agentNativeDesktopCode";
626
+ const DESKTOP_CODE_SURFACE_SESSION_KEY = "agent-native:desktop-code-surface";
627
+
628
+ function isDesktopCodeSurfaceRequested(): boolean {
629
+ if (typeof window === "undefined") return false;
630
+ try {
631
+ const requested =
632
+ new URLSearchParams(window.location.search).get(
633
+ DESKTOP_CODE_SURFACE_QUERY_PARAM,
634
+ ) === "1";
635
+ if (requested) {
636
+ window.sessionStorage.setItem(DESKTOP_CODE_SURFACE_SESSION_KEY, "1");
637
+ return true;
638
+ }
639
+ return (
640
+ window.sessionStorage.getItem(DESKTOP_CODE_SURFACE_SESSION_KEY) === "1"
641
+ );
642
+ } catch {
643
+ return false;
644
+ }
645
+ }
646
+
647
+ export function resolveAgentPanelChatSurface(
648
+ explicitSurface: AgentChatSurfaceKind | undefined,
649
+ desktopCodeSurfaceRequested: boolean,
650
+ ): AgentChatSurfaceKind {
651
+ if (explicitSurface) return explicitSurface;
652
+ return desktopCodeSurfaceRequested ? "desktop" : "app";
653
+ }
654
+
655
+ function CodeAccessUnavailablePanel({
656
+ title,
657
+ description,
658
+ ctaLabel,
659
+ ctaHref,
660
+ secondaryCtaLabel = "Use Builder",
661
+ secondaryCtaHref,
662
+ compact = false,
663
+ }: {
664
+ title: string;
665
+ description: string;
666
+ ctaLabel: string;
667
+ ctaHref?: string;
668
+ secondaryCtaLabel?: string;
669
+ secondaryCtaHref?: string;
670
+ compact?: boolean;
671
+ }) {
672
+ const { connectUrl: builderConnectUrl } = useBuilderConnectUrl();
673
+ const builderHref =
674
+ secondaryCtaHref ??
675
+ (builderConnectUrl
676
+ ? withBuilderConnectTrackingParams(builderConnectUrl, {
677
+ source: "code_access_unavailable_panel",
678
+ flow: "background_agent",
679
+ })
680
+ : "https://builder.io");
681
+
682
+ return (
683
+ <div
684
+ className={cn(
685
+ "rounded-lg border border-border bg-muted/35 text-center",
686
+ compact ? "mx-3 mt-2 px-3 py-2.5" : "max-w-[300px] px-4 py-4",
687
+ )}
688
+ >
689
+ <div
690
+ className={cn(
691
+ "mx-auto flex items-center justify-center rounded-full bg-background text-muted-foreground",
692
+ compact ? "mb-2 h-8 w-8" : "mb-3 h-10 w-10",
693
+ )}
694
+ >
695
+ <IconTerminal2 className={compact ? "h-4 w-4" : "h-5 w-5"} />
696
+ </div>
697
+ <p className="text-sm font-medium text-foreground">{title}</p>
698
+ <p
699
+ className={cn(
700
+ "mt-1 text-muted-foreground",
701
+ compact ? "text-[11px] leading-snug" : "text-xs leading-relaxed",
702
+ )}
703
+ >
704
+ {description}
705
+ </p>
706
+ <div className="mt-3 flex flex-wrap items-center justify-center gap-2">
707
+ {ctaHref ? (
708
+ <a
709
+ href={ctaHref}
710
+ target="_blank"
711
+ rel="noreferrer"
712
+ className="inline-flex items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background hover:opacity-90"
713
+ >
714
+ {ctaLabel}
715
+ <IconExternalLink className="h-3 w-3" />
716
+ </a>
717
+ ) : null}
718
+ <a
719
+ href={builderHref}
720
+ target="_blank"
721
+ rel="noreferrer"
722
+ onClick={() => {
723
+ trackEvent("builder connect clicked", {
724
+ feature: "builder",
725
+ stage: "client",
726
+ source: "code_access_unavailable_panel",
727
+ flow: "background_agent",
728
+ connect_url_kind: builderConnectUrl ? "provided" : "fallback",
729
+ });
730
+ }}
731
+ className="inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground hover:bg-accent"
732
+ >
733
+ {secondaryCtaLabel}
734
+ <IconExternalLink className="h-3 w-3" />
735
+ </a>
736
+ </div>
737
+ </div>
738
+ );
739
+ }
740
+
741
+ function AgentPanelInner({
742
+ defaultMode = "chat",
743
+ className,
744
+ style,
745
+ apiUrl,
746
+ emptyStateText,
747
+ emptyStateAddon,
748
+ suggestions,
749
+ dynamicSuggestions,
750
+ showHeader = true,
751
+ onCollapse,
752
+ isFullscreen,
753
+ onToggleFullscreen,
754
+ devAppUrl,
755
+ storageKey,
756
+ restoreActiveThread = true,
757
+ scope,
758
+ showScopeBadge,
759
+ browserTabId,
760
+ threadUrlSync,
761
+ chatNotice,
762
+ showTabBar = true,
763
+ showPageNewChatButton = false,
764
+ allowSettingsMode = true,
765
+ agentPageHref,
766
+ codeAccess,
767
+ ...assistantChatProps
768
+ }: AgentPanelProps) {
769
+ const t = useT();
770
+ const navigate = useNavigate();
771
+ const mounted = useClientOnly();
772
+ const keyPrefix = storageKey ? `:${storageKey}` : "";
773
+ const execModeKey = `${EXEC_MODE_KEY}${keyPrefix}`;
774
+ const panelModeKey = `agent-native-panel-mode${keyPrefix}`;
775
+ const isMac = useMemo(
776
+ () =>
777
+ typeof navigator !== "undefined" &&
778
+ /Mac|iPhone|iPad/.test(navigator.userAgent),
779
+ [],
780
+ );
781
+ const closeTabHint = isMac ? "\u2303W" : "Alt+W";
782
+ const closeAllTabsHint = isMac ? "\u2303\u2325W" : "Ctrl+Alt+W";
783
+ const toggleSidebarHint = isMac ? "\u2318\\" : "Ctrl+\\";
784
+
785
+ const [execMode, setExecMode] = useState<ExecMode>(() => {
786
+ try {
787
+ const saved = localStorage.getItem(execModeKey);
788
+ if (saved === "build" || saved === "plan") return saved;
789
+ } catch {}
790
+ return "build";
791
+ });
792
+
793
+ const switchExecMode = useCallback(
794
+ (next: ExecMode) => {
795
+ setExecMode(next);
796
+ try {
797
+ localStorage.setItem(execModeKey, next);
798
+ } catch {}
799
+ window.dispatchEvent(
800
+ new CustomEvent("agent-panel:exec-mode-change", {
801
+ detail: { mode: next },
802
+ }),
803
+ );
804
+ },
805
+ [execModeKey],
806
+ );
807
+
808
+ const [mode, setMode] = useState<PanelMode>(() => {
809
+ try {
810
+ const saved = localStorage.getItem(panelModeKey);
811
+ if (
812
+ saved === "chat" ||
813
+ saved === "cli" ||
814
+ saved === "resources" ||
815
+ saved === "settings"
816
+ )
817
+ return normalizeAgentPanelModeForSurface(saved, allowSettingsMode);
818
+ } catch {}
819
+ return normalizeAgentPanelModeForSurface(defaultMode, allowSettingsMode);
820
+ });
821
+ useEffect(() => {
822
+ try {
823
+ localStorage.setItem(panelModeKey, mode);
824
+ } catch {}
825
+ }, [mode, panelModeKey]);
826
+ const [settingsSection, setSettingsSection] = useState<{
827
+ section: string | null;
828
+ requestKey: number;
829
+ }>({ section: null, requestKey: 0 });
830
+ const switchMode = useCallback(
831
+ (m: PanelMode) => {
832
+ startTransition(() =>
833
+ setMode(normalizeAgentPanelModeForSurface(m, allowSettingsMode)),
834
+ );
835
+ },
836
+ [allowSettingsMode],
837
+ );
838
+ useEffect(() => {
839
+ const nextMode = normalizeAgentPanelModeForSurface(mode, allowSettingsMode);
840
+ if (nextMode !== mode) switchMode(nextMode);
841
+ }, [mode, allowSettingsMode, switchMode]);
842
+ const openRunThread = useCallback(
843
+ (threadId: string, run?: AgentRun) => {
844
+ switchMode("chat");
845
+ const metadata = run?.metadata ?? {};
846
+ const parentThreadId =
847
+ typeof metadata.parentThreadId === "string"
848
+ ? metadata.parentThreadId.trim()
849
+ : "";
850
+ const isAgentTeam =
851
+ metadata.kind === "agent-team" || metadata.source === "agent-teams";
852
+ if (isAgentTeam && parentThreadId && parentThreadId !== threadId) {
853
+ window.dispatchEvent(
854
+ new CustomEvent("agent-task-open", {
855
+ detail: {
856
+ threadId,
857
+ parentThreadId,
858
+ description:
859
+ typeof metadata.description === "string"
860
+ ? metadata.description
861
+ : run?.title || "",
862
+ name: typeof metadata.name === "string" ? metadata.name : "",
863
+ },
864
+ }),
865
+ );
866
+ return;
867
+ }
868
+ window.dispatchEvent(
869
+ new CustomEvent("agent-chat:open-thread", {
870
+ detail: { threadId },
871
+ }),
872
+ );
873
+ },
874
+ [switchMode],
875
+ );
876
+ const activateOnKeyDown = useCallback(
877
+ (activate: () => void) => (event: React.KeyboardEvent) => {
878
+ if (!ACTIVATE_KEYS.has(event.key)) return;
879
+ event.preventDefault();
880
+ activate();
881
+ },
882
+ [],
883
+ );
884
+
885
+ // Listen for mode changes from the frame parent (via AgentSidebar)
886
+ useEffect(() => {
887
+ function handler(e: Event) {
888
+ const detail = (e as CustomEvent).detail;
889
+ if (detail?.mode) switchMode(detail.mode);
890
+ }
891
+ window.addEventListener(AGENT_PANEL_SET_MODE_EVENT, handler);
892
+ return () =>
893
+ window.removeEventListener(AGENT_PANEL_SET_MODE_EVENT, handler);
894
+ }, [switchMode]);
895
+
896
+ // Open settings tab when requested (replaces the old popover open event)
897
+ useEffect(() => {
898
+ function handleOpenSettings(event: Event) {
899
+ const section = (event as CustomEvent<{ section?: string }>).detail
900
+ ?.section;
901
+ setSettingsSection((prev) => ({
902
+ section: section ?? null,
903
+ requestKey: prev.requestKey + 1,
904
+ }));
905
+ if (!allowSettingsMode) {
906
+ navigate({
907
+ pathname: "/settings",
908
+ hash: settingsRouteHashForSection(section),
909
+ });
910
+ switchMode("chat");
911
+ return;
912
+ }
913
+ switchMode("settings");
914
+ }
915
+ window.addEventListener(
916
+ AGENT_PANEL_OPEN_SETTINGS_EVENT,
917
+ handleOpenSettings,
918
+ );
919
+ return () =>
920
+ window.removeEventListener(
921
+ AGENT_PANEL_OPEN_SETTINGS_EVENT,
922
+ handleOpenSettings,
923
+ );
924
+ }, [allowSettingsMode, navigate, switchMode]);
925
+
926
+ // CLI terminal tabs (ephemeral — not persisted to SQL)
927
+ const [cliTabs, setCliTabs] = useState<string[]>(["cli-1"]);
928
+ const [activeCliTab, setActiveCliTab] = useState("cli-1");
929
+ const cliCounter = useRef(1);
930
+
931
+ const addCliTab = useCallback(() => {
932
+ const id = `cli-${++cliCounter.current}`;
933
+ setCliTabs((prev) => [...prev, id]);
934
+ setActiveCliTab(id);
935
+ }, []);
936
+
937
+ const closeCliTab = useCallback(
938
+ (id: string) => {
939
+ setCliTabs((prev) => {
940
+ if (prev.length <= 1) {
941
+ // Last tab — replace with a new one (acts as "clear")
942
+ const newId = `cli-${++cliCounter.current}`;
943
+ setActiveCliTab(newId);
944
+ return [newId];
945
+ }
946
+ const next = prev.filter((t) => t !== id);
947
+ if (id === activeCliTab) {
948
+ const idx = prev.indexOf(id);
949
+ setActiveCliTab(next[Math.min(idx, next.length - 1)]);
950
+ }
951
+ return next;
952
+ });
953
+ },
954
+ [activeCliTab],
955
+ );
956
+
957
+ const closeOtherCliTabs = useCallback((id: string) => {
958
+ setCliTabs([id]);
959
+ setActiveCliTab(id);
960
+ }, []);
961
+
962
+ const closeAllCliTabs = useCallback(() => {
963
+ const id = `cli-${++cliCounter.current}`;
964
+ setCliTabs([id]);
965
+ setActiveCliTab(id);
966
+ }, []);
967
+
968
+ // Tab close shortcuts. Avoid Cmd+W (browser/OS) and (on Windows) Ctrl+W.
969
+ // Mac: Ctrl+W → close tab, Ctrl+Alt+W → close all
970
+ // Windows/Linux: Alt+W → close tab, Ctrl+Alt+W → close all
971
+ // Use e.code (physical key) — on Mac, Alt+W inserts ∑ and e.key isn't "w".
972
+ useEffect(() => {
973
+ const handleKeyDown = (e: KeyboardEvent) => {
974
+ if (e.code !== "KeyW" || e.metaKey || e.shiftKey) return;
975
+ const isCloseAll = e.ctrlKey && e.altKey;
976
+ const isCloseOne = isMac
977
+ ? e.ctrlKey && !e.altKey
978
+ : e.altKey && !e.ctrlKey;
979
+ if (!isCloseAll && !isCloseOne) return;
980
+ e.preventDefault();
981
+ if (mode === "chat") {
982
+ window.dispatchEvent(
983
+ new CustomEvent(
984
+ isCloseAll
985
+ ? "agent-chat:close-all-tabs"
986
+ : "agent-chat:close-current-tab",
987
+ ),
988
+ );
989
+ } else if (mode === "cli") {
990
+ if (isCloseAll) closeAllCliTabs();
991
+ else if (activeCliTab) closeCliTab(activeCliTab);
992
+ }
993
+ };
994
+ document.addEventListener("keydown", handleKeyDown);
995
+ return () => document.removeEventListener("keydown", handleKeyDown);
996
+ }, [mode, activeCliTab, closeCliTab, closeAllCliTabs, isMac]);
997
+
998
+ const availableClis = useAvailableClis();
999
+ const [selectedCli, selectCli] = useCliSelection(keyPrefix);
1000
+ const { isDevMode, canToggle, setDevMode } = useDevMode(apiUrl);
1001
+ const effectiveAgentChatSurface = resolveAgentPanelChatSurface(
1002
+ assistantChatProps.agentChatSurface,
1003
+ isDesktopCodeSurfaceRequested(),
1004
+ );
1005
+ const isDevFrameChatSurface = effectiveAgentChatSurface === "dev-frame";
1006
+ const isCodeEditingChatSurface =
1007
+ isDevFrameChatSurface || effectiveAgentChatSurface === "desktop";
1008
+ const inferredCodeAccessEnabled = !isDevMode || isCodeEditingChatSurface;
1009
+ const codeAccessEnabled = codeAccess?.enabled ?? inferredCodeAccessEnabled;
1010
+ const codeUnavailableTitle =
1011
+ codeAccess?.unavailableTitle ?? t("agentPanel.openDesktopToEditCode");
1012
+ const codeUnavailableDescription =
1013
+ codeAccess?.unavailableDescription ??
1014
+ t("agentPanel.codeUnavailableDescription");
1015
+ const codeUnavailableCtaLabel =
1016
+ codeAccess?.unavailableCtaLabel ?? t("agentPanel.downloadDesktop");
1017
+ const codeUnavailableCtaHref =
1018
+ codeAccess?.unavailableCtaHref ?? "https://www.agent-native.com/download";
1019
+ const codeUnavailableSecondaryCtaLabel =
1020
+ codeAccess?.unavailableSecondaryCtaLabel ?? t("agentPanel.useBuilder");
1021
+ const codeUnavailableSecondaryCtaHref =
1022
+ codeAccess?.unavailableSecondaryCtaHref;
1023
+ const canUseCodeTools =
1024
+ isDevMode && codeAccessEnabled && isCodeEditingChatSurface;
1025
+ // Hide the CLI tab when embedded in the Builder.io frame — code editing
1026
+ // there happens via Builder, and the CLI panel only offers a Download
1027
+ // Desktop CTA, which adds clutter without value.
1028
+ const showCliMode =
1029
+ (isDevMode || !codeAccessEnabled) && isCodeEditingChatSurface;
1030
+ useEffect(() => {
1031
+ if (mode === "cli" && !showCliMode) switchMode("chat");
1032
+ }, [mode, showCliMode, switchMode]);
1033
+
1034
+ // Notify frame when dev mode changes — use both a local CustomEvent (for
1035
+ // when AgentPanel is rendered directly in the frame) AND postMessage (for
1036
+ // when AgentPanel is inside the iframe and needs to cross the boundary).
1037
+ const prevIsDevMode = useRef(isDevMode);
1038
+ useEffect(() => {
1039
+ if (prevIsDevMode.current !== isDevMode) {
1040
+ prevIsDevMode.current = isDevMode;
1041
+ window.dispatchEvent(
1042
+ new CustomEvent("agent-panel:dev-mode-change", {
1043
+ detail: { isDevMode },
1044
+ }),
1045
+ );
1046
+ // Cross iframe boundary to the frame parent
1047
+ if (window.parent !== window) {
1048
+ window.parent.postMessage(
1049
+ { type: "agentNative.devModeChange", data: { isDevMode } },
1050
+ parentFrameTargetOrigin(),
1051
+ );
1052
+ }
1053
+ }
1054
+ }, [isDevMode]);
1055
+
1056
+ const isLocalhost =
1057
+ mounted &&
1058
+ typeof window !== "undefined" &&
1059
+ (window.location.hostname === "localhost" ||
1060
+ window.location.hostname === "127.0.0.1" ||
1061
+ window.location.hostname === "::1");
1062
+ const showDevToggle = canToggle && isLocalhost && isCodeEditingChatSurface;
1063
+
1064
+ const renderModeButtons = useCallback(
1065
+ (activeMode: PanelMode) => (
1066
+ <TooltipProvider delayDuration={200}>
1067
+ <div className="flex shrink-0 items-center gap-1">
1068
+ <Tooltip>
1069
+ <TooltipTrigger asChild>
1070
+ <button
1071
+ onClick={() => switchMode("chat")}
1072
+ aria-label={t("agentPanel.chatMode")}
1073
+ className={cn(
1074
+ "flex items-center gap-1 rounded-md px-2 py-1 text-[12px] leading-none",
1075
+ activeMode === "chat"
1076
+ ? "bg-accent text-foreground"
1077
+ : "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
1078
+ )}
1079
+ style={AGENT_PANEL_CONTROL_STYLE}
1080
+ >
1081
+ <IconMessageCircle size={14} />
1082
+ {t("agentPanel.chat")}
1083
+ </button>
1084
+ </TooltipTrigger>
1085
+ <TooltipContent>{t("agentPanel.chatMode")}</TooltipContent>
1086
+ </Tooltip>
1087
+ {showCliMode && (
1088
+ <Tooltip>
1089
+ <TooltipTrigger asChild>
1090
+ <button
1091
+ onClick={() => switchMode("cli")}
1092
+ aria-label={t("agentPanel.cliTerminalMode")}
1093
+ className={cn(
1094
+ "flex items-center gap-1 rounded-md px-2 py-1 text-[12px] leading-none",
1095
+ activeMode === "cli"
1096
+ ? "bg-accent text-foreground"
1097
+ : "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
1098
+ )}
1099
+ style={AGENT_PANEL_CONTROL_STYLE}
1100
+ >
1101
+ <IconTerminal2 size={14} />
1102
+ {t("agentPanel.cli")}
1103
+ </button>
1104
+ </TooltipTrigger>
1105
+ <TooltipContent className="max-w-[260px]">
1106
+ {codeAccessEnabled
1107
+ ? t("agentPanel.cliTerminalMode")
1108
+ : codeUnavailableDescription}
1109
+ </TooltipContent>
1110
+ </Tooltip>
1111
+ )}
1112
+ <Tooltip>
1113
+ <TooltipTrigger asChild>
1114
+ <button
1115
+ onClick={() => switchMode("resources")}
1116
+ aria-label={t("agentPanel.workspaceMode")}
1117
+ className={cn(
1118
+ "flex items-center gap-1 rounded-md px-2 py-1 text-[12px] leading-none",
1119
+ activeMode === "resources"
1120
+ ? "bg-accent text-foreground"
1121
+ : "text-muted-foreground hover:bg-accent/50 hover:text-foreground",
1122
+ )}
1123
+ style={AGENT_PANEL_CONTROL_STYLE}
1124
+ >
1125
+ <IconLayoutGrid size={14} />
1126
+ {t("agentPanel.workspace")}
1127
+ </button>
1128
+ </TooltipTrigger>
1129
+ <TooltipContent>{t("agentPanel.workspaceMode")}</TooltipContent>
1130
+ </Tooltip>
1131
+ </div>
1132
+ </TooltipProvider>
1133
+ ),
1134
+ [codeAccessEnabled, codeUnavailableDescription, showCliMode, t],
1135
+ );
1136
+
1137
+ const [headerMenuOpen, setHeaderMenuOpen] = useState(false);
1138
+ const [feedbackOpen, setFeedbackOpen] = useState(false);
1139
+
1140
+ const getChatThreadShareUrl = useCallback(
1141
+ (threadId: string) => {
1142
+ if (typeof window === "undefined") return undefined;
1143
+ if (
1144
+ threadUrlSync &&
1145
+ typeof threadUrlSync === "object" &&
1146
+ typeof threadUrlSync.getPath === "function"
1147
+ ) {
1148
+ return new URL(
1149
+ appPath(threadUrlSync.getPath(threadId)),
1150
+ window.location.origin,
1151
+ ).toString();
1152
+ }
1153
+ const url = new URL(window.location.href);
1154
+ url.searchParams.set("thread", threadId);
1155
+ return url.toString();
1156
+ },
1157
+ [threadUrlSync],
1158
+ );
1159
+
1160
+ const renderHeaderActions = useCallback(
1161
+ ({
1162
+ activeChatSessionId,
1163
+ activeTabId,
1164
+ activeTabMessageCount,
1165
+ addTab,
1166
+ clearActiveTab,
1167
+ closeAllTabs,
1168
+ closeOtherTabs,
1169
+ closeTab,
1170
+ showHistory,
1171
+ tabs,
1172
+ toggleHistory,
1173
+ }: Pick<
1174
+ MultiTabAssistantChatHeaderProps,
1175
+ | "activeTabId"
1176
+ | "activeTabMessageCount"
1177
+ | "addTab"
1178
+ | "clearActiveTab"
1179
+ | "closeAllTabs"
1180
+ | "closeOtherTabs"
1181
+ | "closeTab"
1182
+ | "showHistory"
1183
+ | "tabs"
1184
+ | "toggleHistory"
1185
+ > & { activeChatSessionId?: string }) => (
1186
+ <div className="relative flex shrink-0 items-center gap-0.5">
1187
+ {!onCollapse && SHOW_ONBOARDING && (
1188
+ <Suspense fallback={null}>
1189
+ <SetupButton />
1190
+ </Suspense>
1191
+ )}
1192
+ {!onCollapse &&
1193
+ (() => {
1194
+ const activeTab =
1195
+ mode === "chat" && activeChatSessionId
1196
+ ? tabs.find((tab) => tab.id === activeChatSessionId)
1197
+ : undefined;
1198
+ if (
1199
+ !activeTab ||
1200
+ (activeTabMessageCount <= 0 && activeTab.status === "idle")
1201
+ ) {
1202
+ return null;
1203
+ }
1204
+ return (
1205
+ <ShareButton
1206
+ resourceType="chat_thread"
1207
+ resourceId={activeTab.id}
1208
+ resourceTitle={activeTab.label || "Chat"}
1209
+ shareUrl={getChatThreadShareUrl(activeTab.id)}
1210
+ trigger="icon"
1211
+ triggerClassName="h-7 w-7"
1212
+ />
1213
+ );
1214
+ })()}
1215
+ <FeedbackButton
1216
+ variant="icon"
1217
+ side="bottom"
1218
+ align="end"
1219
+ chatSessionId={activeChatSessionId}
1220
+ chatStorageKey={storageKey}
1221
+ open={feedbackOpen}
1222
+ onOpenChange={setFeedbackOpen}
1223
+ trigger={
1224
+ <button
1225
+ type="button"
1226
+ tabIndex={-1}
1227
+ aria-hidden="true"
1228
+ className="pointer-events-none absolute end-0 top-full h-px w-px opacity-0"
1229
+ />
1230
+ }
1231
+ />
1232
+ {mode === "chat" && (
1233
+ <IconTooltip content={t("agentPanel.newChat")}>
1234
+ <button
1235
+ onClick={addTab}
1236
+ aria-label={t("agentPanel.newChat")}
1237
+ className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50"
1238
+ >
1239
+ <IconPlus size={14} />
1240
+ </button>
1241
+ </IconTooltip>
1242
+ )}
1243
+ {!onCollapse && mode === "cli" && canUseCodeTools && (
1244
+ <IconTooltip content={t("agentPanel.newTerminal")}>
1245
+ <button
1246
+ onClick={addCliTab}
1247
+ aria-label={t("agentPanel.newTerminal")}
1248
+ className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50"
1249
+ >
1250
+ <IconPlus size={14} />
1251
+ </button>
1252
+ </IconTooltip>
1253
+ )}
1254
+ {agentPageHref &&
1255
+ shouldShowAgentPanelFullViewAction(agentPageHref, mode) && (
1256
+ <IconTooltip content={t("agentPanel.openFullView")}>
1257
+ <Link
1258
+ to={agentPageHref}
1259
+ aria-label={t("agentPanel.openFullView")}
1260
+ className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:bg-accent/50 hover:text-foreground"
1261
+ >
1262
+ <IconArrowsMaximize size={14} />
1263
+ </Link>
1264
+ </IconTooltip>
1265
+ )}
1266
+ <DropdownMenu open={headerMenuOpen} onOpenChange={setHeaderMenuOpen}>
1267
+ <DropdownMenuTrigger asChild>
1268
+ <button
1269
+ className={cn(
1270
+ "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50",
1271
+ (headerMenuOpen || mode === "settings") &&
1272
+ "bg-accent text-foreground",
1273
+ )}
1274
+ aria-label={t("agentPanel.panelOptions")}
1275
+ >
1276
+ <IconDotsVertical size={14} />
1277
+ </button>
1278
+ </DropdownMenuTrigger>
1279
+ <DropdownMenuContent align="end" sideOffset={6} className="w-48">
1280
+ {onCollapse && (
1281
+ <>
1282
+ <DropdownMenuItem onSelect={onCollapse}>
1283
+ <IconLayoutSidebarRightCollapse
1284
+ size={14}
1285
+ className="shrink-0"
1286
+ />
1287
+ {t("agentPanel.collapseSidebar")}
1288
+ <DropdownMenuShortcut>
1289
+ {toggleSidebarHint}
1290
+ </DropdownMenuShortcut>
1291
+ </DropdownMenuItem>
1292
+ <DropdownMenuSeparator />
1293
+ </>
1294
+ )}
1295
+ {onCollapse && mode === "chat" && (
1296
+ <DropdownMenuItem onSelect={addTab}>
1297
+ <IconPlus size={14} className="shrink-0" />
1298
+ {t("agentPanel.newChat")}
1299
+ </DropdownMenuItem>
1300
+ )}
1301
+ {mode === "chat" && toggleHistory && (
1302
+ <DropdownMenuItem onSelect={toggleHistory}>
1303
+ <IconHistory size={14} className="shrink-0" />
1304
+ {showHistory
1305
+ ? t("agentPanel.hideChats")
1306
+ : t("agentPanel.allChats")}
1307
+ </DropdownMenuItem>
1308
+ )}
1309
+ {mode === "chat" && (
1310
+ <RunsTrayMenuItem
1311
+ pollMs={0}
1312
+ limit={12}
1313
+ showRecent={true}
1314
+ onOpenThread={openRunThread}
1315
+ />
1316
+ )}
1317
+ {mode === "chat" && <DropdownMenuSeparator />}
1318
+ {mode === "cli" && availableClis.length > 0 && (
1319
+ <>
1320
+ {availableClis.map((cli) => (
1321
+ <DropdownMenuItem
1322
+ key={cli.command}
1323
+ onSelect={() => selectCli(cli.command)}
1324
+ className={cn(
1325
+ cli.command === selectedCli
1326
+ ? "font-medium"
1327
+ : "text-muted-foreground",
1328
+ )}
1329
+ >
1330
+ {cli.command === selectedCli ? (
1331
+ <IconCheck size={12} className="shrink-0" />
1332
+ ) : (
1333
+ <span className="w-3" />
1334
+ )}
1335
+ {cli.label}
1336
+ </DropdownMenuItem>
1337
+ ))}
1338
+ <DropdownMenuSeparator />
1339
+ </>
1340
+ )}
1341
+ {allowSettingsMode && (
1342
+ <DropdownMenuItem
1343
+ onSelect={() => switchMode("settings")}
1344
+ className={cn(
1345
+ mode === "settings" ? "font-medium" : "text-muted-foreground",
1346
+ )}
1347
+ >
1348
+ <IconSettings size={14} className="shrink-0" />
1349
+ {t("agentPanel.settings")}
1350
+ </DropdownMenuItem>
1351
+ )}
1352
+ <DropdownMenuItem
1353
+ onSelect={() => {
1354
+ // Defer past the closing DropdownMenu's focus-restore/dismiss-layer
1355
+ // teardown, otherwise it can immediately dismiss the Popover we're
1356
+ // opening in the same tick (Radix nested-overlay race).
1357
+ setTimeout(() => setFeedbackOpen(true), 0);
1358
+ }}
1359
+ >
1360
+ <IconMessageDots size={14} className="shrink-0" />
1361
+ {t("agentPanel.feedback")}
1362
+ </DropdownMenuItem>
1363
+ {onToggleFullscreen && (
1364
+ <DropdownMenuItem onSelect={onToggleFullscreen}>
1365
+ {isFullscreen ? (
1366
+ <IconArrowsMinimize size={14} className="shrink-0" />
1367
+ ) : (
1368
+ <IconArrowsMaximize size={14} className="shrink-0" />
1369
+ )}
1370
+ {isFullscreen
1371
+ ? t("agentPanel.exitFullscreen")
1372
+ : t("agentPanel.fullscreen")}
1373
+ </DropdownMenuItem>
1374
+ )}
1375
+ {((mode === "chat" && activeTabId) ||
1376
+ (mode === "cli" && canUseCodeTools && activeCliTab)) && (
1377
+ <>
1378
+ <DropdownMenuSeparator />
1379
+ {mode === "chat" ? (
1380
+ shouldShowAgentPanelChatTabBar(tabs, activeTabId) ? (
1381
+ <>
1382
+ <DropdownMenuItem onSelect={() => closeTab(activeTabId)}>
1383
+ <IconX size={14} className="shrink-0" />
1384
+ {t("agentPanel.closeTab")}
1385
+ <DropdownMenuShortcut>
1386
+ {closeTabHint}
1387
+ </DropdownMenuShortcut>
1388
+ </DropdownMenuItem>
1389
+ <DropdownMenuItem
1390
+ onSelect={() => closeOtherTabs(activeTabId)}
1391
+ >
1392
+ {t("agentPanel.closeOtherTabs")}
1393
+ </DropdownMenuItem>
1394
+ <DropdownMenuItem onSelect={() => closeAllTabs()}>
1395
+ {t("agentPanel.closeAllTabs")}
1396
+ <DropdownMenuShortcut>
1397
+ {closeAllTabsHint}
1398
+ </DropdownMenuShortcut>
1399
+ </DropdownMenuItem>
1400
+ </>
1401
+ ) : (
1402
+ <DropdownMenuItem onSelect={clearActiveTab}>
1403
+ <IconX size={14} className="shrink-0" />
1404
+ {t("agentPanel.clearChat")}
1405
+ </DropdownMenuItem>
1406
+ )
1407
+ ) : (
1408
+ <>
1409
+ <DropdownMenuItem
1410
+ onSelect={() => closeCliTab(activeCliTab)}
1411
+ >
1412
+ <IconX size={14} className="shrink-0" />
1413
+ {t("agentPanel.closeTab")}
1414
+ <DropdownMenuShortcut>
1415
+ {closeTabHint}
1416
+ </DropdownMenuShortcut>
1417
+ </DropdownMenuItem>
1418
+ <DropdownMenuItem
1419
+ onSelect={() => closeOtherCliTabs(activeCliTab)}
1420
+ >
1421
+ {t("agentPanel.closeOtherTabs")}
1422
+ </DropdownMenuItem>
1423
+ <DropdownMenuItem onSelect={() => closeAllCliTabs()}>
1424
+ {t("agentPanel.closeAllTabs")}
1425
+ <DropdownMenuShortcut>
1426
+ {closeAllTabsHint}
1427
+ </DropdownMenuShortcut>
1428
+ </DropdownMenuItem>
1429
+ </>
1430
+ )}
1431
+ </>
1432
+ )}
1433
+ </DropdownMenuContent>
1434
+ </DropdownMenu>
1435
+ {onCollapse && (
1436
+ <IconTooltip content={t("agentPanel.collapseSidebar")}>
1437
+ <button
1438
+ type="button"
1439
+ onClick={onCollapse}
1440
+ aria-label={t("agentPanel.collapseSidebar")}
1441
+ className="flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50"
1442
+ >
1443
+ <IconX size={14} />
1444
+ </button>
1445
+ </IconTooltip>
1446
+ )}
1447
+ </div>
1448
+ ),
1449
+ [
1450
+ activeCliTab,
1451
+ addCliTab,
1452
+ allowSettingsMode,
1453
+ availableClis,
1454
+ canUseCodeTools,
1455
+ closeAllCliTabs,
1456
+ closeAllTabsHint,
1457
+ closeCliTab,
1458
+ closeOtherCliTabs,
1459
+ closeTabHint,
1460
+ feedbackOpen,
1461
+ getChatThreadShareUrl,
1462
+ headerMenuOpen,
1463
+ isFullscreen,
1464
+ mode,
1465
+ agentPageHref,
1466
+ onCollapse,
1467
+ onToggleFullscreen,
1468
+ openRunThread,
1469
+ selectCli,
1470
+ selectedCli,
1471
+ storageKey,
1472
+ switchMode,
1473
+ t,
1474
+ ],
1475
+ );
1476
+
1477
+ const renderPageChatOverlay = useCallback(
1478
+ ({
1479
+ activeTabId,
1480
+ activeTabMessageCount,
1481
+ addTab,
1482
+ tabs,
1483
+ }: MultiTabAssistantChatHeaderProps) => {
1484
+ if (
1485
+ !shouldShowAgentPanelPageNewChatButton(
1486
+ tabs,
1487
+ activeTabId,
1488
+ activeTabMessageCount,
1489
+ )
1490
+ ) {
1491
+ return null;
1492
+ }
1493
+ const activeTab = activeTabId
1494
+ ? tabs.find((tab) => tab.id === activeTabId)
1495
+ : undefined;
1496
+ const canShareActiveTab =
1497
+ activeTab && (activeTabMessageCount > 0 || activeTab.status !== "idle");
1498
+
1499
+ return (
1500
+ <>
1501
+ <div
1502
+ aria-hidden="true"
1503
+ data-agent-page-chat-fade=""
1504
+ className="pointer-events-none absolute inset-x-0 top-0 z-50 h-16 bg-gradient-to-b from-background via-background/90 to-transparent opacity-0 transition-opacity duration-150"
1505
+ />
1506
+ <div className="pointer-events-none absolute inset-x-0 top-3 z-[60] flex justify-end px-3 sm:top-4 sm:px-4">
1507
+ <div className="pointer-events-auto flex items-center gap-1">
1508
+ {canShareActiveTab ? (
1509
+ <ShareButton
1510
+ resourceType="chat_thread"
1511
+ resourceId={activeTab.id}
1512
+ resourceTitle={activeTab.label || "Chat"}
1513
+ shareUrl={getChatThreadShareUrl(activeTab.id)}
1514
+ trigger="icon"
1515
+ triggerClassName="h-8 w-8 border border-border bg-background/95 shadow-sm backdrop-blur hover:bg-accent"
1516
+ />
1517
+ ) : null}
1518
+ <button
1519
+ type="button"
1520
+ data-agent-page-new-chat=""
1521
+ aria-label={t("agentPanel.newChat")}
1522
+ onClick={() => {
1523
+ addTab();
1524
+ }}
1525
+ className="inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background/95 px-2.5 text-xs font-medium text-foreground shadow-sm backdrop-blur transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1526
+ >
1527
+ <IconPlus size={14} />
1528
+ <span>{t("agentPanel.newChat")}</span>
1529
+ </button>
1530
+ </div>
1531
+ </div>
1532
+ </>
1533
+ );
1534
+ },
1535
+ [getChatThreadShareUrl, t],
1536
+ );
1537
+
1538
+ // Ref callback: scroll the active tab into view in the overflow container.
1539
+ // Uses getBoundingClientRect for reliable positioning regardless of offsetParent.
1540
+ const activeTabRefCb = useCallback((el: HTMLDivElement | null) => {
1541
+ if (!el) return;
1542
+ const container = el.parentElement;
1543
+ if (!container) return;
1544
+ // Use rAF so layout is settled after React commit
1545
+ requestAnimationFrame(() => {
1546
+ const containerRect = container.getBoundingClientRect();
1547
+ const tabRect = el.getBoundingClientRect();
1548
+ if (tabRect.left < containerRect.left) {
1549
+ container.scrollLeft += tabRect.left - containerRect.left;
1550
+ } else if (tabRect.right > containerRect.right) {
1551
+ container.scrollLeft += tabRect.right - containerRect.right;
1552
+ }
1553
+ });
1554
+ }, []);
1555
+
1556
+ const renderChatHeader = useCallback(
1557
+ ({
1558
+ tabs,
1559
+ activeTabId,
1560
+ activeTabMessageCount,
1561
+ setActiveTabId,
1562
+ addTab,
1563
+ clearActiveTab,
1564
+ closeTab,
1565
+ closeOtherTabs,
1566
+ closeAllTabs,
1567
+ showHistory,
1568
+ toggleHistory,
1569
+ }: MultiTabAssistantChatHeaderProps) => {
1570
+ const { activeTab, childTabs, focusParentId, hasSubTabs, mainTabs } =
1571
+ getAgentPanelChatTabGroups(tabs, activeTabId);
1572
+ const showSidebarChatTabs =
1573
+ Boolean(onCollapse) &&
1574
+ mode === "chat" &&
1575
+ shouldShowAgentPanelSidebarChatTabs(tabs);
1576
+
1577
+ return (
1578
+ <div
1579
+ className="agent-sidebar-chat-header flex flex-col shrink-0"
1580
+ data-agent-sidebar-chat-header={onCollapse ? "" : undefined}
1581
+ data-agent-sidebar-chat-header-active={
1582
+ headerMenuOpen || feedbackOpen ? "" : undefined
1583
+ }
1584
+ >
1585
+ {/* Top bar: chat tabs/mode buttons + actions */}
1586
+ <div
1587
+ className={cn(
1588
+ AGENT_PANEL_HEADER_CLASS,
1589
+ !onCollapse && "border-b border-border",
1590
+ )}
1591
+ style={AGENT_PANEL_HEADER_STYLE}
1592
+ >
1593
+ <div className="flex min-w-0 flex-1 items-center gap-1 overflow-hidden">
1594
+ {showSidebarChatTabs ? (
1595
+ <div className="agent-tabs-scroll flex min-w-0 flex-1 items-center gap-0.5 overflow-x-auto">
1596
+ {mainTabs.map((tab) => {
1597
+ const isActive =
1598
+ tab.id === activeTabId ||
1599
+ (tab.id === focusParentId &&
1600
+ activeTab?.parentThreadId === tab.id);
1601
+ return (
1602
+ <div
1603
+ key={tab.id}
1604
+ role="button"
1605
+ tabIndex={0}
1606
+ ref={isActive ? activeTabRefCb : undefined}
1607
+ onClick={() => setActiveTabId(tab.id)}
1608
+ onKeyDown={activateOnKeyDown(() =>
1609
+ setActiveTabId(tab.id),
1610
+ )}
1611
+ className={cn(
1612
+ "agent-tab relative flex shrink-0 items-center gap-1 rounded-md px-2.5 py-1.5 text-[11px] font-medium cursor-pointer max-w-[150px]",
1613
+ isActive
1614
+ ? "bg-accent text-foreground"
1615
+ : "text-muted-foreground hover:bg-accent hover:text-foreground",
1616
+ )}
1617
+ >
1618
+ <span className="truncate pe-1">{tab.label}</span>
1619
+ {tab.status === "running" && (
1620
+ <span className="h-1.5 w-1.5 shrink-0 rounded-full bg-muted-foreground/50 animate-pulse" />
1621
+ )}
1622
+ <button
1623
+ type="button"
1624
+ aria-label="Close tab"
1625
+ onClick={(e) => {
1626
+ e.stopPropagation();
1627
+ closeTab(tab.id);
1628
+ }}
1629
+ className="agent-tab-close flex items-center justify-end text-muted-foreground hover:text-foreground"
1630
+ style={{
1631
+ position: "absolute",
1632
+ right: 0,
1633
+ top: 0,
1634
+ bottom: 0,
1635
+ width: 28,
1636
+ paddingRight: 6,
1637
+ borderRadius: "0 6px 6px 0",
1638
+ background:
1639
+ "linear-gradient(to right, transparent, hsl(var(--accent)) 40%)",
1640
+ }}
1641
+ >
1642
+ <IconX size={10} />
1643
+ </button>
1644
+ </div>
1645
+ );
1646
+ })}
1647
+ </div>
1648
+ ) : shouldShowAgentPanelModeButtons(Boolean(onCollapse)) ? (
1649
+ renderModeButtons(mode)
1650
+ ) : null}
1651
+ </div>
1652
+ <div className="flex items-center gap-0.5">
1653
+ {renderHeaderActions({
1654
+ activeChatSessionId: activeTabId,
1655
+ activeTabId,
1656
+ activeTabMessageCount,
1657
+ addTab,
1658
+ clearActiveTab,
1659
+ closeAllTabs,
1660
+ closeOtherTabs,
1661
+ closeTab,
1662
+ showHistory,
1663
+ tabs,
1664
+ toggleHistory,
1665
+ })}
1666
+ </div>
1667
+ </div>
1668
+ {mode === "chat" && chatNotice ? (
1669
+ <div className="border-b border-border">{chatNotice}</div>
1670
+ ) : null}
1671
+ {/* Tab bar: only visible when there is actually more than one tab to switch between. */}
1672
+ {showTabBar &&
1673
+ (mode === "chat" || (mode === "cli" && canUseCodeTools)) &&
1674
+ (() => {
1675
+ const showChatTabBar =
1676
+ mode === "chat" &&
1677
+ shouldShowAgentPanelChatTabBar(tabs, activeTabId);
1678
+ const showCliTabBar =
1679
+ mode === "cli" &&
1680
+ canUseCodeTools &&
1681
+ shouldShowAgentPanelCliTabBar(cliTabs);
1682
+
1683
+ if (!showChatTabBar && !showCliTabBar) return null;
1684
+
1685
+ return (
1686
+ <>
1687
+ {!showSidebarChatTabs && (
1688
+ <div className="flex items-center px-2 py-1 border-b border-border gap-0.5">
1689
+ <div className="agent-tabs-scroll flex items-center gap-0.5 min-w-0 overflow-x-auto flex-1">
1690
+ {mode === "chat"
1691
+ ? mainTabs.map((tab) => {
1692
+ // Highlight the parent tab if a child is active
1693
+ const isActive =
1694
+ tab.id === activeTabId ||
1695
+ (tab.id === focusParentId &&
1696
+ activeTab?.parentThreadId === tab.id);
1697
+ return (
1698
+ <div
1699
+ key={tab.id}
1700
+ role="button"
1701
+ tabIndex={0}
1702
+ ref={isActive ? activeTabRefCb : undefined}
1703
+ onClick={() => setActiveTabId(tab.id)}
1704
+ onKeyDown={activateOnKeyDown(() =>
1705
+ setActiveTabId(tab.id),
1706
+ )}
1707
+ className={cn(
1708
+ "agent-tab relative flex shrink-0 items-center gap-1 rounded-md px-2.5 py-1.5 text-[11px] font-medium cursor-pointer max-w-[150px]",
1709
+ isActive
1710
+ ? "bg-accent text-foreground"
1711
+ : "text-muted-foreground hover:bg-accent hover:text-foreground",
1712
+ )}
1713
+ >
1714
+ <span className="truncate pe-1">
1715
+ {tab.label}
1716
+ </span>
1717
+ {tab.status === "running" && (
1718
+ <span className="h-1.5 w-1.5 shrink-0 rounded-full bg-muted-foreground/50 animate-pulse" />
1719
+ )}
1720
+ <button
1721
+ type="button"
1722
+ aria-label="Close tab"
1723
+ onClick={(e) => {
1724
+ e.stopPropagation();
1725
+ closeTab(tab.id);
1726
+ }}
1727
+ className="agent-tab-close flex items-center justify-end text-muted-foreground hover:text-foreground"
1728
+ style={{
1729
+ position: "absolute",
1730
+ right: 0,
1731
+ top: 0,
1732
+ bottom: 0,
1733
+ width: 28,
1734
+ paddingRight: 6,
1735
+ borderRadius: "0 6px 6px 0",
1736
+ background:
1737
+ "linear-gradient(to right, transparent, hsl(var(--accent)) 40%)",
1738
+ }}
1739
+ >
1740
+ <IconX size={10} />
1741
+ </button>
1742
+ </div>
1743
+ );
1744
+ })
1745
+ : cliTabs.map((id, i) => (
1746
+ <div
1747
+ key={id}
1748
+ role="button"
1749
+ tabIndex={0}
1750
+ ref={
1751
+ id === activeCliTab
1752
+ ? activeTabRefCb
1753
+ : undefined
1754
+ }
1755
+ onClick={() => setActiveCliTab(id)}
1756
+ onKeyDown={activateOnKeyDown(() =>
1757
+ setActiveCliTab(id),
1758
+ )}
1759
+ className={cn(
1760
+ "agent-tab relative flex shrink-0 items-center gap-1 rounded-md px-2.5 py-1.5 text-[11px] font-medium cursor-pointer",
1761
+ id === activeCliTab
1762
+ ? "bg-accent text-foreground"
1763
+ : "text-muted-foreground hover:bg-accent hover:text-foreground",
1764
+ )}
1765
+ >
1766
+ <span>Terminal {i + 1}</span>
1767
+ <button
1768
+ type="button"
1769
+ aria-label="Close tab"
1770
+ onClick={(e) => {
1771
+ e.stopPropagation();
1772
+ closeCliTab(id);
1773
+ }}
1774
+ className="agent-tab-close flex items-center justify-end text-muted-foreground hover:text-foreground"
1775
+ style={{
1776
+ position: "absolute",
1777
+ right: 0,
1778
+ top: 0,
1779
+ bottom: 0,
1780
+ width: 28,
1781
+ paddingRight: 6,
1782
+ borderRadius: "0 6px 6px 0",
1783
+ background:
1784
+ "linear-gradient(to right, transparent, hsl(var(--accent)) 40%)",
1785
+ }}
1786
+ >
1787
+ <IconX size={10} />
1788
+ </button>
1789
+ </div>
1790
+ ))}
1791
+ </div>
1792
+ </div>
1793
+ )}
1794
+ {/* Sub-agent tab row — shown when the active context has children */}
1795
+ {mode === "chat" && hasSubTabs && (
1796
+ <div
1797
+ className={cn(
1798
+ "flex items-center px-2 py-0.5 gap-0.5 bg-muted/30",
1799
+ !showSidebarChatTabs && "border-b border-border",
1800
+ )}
1801
+ >
1802
+ <div className="agent-tabs-scroll flex items-center gap-0.5 min-w-0 overflow-x-auto flex-1">
1803
+ <div
1804
+ role="button"
1805
+ tabIndex={0}
1806
+ onClick={() => setActiveTabId(focusParentId)}
1807
+ onKeyDown={activateOnKeyDown(() =>
1808
+ setActiveTabId(focusParentId),
1809
+ )}
1810
+ className={cn(
1811
+ "flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium cursor-pointer",
1812
+ activeTabId === focusParentId
1813
+ ? "bg-accent text-foreground"
1814
+ : "text-muted-foreground hover:bg-accent hover:text-foreground",
1815
+ )}
1816
+ >
1817
+ Main
1818
+ </div>
1819
+ {childTabs.map((tab) => (
1820
+ <div
1821
+ key={tab.id}
1822
+ role="button"
1823
+ tabIndex={0}
1824
+ ref={
1825
+ tab.id === activeTabId
1826
+ ? activeTabRefCb
1827
+ : undefined
1828
+ }
1829
+ onClick={() => setActiveTabId(tab.id)}
1830
+ onKeyDown={activateOnKeyDown(() =>
1831
+ setActiveTabId(tab.id),
1832
+ )}
1833
+ className={cn(
1834
+ "agent-tab relative flex shrink-0 items-center gap-1 rounded-md px-2 py-1 text-[10px] font-medium cursor-pointer max-w-[140px]",
1835
+ tab.id === activeTabId
1836
+ ? "bg-accent text-foreground"
1837
+ : "text-muted-foreground hover:bg-accent hover:text-foreground",
1838
+ )}
1839
+ >
1840
+ <span className="truncate pe-1">
1841
+ {tab.subAgentName || tab.label}
1842
+ </span>
1843
+ {tab.status === "running" && (
1844
+ <span className="h-1 w-1 shrink-0 rounded-full bg-muted-foreground/50 animate-pulse" />
1845
+ )}
1846
+ <button
1847
+ type="button"
1848
+ aria-label="Close tab"
1849
+ onClick={(e) => {
1850
+ e.stopPropagation();
1851
+ closeTab(tab.id);
1852
+ }}
1853
+ className="agent-tab-close flex items-center justify-end text-muted-foreground hover:text-foreground"
1854
+ style={{
1855
+ position: "absolute",
1856
+ right: 0,
1857
+ top: 0,
1858
+ bottom: 0,
1859
+ width: 24,
1860
+ paddingRight: 4,
1861
+ borderRadius: "0 6px 6px 0",
1862
+ background:
1863
+ "linear-gradient(to right, transparent, hsl(var(--accent)) 40%)",
1864
+ }}
1865
+ >
1866
+ <IconX size={8} />
1867
+ </button>
1868
+ </div>
1869
+ ))}
1870
+ </div>
1871
+ </div>
1872
+ )}
1873
+ </>
1874
+ );
1875
+ })()}
1876
+ </div>
1877
+ );
1878
+ },
1879
+ [
1880
+ mode,
1881
+ renderHeaderActions,
1882
+ renderModeButtons,
1883
+ chatNotice,
1884
+ canUseCodeTools,
1885
+ feedbackOpen,
1886
+ headerMenuOpen,
1887
+ onCollapse,
1888
+ showTabBar,
1889
+ cliTabs,
1890
+ activeCliTab,
1891
+ activeTabRefCb,
1892
+ activateOnKeyDown,
1893
+ closeCliTab,
1894
+ ],
1895
+ );
1896
+
1897
+ return (
1898
+ <div
1899
+ className={cn(
1900
+ "agent-panel-root flex flex-1 flex-col min-h-0 h-full text-[13px] leading-[1.2] antialiased",
1901
+ className,
1902
+ )}
1903
+ style={{ ...AGENT_PANEL_ROOT_STYLE, ...style }}
1904
+ data-agent-fullscreen={isFullscreen ? "true" : undefined}
1905
+ >
1906
+ {/* Tailwind group-hover/tab doesn't work in core package — inject directly.
1907
+ Fullscreen rules center the message stream and composer to a Claude-style
1908
+ column while leaving the header bar at full width so the action buttons
1909
+ stay pinned to the top corners. */}
1910
+ <style
1911
+ dangerouslySetInnerHTML={{
1912
+ __html:
1913
+ "@media (hover:hover) and (pointer:fine){" +
1914
+ ".agent-sidebar-chat-header[data-agent-sidebar-chat-header]{opacity:0;pointer-events:none;transition:opacity 150ms ease-out;}" +
1915
+ ".agent-panel-root:hover .agent-sidebar-chat-header[data-agent-sidebar-chat-header],.agent-panel-root:focus-within .agent-sidebar-chat-header[data-agent-sidebar-chat-header],.agent-sidebar-chat-header[data-agent-sidebar-chat-header][data-agent-sidebar-chat-header-active]{opacity:1;pointer-events:auto;}" +
1916
+ "}" +
1917
+ ".agent-tab-close{opacity:0}.agent-tab:hover .agent-tab-close{opacity:1}" +
1918
+ ".agent-tabs-scroll{scrollbar-width:none;-ms-overflow-style:none;}" +
1919
+ ".agent-tabs-scroll::-webkit-scrollbar{display:none;}" +
1920
+ `[data-agent-fullscreen='true'] .agent-thread-content,` +
1921
+ `[data-agent-fullscreen='true'] .agent-running-activity{` +
1922
+ `max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
1923
+ `margin-left:auto;margin-right:auto;width:100%;}` +
1924
+ `[data-agent-fullscreen='true'] .agent-composer-area,` +
1925
+ `[data-agent-fullscreen='true'] .agent-plan-mode-callout{` +
1926
+ `max-width:${FULLSCREEN_CHAT_COLUMN_MAX_PX}px;` +
1927
+ `margin-left:auto;margin-right:auto;width:100%;}`,
1928
+ }}
1929
+ />
1930
+ {/* Framework onboarding — appears above the chat/cli/settings tabs
1931
+ so it's visible regardless of which tab the user is on. The panel
1932
+ hides itself once all required steps are done or the user dismisses
1933
+ it. */}
1934
+ {SHOW_ONBOARDING && mounted && (
1935
+ <Suspense fallback={null}>
1936
+ <OnboardingPanel />
1937
+ </Suspense>
1938
+ )}
1939
+
1940
+ {/* Chat view — always mounted to preserve state.
1941
+ Header (with tabs + mode buttons) is always visible.
1942
+ Chat content is hidden when CLI or resources mode is active.
1943
+ The wrapper collapses (no flex-1) when another mode is active
1944
+ so it only takes the height of its header.
1945
+ The Suspense boundary renders the header chrome immediately while
1946
+ the lazy assistant-ui chunk loads in the background. */}
1947
+ <div
1948
+ className={cn(
1949
+ "flex flex-col min-h-0",
1950
+ mode === "chat" ? "flex-1" : "shrink-0",
1951
+ )}
1952
+ >
1953
+ {mounted && (
1954
+ <Suspense
1955
+ fallback={
1956
+ <ChatLoadingSkeleton
1957
+ renderHeader={showHeader ? renderChatHeader : undefined}
1958
+ centerComposerWhenEmpty={
1959
+ assistantChatProps.centerComposerWhenEmpty
1960
+ }
1961
+ composerSlot={assistantChatProps.composerSlot}
1962
+ composerAreaClassName={assistantChatProps.composerAreaClassName}
1963
+ composerLayoutVariant={assistantChatProps.composerLayoutVariant}
1964
+ />
1965
+ }
1966
+ >
1967
+ <MultiTabAssistantChatLazy
1968
+ {...assistantChatProps}
1969
+ agentChatSurface={effectiveAgentChatSurface}
1970
+ apiUrl={apiUrl}
1971
+ showHeader={false}
1972
+ renderHeader={showHeader ? renderChatHeader : undefined}
1973
+ showTabBar={showTabBar}
1974
+ renderOverlay={
1975
+ showPageNewChatButton && !showHeader
1976
+ ? renderPageChatOverlay
1977
+ : undefined
1978
+ }
1979
+ contentHidden={mode !== "chat"}
1980
+ emptyStateText={emptyStateText}
1981
+ emptyStateAddon={emptyStateAddon}
1982
+ suggestions={suggestions}
1983
+ dynamicSuggestions={dynamicSuggestions}
1984
+ onSwitchToCli={() => switchMode("cli")}
1985
+ execMode={execMode}
1986
+ onExecModeChange={switchExecMode}
1987
+ storageKey={storageKey}
1988
+ restoreActiveThread={restoreActiveThread}
1989
+ scope={scope}
1990
+ showScopeBadge={showScopeBadge}
1991
+ browserTabId={browserTabId}
1992
+ threadUrlSync={threadUrlSync}
1993
+ />
1994
+ </Suspense>
1995
+ )}
1996
+ </div>
1997
+
1998
+ {/* CLI terminals — code-capable dev mode: real terminal, otherwise handoff. */}
1999
+ {canUseCodeTools
2000
+ ? mode === "cli" &&
2001
+ cliTabs.map((id) => (
2002
+ <div
2003
+ key={id}
2004
+ className="min-h-0 relative flex-1"
2005
+ style={{
2006
+ display: id === activeCliTab ? undefined : "none",
2007
+ }}
2008
+ >
2009
+ <Suspense
2010
+ fallback={
2011
+ <div className="flex items-center justify-center h-full text-muted-foreground text-sm">
2012
+ Loading terminal...
2013
+ </div>
2014
+ }
2015
+ >
2016
+ <AgentTerminal
2017
+ command={selectedCli}
2018
+ hideInFrame={false}
2019
+ className="h-full"
2020
+ style={{ background: "transparent" }}
2021
+ />
2022
+ </Suspense>
2023
+ </div>
2024
+ ))
2025
+ : mode === "cli" && (
2026
+ <div className="flex flex-1 flex-col items-center justify-center min-h-0 px-6 gap-3">
2027
+ <CodeAccessUnavailablePanel
2028
+ title={
2029
+ codeAccessEnabled
2030
+ ? t("agentPanel.cliRequiresDevMode")
2031
+ : codeUnavailableTitle
2032
+ }
2033
+ description={
2034
+ codeAccessEnabled
2035
+ ? t("agentPanel.cliRequiresDevModeDescription")
2036
+ : codeUnavailableDescription
2037
+ }
2038
+ ctaLabel={codeUnavailableCtaLabel}
2039
+ ctaHref={codeAccessEnabled ? undefined : codeUnavailableCtaHref}
2040
+ secondaryCtaLabel={codeUnavailableSecondaryCtaLabel}
2041
+ secondaryCtaHref={codeUnavailableSecondaryCtaHref}
2042
+ />
2043
+ </div>
2044
+ )}
2045
+
2046
+ {/* Resources view */}
2047
+ {mode === "resources" && (
2048
+ <div className="flex flex-1 flex-col min-h-0">
2049
+ <Suspense
2050
+ fallback={
2051
+ <div className="flex h-full flex-col min-h-0">
2052
+ <div className="flex shrink-0 items-center justify-between border-b border-border px-2 py-1.5">
2053
+ <div className="flex items-center gap-1">
2054
+ <div className="h-5 w-16 rounded bg-muted animate-pulse" />
2055
+ <div className="h-5 w-14 rounded bg-muted animate-pulse" />
2056
+ </div>
2057
+ </div>
2058
+ </div>
2059
+ }
2060
+ >
2061
+ <ResourcesPanel />
2062
+ </Suspense>
2063
+ </div>
2064
+ )}
2065
+
2066
+ {/* Settings / Setup view */}
2067
+ {mode === "settings" && (
2068
+ <div className="flex flex-col flex-1 min-h-0">
2069
+ <Suspense
2070
+ fallback={
2071
+ <div className="p-3 space-y-2">
2072
+ <div className="h-10 w-full rounded-lg bg-muted animate-pulse" />
2073
+ <div className="h-10 w-full rounded-lg bg-muted animate-pulse" />
2074
+ <div className="h-10 w-full rounded-lg bg-muted animate-pulse" />
2075
+ </div>
2076
+ }
2077
+ >
2078
+ <SettingsPanel
2079
+ isDevMode={isDevMode}
2080
+ onToggleDevMode={() => setDevMode(!isDevMode)}
2081
+ showDevToggle={showDevToggle}
2082
+ devAppUrl={devAppUrl}
2083
+ initialSection={settingsSection.section}
2084
+ sectionRequestKey={settingsSection.requestKey}
2085
+ />
2086
+ </Suspense>
2087
+ </div>
2088
+ )}
2089
+ </div>
2090
+ );
2091
+ }
2092
+
2093
+ // ─── Resize handle ──────────────────────────────────────────────────────────
2094
+
2095
+ const SIDEBAR_STORAGE_KEY = "agent-native-sidebar-width";
2096
+ const SIDEBAR_FULLSCREEN_KEY = "agent-native-sidebar-fullscreen";
2097
+ const SIDEBAR_MIN = 280;
2098
+ const SIDEBAR_MAX = 700;
2099
+ const SIDEBAR_ANIMATION_MS = 260;
2100
+ const SIDEBAR_OVERLAY_Z_INDEX = 70;
2101
+ const SIDEBAR_FULLSCREEN_Z_INDEX = 90;
2102
+ /** Shared max width of the centered fullscreen chat column and composer. */
2103
+ const FULLSCREEN_CHAT_COLUMN_MAX_PX = 684;
2104
+
2105
+ function ResizeHandle({
2106
+ position,
2107
+ onDrag,
2108
+ onResizeStart,
2109
+ onResizeEnd,
2110
+ }: {
2111
+ position: "left" | "right";
2112
+ onDrag: (delta: number) => void;
2113
+ onResizeStart: () => void;
2114
+ onResizeEnd: () => void;
2115
+ }) {
2116
+ const ref = useRef<HTMLDivElement>(null);
2117
+ const dragging = useRef(false);
2118
+ const lastX = useRef(0);
2119
+ const onDragRef = useRef(onDrag);
2120
+ const onResizeStartRef = useRef(onResizeStart);
2121
+ const onResizeEndRef = useRef(onResizeEnd);
2122
+ onDragRef.current = onDrag;
2123
+ onResizeStartRef.current = onResizeStart;
2124
+ onResizeEndRef.current = onResizeEnd;
2125
+ const GRAB_ZONE = 5; // px on each side of the border
2126
+
2127
+ // All drag logic runs via document-level listeners so the 1px-wide
2128
+ // element doesn't need to capture pointer events itself.
2129
+ useEffect(() => {
2130
+ const el = ref.current;
2131
+ if (!el) return;
2132
+ let cursorActive = false;
2133
+
2134
+ function onMouseDown(e: MouseEvent) {
2135
+ const rect = el!.getBoundingClientRect();
2136
+ const dist = Math.abs(e.clientX - (rect.left + rect.width / 2));
2137
+ if (dist > GRAB_ZONE) return;
2138
+ e.preventDefault();
2139
+ dragging.current = true;
2140
+ lastX.current = e.clientX;
2141
+ onResizeStartRef.current();
2142
+ document.body.style.cursor = "col-resize";
2143
+ document.body.style.userSelect = "none";
2144
+ }
2145
+
2146
+ function onMouseMove(e: MouseEvent) {
2147
+ if (dragging.current) {
2148
+ const delta = e.clientX - lastX.current;
2149
+ lastX.current = e.clientX;
2150
+ onDragRef.current(position === "left" ? delta : -delta);
2151
+ return;
2152
+ }
2153
+ // Hover cursor
2154
+ const rect = el!.getBoundingClientRect();
2155
+ const dist = Math.abs(e.clientX - (rect.left + rect.width / 2));
2156
+ const near = dist <= GRAB_ZONE;
2157
+ if (near && !cursorActive) {
2158
+ cursorActive = true;
2159
+ document.body.style.cursor = "col-resize";
2160
+ } else if (!near && cursorActive) {
2161
+ cursorActive = false;
2162
+ document.body.style.cursor = "";
2163
+ }
2164
+ }
2165
+
2166
+ function endDrag() {
2167
+ if (!dragging.current) return;
2168
+ dragging.current = false;
2169
+ document.body.style.cursor = "";
2170
+ document.body.style.userSelect = "";
2171
+ onResizeEndRef.current();
2172
+ }
2173
+
2174
+ // mouseup covers the normal release-inside-the-page case; window blur
2175
+ // covers releasing the button outside the browser window/iframe (e.g.
2176
+ // dragging the sidebar wide and letting go over the OS chrome), which
2177
+ // never delivers a mouseup to this document. Without both, a drag that
2178
+ // ends abnormally — or this effect re-running/unmounting mid-drag —
2179
+ // could leave `document.body.style.userSelect` stuck at "none",
2180
+ // silently breaking text selection/copy everywhere in the app.
2181
+ document.addEventListener("mousedown", onMouseDown);
2182
+ document.addEventListener("mousemove", onMouseMove);
2183
+ document.addEventListener("mouseup", endDrag);
2184
+ window.addEventListener("blur", endDrag);
2185
+ return () => {
2186
+ document.removeEventListener("mousedown", onMouseDown);
2187
+ document.removeEventListener("mousemove", onMouseMove);
2188
+ document.removeEventListener("mouseup", endDrag);
2189
+ window.removeEventListener("blur", endDrag);
2190
+ if (cursorActive) document.body.style.cursor = "";
2191
+ // Always clear regardless of `dragging`/`cursorActive` state — this
2192
+ // effect can unmount or re-run (position change, sidebar layout
2193
+ // change) while a drag is in flight, and a stuck "none" here disables
2194
+ // selection app-wide until reload.
2195
+ document.body.style.userSelect = "";
2196
+ dragging.current = false;
2197
+ onResizeEndRef.current();
2198
+ };
2199
+ }, [position]);
2200
+
2201
+ return (
2202
+ <div
2203
+ ref={ref}
2204
+ className={cn(
2205
+ "agent-sidebar-resize-handle relative z-20 w-px shrink-0 touch-none select-none bg-transparent transition-colors hover:bg-border active:bg-border",
2206
+ )}
2207
+ style={{ cursor: "col-resize" }}
2208
+ />
2209
+ );
2210
+ }
2211
+
2212
+ /**
2213
+ * Syncs the current URL (pathname + search + hash) to application_state
2214
+ * under `__url__`, and processes one-shot URL-update commands the agent
2215
+ * writes to `__set_url__`. Lives inside AgentSidebar so every framework
2216
+ * template gets URL visibility + URL-write capability for its agent
2217
+ * without per-template wiring.
2218
+ *
2219
+ * Two directions:
2220
+ * UI → state — on route change, write `{ pathname, search, hash,
2221
+ * searchParams }` to `__url__`. The production agent reads
2222
+ * this and includes it in the auto-injected `<current-url>`
2223
+ * block, so the agent always knows what page the user is
2224
+ * on, including filter/search params like `?f_date=2026-01`.
2225
+ *
2226
+ * state → UI — the framework's `set-search-params` / `set-url-path`
2227
+ * tools write a command to `__set_url__`. This hook reads
2228
+ * the command, applies it via react-router, then deletes
2229
+ * the key. The UI reacts in one tick, no page reload.
2230
+ */
2231
+ const SAFE_BROWSER_TAB_ID_RE = /^[A-Za-z0-9_-]{1,96}$/;
2232
+
2233
+ function URLSync({ browserTabId }: { browserTabId?: string }) {
2234
+ const location = useLocation();
2235
+ const navigate = useNavigate();
2236
+ const queryClient = useQueryClient();
2237
+ const normalizedBrowserTabId = React.useMemo(() => {
2238
+ if (typeof browserTabId !== "string") return undefined;
2239
+ const trimmed = browserTabId.trim();
2240
+ return SAFE_BROWSER_TAB_ID_RE.test(trimmed) ? trimmed : undefined;
2241
+ }, [browserTabId]);
2242
+ const appStateKey = React.useCallback(
2243
+ (key: string) =>
2244
+ normalizedBrowserTabId ? `${key}:${normalizedBrowserTabId}` : key,
2245
+ [normalizedBrowserTabId],
2246
+ );
2247
+ const setUrlQueryKey = React.useMemo(
2248
+ () => ["__set_url__", normalizedBrowserTabId ?? "global"],
2249
+ [normalizedBrowserTabId],
2250
+ );
2251
+
2252
+ // Outbound: write the current URL to app-state whenever it changes.
2253
+ React.useEffect(() => {
2254
+ const searchParams: Record<string, string> = {};
2255
+ for (const [k, v] of new URLSearchParams(location.search).entries()) {
2256
+ searchParams[k] = v;
2257
+ }
2258
+ const body = {
2259
+ pathname: location.pathname,
2260
+ search: location.search,
2261
+ hash: location.hash,
2262
+ searchParams,
2263
+ };
2264
+ const write = (key: string) =>
2265
+ fetch(agentNativePath(`/_agent-native/application-state/${key}`), {
2266
+ method: "PUT",
2267
+ keepalive: true,
2268
+ headers: { "Content-Type": "application/json" },
2269
+ body: JSON.stringify(body),
2270
+ }).catch(() => {});
2271
+ write(appStateKey("__url__"));
2272
+ if (normalizedBrowserTabId) write("__url__");
2273
+ }, [
2274
+ appStateKey,
2275
+ location.pathname,
2276
+ location.search,
2277
+ location.hash,
2278
+ normalizedBrowserTabId,
2279
+ ]);
2280
+
2281
+ // Inbound: poll for URL-update commands from the agent. `useDbSync`
2282
+ // invalidates this key on every relevant app-state event, so default
2283
+ // `structuralSharing: true` is critical — without it, repeated reads of the
2284
+ // same stale command (when the consume-DELETE below races against the next
2285
+ // invalidation) churned the useEffect and re-applied the navigation in a
2286
+ // tight loop. With structural sharing on, the previous reference is reused
2287
+ // when the JSON is unchanged so the useEffect only fires when the command
2288
+ // actually changes; the `lastProcessedDedupKeyRef` below covers the residual
2289
+ // race window after the cache is cleared to `null`.
2290
+ const { data: command } = useQuery<{
2291
+ key: string;
2292
+ command: {
2293
+ pathname?: string;
2294
+ searchParams?: Record<string, string | null>;
2295
+ mergeSearchParams?: boolean;
2296
+ hash?: string;
2297
+ _writeId?: string;
2298
+ };
2299
+ } | null>({
2300
+ queryKey: setUrlQueryKey,
2301
+ queryFn: async () => {
2302
+ const read = async (key: string) => {
2303
+ const res = await fetch(
2304
+ agentNativePath(`/_agent-native/application-state/${key}`),
2305
+ );
2306
+ if (!res.ok || res.status === 204) return null;
2307
+ const text = await res.text();
2308
+ if (!text) return null;
2309
+ const data = JSON.parse(text);
2310
+ return data ? { key, command: data } : null;
2311
+ };
2312
+ try {
2313
+ return (
2314
+ (normalizedBrowserTabId
2315
+ ? await read(appStateKey("__set_url__"))
2316
+ : null) ?? (await read("__set_url__"))
2317
+ );
2318
+ } catch {
2319
+ return null;
2320
+ }
2321
+ },
2322
+ retry: false,
2323
+ });
2324
+
2325
+ const lastProcessedDedupKeyRef = React.useRef<string | null>(null);
2326
+
2327
+ React.useEffect(() => {
2328
+ if (!command) return;
2329
+ const cmd = command.command;
2330
+ const dedupKey =
2331
+ cmd._writeId ??
2332
+ JSON.stringify({
2333
+ pathname: cmd.pathname,
2334
+ searchParams: cmd.searchParams,
2335
+ mergeSearchParams: cmd.mergeSearchParams,
2336
+ hash: cmd.hash,
2337
+ });
2338
+ if (lastProcessedDedupKeyRef.current === dedupKey) {
2339
+ // Same command we already handled — the DELETE below races against the
2340
+ // next polling refetch, so when it loses the same command can show up
2341
+ // again on the next tick. Re-fire DELETE and bail rather than navigate
2342
+ // again.
2343
+ fetch(
2344
+ agentNativePath(`/_agent-native/application-state/${command.key}`),
2345
+ {
2346
+ method: "DELETE",
2347
+ headers: { "X-Agent-Native-CSRF": "1" },
2348
+ },
2349
+ ).catch(() => {});
2350
+ queryClient.setQueryData(setUrlQueryKey, null);
2351
+ return;
2352
+ }
2353
+ lastProcessedDedupKeyRef.current = dedupKey;
2354
+
2355
+ // Delete the one-shot command before applying so duplicate events
2356
+ // don't cause repeated navigation.
2357
+ fetch(agentNativePath(`/_agent-native/application-state/${command.key}`), {
2358
+ method: "DELETE",
2359
+ headers: { "X-Agent-Native-CSRF": "1" },
2360
+ }).catch(() => {});
2361
+ try {
2362
+ const current = new URL(window.location.href);
2363
+ const nextPath = cmd.pathname ?? current.pathname;
2364
+ const nextSearch =
2365
+ cmd.mergeSearchParams !== false
2366
+ ? new URLSearchParams(current.search)
2367
+ : new URLSearchParams();
2368
+ if (cmd.searchParams) {
2369
+ for (const [k, v] of Object.entries(cmd.searchParams)) {
2370
+ if (v === null || v === "") nextSearch.delete(k);
2371
+ else nextSearch.set(k, v);
2372
+ }
2373
+ }
2374
+ const nextHash = cmd.hash ?? current.hash;
2375
+ const qs = nextSearch.toString();
2376
+ const url = nextPath + (qs ? `?${qs}` : "") + (nextHash || "");
2377
+ // Skip the navigation if the URL is already at the target state —
2378
+ // avoids needless react-router work and any revalidation side-effects
2379
+ // that come with it.
2380
+ // Mark that the agent just wrote the URL so consumers (e.g. a
2381
+ // dashboard restoring saved filter defaults) can skip any auto-
2382
+ // restore that would clobber the agent's change. Set this BEFORE
2383
+ // the same-URL short-circuit — a no-op nav is still an explicit
2384
+ // "agent authored this state" signal that consumers depend on.
2385
+ try {
2386
+ sessionStorage.setItem("__agentUrlAppliedAt__", String(Date.now()));
2387
+ } catch {
2388
+ // sessionStorage unavailable — not fatal.
2389
+ }
2390
+ const currentUrl =
2391
+ current.pathname + (current.search || "") + (current.hash || "");
2392
+ if (url === currentUrl) {
2393
+ queryClient.setQueryData(setUrlQueryKey, null);
2394
+ return;
2395
+ }
2396
+ // Replace rather than push so repeated agent URL updates don't
2397
+ // clutter the history stack and can't trigger extra remounts from
2398
+ // router navigation lifecycle.
2399
+ window.setTimeout(() => navigate(url, { replace: true }), 0);
2400
+ } catch {
2401
+ // Malformed command — ignore.
2402
+ }
2403
+ queryClient.setQueryData(setUrlQueryKey, null);
2404
+ }, [command, navigate, queryClient, setUrlQueryKey]);
2405
+
2406
+ return null;
2407
+ }
2408
+ /**
2409
+ * Remounts its children whenever the framework's `refresh-screen` tool is
2410
+ * invoked. Used inside AgentSidebar so the main content area re-fetches
2411
+ * without disturbing the chat sidebar's in-flight state.
2412
+ *
2413
+ * Two mechanisms work together here:
2414
+ *
2415
+ * 1. Before the remount, every react-query cache entry is marked stale
2416
+ * via `invalidateQueries({ refetchType: "none" })`. This does NOT
2417
+ * trigger a refetch on its own, so active queries elsewhere (chat
2418
+ * sidebar, left nav) keep their current data — they'll refetch only
2419
+ * on their next natural trigger.
2420
+ * 2. The React `key` then bumps, unmounting and remounting the subtree.
2421
+ * On remount, child components re-subscribe to their queries, see
2422
+ * the data is stale, and refetch — regardless of configured
2423
+ * `staleTime`. This is what makes the dashboard pick up the agent's
2424
+ * edits even when the query uses `staleTime: 30_000` or similar.
2425
+ */
2426
+ function ScreenRefreshBoundary({ children }: { children: React.ReactNode }) {
2427
+ const key = useScreenRefreshKey();
2428
+ const queryClient = useQueryClient();
2429
+ const lastKeyRef = React.useRef(key);
2430
+ if (key !== lastKeyRef.current) {
2431
+ lastKeyRef.current = key;
2432
+ // Mark every cached query stale without kicking off a refetch. The
2433
+ // subtree-level refetches happen naturally when the new tree mounts
2434
+ // below and child components re-subscribe.
2435
+ queryClient.invalidateQueries({ refetchType: "none" });
2436
+ }
2437
+ return <React.Fragment key={key}>{children}</React.Fragment>;
2438
+ }
2439
+
2440
+ class AgentPanelErrorBoundary extends React.Component<
2441
+ { children: React.ReactNode; onReset: () => void },
2442
+ { error: Error | null; staleIndexRecoveryCount: number }
2443
+ > {
2444
+ state: { error: Error | null; staleIndexRecoveryCount: number } = {
2445
+ error: null,
2446
+ staleIndexRecoveryCount: 0,
2447
+ };
2448
+
2449
+ private recoveryTimer: ReturnType<typeof setTimeout> | null = null;
2450
+ private recoveryCooldownTimer: ReturnType<typeof setTimeout> | null = null;
2451
+
2452
+ static getDerivedStateFromError(error: Error) {
2453
+ return { error };
2454
+ }
2455
+
2456
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
2457
+ if (recoverFromStaleChunkError(error)) {
2458
+ console.warn(
2459
+ "[agent-native] Recovering agent panel after stale chunk error",
2460
+ );
2461
+ return;
2462
+ }
2463
+ const recoverableKind = assistantUiRecoverableRenderErrorKind(error);
2464
+ if (recoverableKind) {
2465
+ console.warn(
2466
+ "[agent-native] Recovering agent panel after assistant UI render error",
2467
+ recoverableKind,
2468
+ );
2469
+ if (this.state.staleIndexRecoveryCount >= 2) {
2470
+ console.error(
2471
+ "[agent-native] Agent panel assistant UI recovery failed",
2472
+ error,
2473
+ errorInfo,
2474
+ );
2475
+ return;
2476
+ }
2477
+ if (!this.recoveryTimer) {
2478
+ this.recoveryTimer = setTimeout(() => {
2479
+ this.recoveryTimer = null;
2480
+ this.setState((state) => ({
2481
+ error: null,
2482
+ staleIndexRecoveryCount: state.staleIndexRecoveryCount + 1,
2483
+ }));
2484
+ this.props.onReset();
2485
+ }, 0);
2486
+ }
2487
+ return;
2488
+ }
2489
+ console.error("[agent-native] Agent panel crashed", error, errorInfo);
2490
+ }
2491
+
2492
+ componentDidUpdate(
2493
+ _prevProps: Readonly<{ children: React.ReactNode; onReset: () => void }>,
2494
+ prevState: Readonly<{
2495
+ error: Error | null;
2496
+ staleIndexRecoveryCount: number;
2497
+ }>,
2498
+ ) {
2499
+ if (
2500
+ prevState.error &&
2501
+ !this.state.error &&
2502
+ this.state.staleIndexRecoveryCount > 0
2503
+ ) {
2504
+ if (this.recoveryCooldownTimer) {
2505
+ clearTimeout(this.recoveryCooldownTimer);
2506
+ }
2507
+ this.recoveryCooldownTimer = setTimeout(() => {
2508
+ this.recoveryCooldownTimer = null;
2509
+ this.setState((state) =>
2510
+ state.error ? null : { staleIndexRecoveryCount: 0 },
2511
+ );
2512
+ }, 2_000);
2513
+ }
2514
+ }
2515
+
2516
+ componentWillUnmount() {
2517
+ if (this.recoveryTimer) {
2518
+ clearTimeout(this.recoveryTimer);
2519
+ }
2520
+ if (this.recoveryCooldownTimer) {
2521
+ clearTimeout(this.recoveryCooldownTimer);
2522
+ }
2523
+ }
2524
+
2525
+ render() {
2526
+ if (!this.state.error) return this.props.children;
2527
+
2528
+ if (
2529
+ assistantUiRecoverableRenderErrorKind(this.state.error) &&
2530
+ this.state.staleIndexRecoveryCount < 2
2531
+ ) {
2532
+ return (
2533
+ <div className="flex h-full items-center justify-center p-6 text-center text-xs text-muted-foreground">
2534
+ Reloading chat UI...
2535
+ </div>
2536
+ );
2537
+ }
2538
+
2539
+ return (
2540
+ <div className="flex h-full flex-col items-center justify-center gap-3 p-6 text-center">
2541
+ <div className="max-w-[260px] space-y-1">
2542
+ <p className="text-sm font-medium text-foreground">
2543
+ Agent panel hit an internal UI error.
2544
+ </p>
2545
+ <p className="text-xs leading-relaxed text-muted-foreground">
2546
+ The app is still usable. Reset the panel to reload the chat UI.
2547
+ </p>
2548
+ </div>
2549
+ <button
2550
+ type="button"
2551
+ className="rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground hover:bg-accent"
2552
+ onClick={() => {
2553
+ this.setState({ error: null, staleIndexRecoveryCount: 0 });
2554
+ this.props.onReset();
2555
+ }}
2556
+ >
2557
+ Reset agent panel
2558
+ </button>
2559
+ <ErrorReportActions
2560
+ appName="Agent panel"
2561
+ title="Agent panel UI error"
2562
+ details={this.state.error.message}
2563
+ issueTitle="Agent panel UI error"
2564
+ className="max-w-[260px]"
2565
+ feedbackClassName="h-7"
2566
+ githubClassName="h-7"
2567
+ />
2568
+ </div>
2569
+ );
2570
+ }
2571
+ }
2572
+
2573
+ export function AgentPanel(props: AgentPanelProps) {
2574
+ const [resetKey, setResetKey] = useState(0);
2575
+ const resetPanel = useCallback(() => {
2576
+ try {
2577
+ const keyPrefix = props.storageKey ? `:${props.storageKey}` : "";
2578
+ localStorage.setItem(`agent-native-panel-mode${keyPrefix}`, "chat");
2579
+ } catch {}
2580
+ setResetKey((key) => key + 1);
2581
+ }, [props.storageKey]);
2582
+ return (
2583
+ <TooltipProvider delayDuration={200}>
2584
+ <AgentPanelErrorBoundary onReset={resetPanel}>
2585
+ <AgentPanelInner key={resetKey} {...props} />
2586
+ </AgentPanelErrorBoundary>
2587
+ </TooltipProvider>
2588
+ );
2589
+ }
2590
+
2591
+ export type AgentChatSurfaceMode = "panel" | "page";
2592
+
2593
+ export interface AgentChatSurfaceProps extends AgentPanelProps {
2594
+ /**
2595
+ * Layout treatment for the reusable chat surface. Use "page" when rendering
2596
+ * chat as the primary route content instead of inside the sidebar shell.
2597
+ * Default: "panel". Inline header and chat-tab chrome are hidden by default;
2598
+ * pass `showHeader` or `showTabBar` to opt into those controls.
2599
+ */
2600
+ mode?: AgentChatSurfaceMode;
2601
+ /**
2602
+ * Apply the shared chat view-transition marker/name to this surface. Pair
2603
+ * with `AgentSidebar chatViewTransition` and navigate via
2604
+ * `startAgentChatViewTransition` or `useAgentRouteState`.
2605
+ */
2606
+ chatViewTransition?: boolean;
2607
+ }
2608
+
2609
+ export function shouldDefaultAgentChatSurfacePageNewChatButton(
2610
+ mode: AgentChatSurfaceMode | undefined,
2611
+ _showTabBar: boolean | undefined,
2612
+ ): boolean {
2613
+ return mode === "page";
2614
+ }
2615
+
2616
+ export function shouldAllowAgentChatSurfaceSettingsMode(
2617
+ mode: AgentChatSurfaceMode | undefined,
2618
+ allowSettingsMode: boolean | undefined,
2619
+ ): boolean {
2620
+ return allowSettingsMode ?? mode !== "page";
2621
+ }
2622
+
2623
+ /**
2624
+ * Reusable chat surface backed by AgentPanel internals.
2625
+ *
2626
+ * This gives page-level routes the same tabbed conversations, composer,
2627
+ * model controls, context chips, and recovery boundary used by the
2628
+ * sidebar without introducing a second chat implementation.
2629
+ */
2630
+ export function AgentChatSurface({
2631
+ mode = "panel",
2632
+ className,
2633
+ defaultMode = "chat",
2634
+ showHeader = false,
2635
+ showTabBar = false,
2636
+ isFullscreen,
2637
+ style,
2638
+ chatViewTransition = false,
2639
+ showPageNewChatButton,
2640
+ ...props
2641
+ }: AgentChatSurfaceProps) {
2642
+ const pageMode = mode === "page";
2643
+ const defaultShowPageNewChatButton =
2644
+ shouldDefaultAgentChatSurfacePageNewChatButton(mode, showTabBar);
2645
+
2646
+ return (
2647
+ <AgentPanel
2648
+ {...props}
2649
+ defaultMode={defaultMode}
2650
+ showHeader={showHeader}
2651
+ showTabBar={showTabBar}
2652
+ isFullscreen={isFullscreen ?? pageMode}
2653
+ allowSettingsMode={shouldAllowAgentChatSurfaceSettingsMode(
2654
+ mode,
2655
+ props.allowSettingsMode,
2656
+ )}
2657
+ showPageNewChatButton={
2658
+ showPageNewChatButton ?? defaultShowPageNewChatButton
2659
+ }
2660
+ className={cn(
2661
+ pageMode && "h-full min-h-0 w-full overflow-hidden bg-background",
2662
+ chatViewTransition && AGENT_CHAT_VIEW_TRANSITION_CLASS,
2663
+ className,
2664
+ )}
2665
+ style={
2666
+ chatViewTransition ? getAgentChatViewTransitionStyle(style) : style
2667
+ }
2668
+ />
2669
+ );
2670
+ }
2671
+
2672
+ // ─── AgentSidebar — wraps content with a toggleable agent panel ─────────────
2673
+
2674
+ export interface AgentSidebarProps {
2675
+ children: React.ReactNode;
2676
+ /** Placeholder text for the empty chat state */
2677
+ emptyStateText?: string;
2678
+ /** Suggestion prompts shown when no messages */
2679
+ suggestions?: string[];
2680
+ /** Context-aware suggestions merged with `suggestions`. Enabled by default. */
2681
+ dynamicSuggestions?: AssistantChatProps["dynamicSuggestions"];
2682
+ /** Optional controls rendered in the chat composer toolbar. */
2683
+ composerToolbarSlot?: AssistantChatProps["composerToolbarSlot"];
2684
+ /** Optional contextual content rendered just above the chat composer. */
2685
+ composerSlot?: AssistantChatProps["composerSlot"];
2686
+ /** Observe the active chat composer's current plain text. */
2687
+ onComposerTextChange?: AssistantChatProps["onComposerTextChange"];
2688
+ /** Optional secondary model menu shown inside the chat composer model picker. */
2689
+ imageModelMenu?: AssistantChatProps["imageModelMenu"];
2690
+ /** Optional content rendered at the bottom of the chat thread. */
2691
+ threadFooterSlot?: AssistantChatProps["threadFooterSlot"];
2692
+ /** Initial sidebar width in pixels. Mount-only; user resize and a saved
2693
+ * localStorage value override this. Default: 380 */
2694
+ defaultSidebarWidth?: number;
2695
+ /** @deprecated Use `defaultSidebarWidth` — this prop is mount-only. */
2696
+ sidebarWidth?: number;
2697
+ /** Which side the sidebar appears on. Default: "right" */
2698
+ position?: "left" | "right";
2699
+ /** Whether the sidebar starts open. Default: false */
2700
+ defaultOpen?: boolean;
2701
+ /** Animate the mobile overlay in a sheet-style slide transition. Default: true */
2702
+ animateMobile?: boolean;
2703
+ /** Animate desktop open/close by resizing the sidebar. Default: true */
2704
+ animateDesktop?: boolean;
2705
+ /**
2706
+ * Apply the shared chat view-transition marker/name to the sidebar panel so a
2707
+ * page-level AgentChatSurface can morph into it on navigation.
2708
+ */
2709
+ chatViewTransition?: boolean;
2710
+ /** Namespace for persisted chat state. Use the same key as AgentChatHome. */
2711
+ storageKey?: string;
2712
+ /** Open the sidebar when a chat run is active or reconnects. */
2713
+ openOnChatRunning?: boolean;
2714
+ /** Override the fullscreen menu action, for templates with a chat-first page. */
2715
+ onFullscreenRequest?: () => void;
2716
+ /** Ambient resource context rendered as a composer chip. */
2717
+ scope?: import("./use-chat-threads.js").ChatThreadScope | null;
2718
+ /** @deprecated Scope context now appears inside the composer. */
2719
+ showScopeBadge?: MultiTabAssistantChatProps["showScopeBadge"];
2720
+ /** Stable browser tab id used for tab-scoped app-state context. */
2721
+ browserTabId?: string;
2722
+ /** Keep chat thread selection in URL state. */
2723
+ threadUrlSync?: MultiTabAssistantChatProps["threadUrlSync"];
2724
+ /** Optional link shown in Resources and Settings modes for the full Agent page. */
2725
+ agentPageHref?: string;
2726
+ }
2727
+
2728
+ /**
2729
+ * Wraps app content with a toggleable agent sidebar.
2730
+ * Use AgentToggleButton in your header to open/close it.
2731
+ */
2732
+ export function AgentSidebar({
2733
+ children,
2734
+ emptyStateText = "How can I help you?",
2735
+ suggestions,
2736
+ dynamicSuggestions,
2737
+ composerToolbarSlot,
2738
+ composerSlot,
2739
+ onComposerTextChange,
2740
+ imageModelMenu,
2741
+ threadFooterSlot,
2742
+ defaultSidebarWidth,
2743
+ sidebarWidth,
2744
+ position = "right",
2745
+ defaultOpen = false,
2746
+ animateMobile = true,
2747
+ animateDesktop = true,
2748
+ chatViewTransition = false,
2749
+ storageKey,
2750
+ openOnChatRunning = false,
2751
+ onFullscreenRequest,
2752
+ scope,
2753
+ showScopeBadge,
2754
+ browserTabId,
2755
+ threadUrlSync,
2756
+ agentPageHref,
2757
+ }: AgentSidebarProps) {
2758
+ const initialWidth = defaultSidebarWidth ?? sidebarWidth ?? 380;
2759
+ const [open, setOpen] = useState(
2760
+ () =>
2761
+ openOnChatRunning || getInitialAgentSidebarOpen(defaultOpen, storageKey),
2762
+ );
2763
+ const [presentationMode, setPresentationMode] = useState(false);
2764
+ const [width, setWidth] = useState(initialWidth);
2765
+ const [isResizing, setIsResizing] = useState(false);
2766
+ const [fullscreen, setFullscreen] = useState(() => {
2767
+ // Force-disable on mobile: a Claude-style centered column makes no sense
2768
+ // when the sidebar already covers most of the viewport.
2769
+ if (
2770
+ typeof window !== "undefined" &&
2771
+ window.matchMedia("(max-width: 767px)").matches
2772
+ ) {
2773
+ return false;
2774
+ }
2775
+ try {
2776
+ return localStorage.getItem(SIDEBAR_FULLSCREEN_KEY) === "true";
2777
+ } catch {
2778
+ return false;
2779
+ }
2780
+ });
2781
+
2782
+ // Track mobile viewport so we can switch to overlay mode.
2783
+ const [isMobile, setIsMobile] = useState(
2784
+ () =>
2785
+ typeof window !== "undefined" &&
2786
+ window.matchMedia("(max-width: 767px)").matches,
2787
+ );
2788
+ useEffect(() => {
2789
+ const mq = window.matchMedia("(max-width: 767px)");
2790
+ const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);
2791
+ mq.addEventListener("change", handler);
2792
+ return () => mq.removeEventListener("change", handler);
2793
+ }, []);
2794
+ useEffect(() => {
2795
+ try {
2796
+ const saved = localStorage.getItem(SIDEBAR_STORAGE_KEY);
2797
+ if (saved) {
2798
+ const n = parseInt(saved, 10);
2799
+ if (n >= SIDEBAR_MIN && n <= SIDEBAR_MAX) setWidth(n);
2800
+ }
2801
+ } catch {}
2802
+ }, []);
2803
+
2804
+ const setOpenPersisted = useCallback(
2805
+ (next: boolean | ((prev: boolean) => boolean)) => {
2806
+ setOpen((prev) => {
2807
+ const value = typeof next === "function" ? next(prev) : next;
2808
+ setAgentSidebarOpenPreference(value, storageKey);
2809
+ return value;
2810
+ });
2811
+ },
2812
+ [storageKey],
2813
+ );
2814
+
2815
+ const applyUrlOpenOverride = useCallback(() => {
2816
+ const override = consumeAgentSidebarUrlOpenOverride(storageKey);
2817
+ if (override !== null) setOpenPersisted(override);
2818
+ }, [setOpenPersisted, storageKey]);
2819
+
2820
+ useEffect(() => {
2821
+ applyUrlOpenOverride();
2822
+ return subscribeAgentSidebarUrlChanges(applyUrlOpenOverride);
2823
+ }, [applyUrlOpenOverride]);
2824
+
2825
+ useEffect(() => {
2826
+ if (openOnChatRunning) setOpen(true);
2827
+ }, [openOnChatRunning]);
2828
+
2829
+ const toggleFullscreen = useCallback(() => {
2830
+ setFullscreen((prev) => {
2831
+ const next = !prev;
2832
+ try {
2833
+ localStorage.setItem(SIDEBAR_FULLSCREEN_KEY, String(next));
2834
+ } catch {}
2835
+ return next;
2836
+ });
2837
+ }, []);
2838
+
2839
+ // Track whether the frame is controlling the sidebar (code mode = frame active).
2840
+ // Default to true when inside an iframe — assume the frame sidebar is active
2841
+ // until told otherwise. This prevents both sidebars flashing after hot reloads.
2842
+ const [frameCodeMode, setFrameCodeMode] = useState(() =>
2843
+ shouldParentFrameOwnAgentPanel(),
2844
+ );
2845
+ // Frame sidebar visibility: we don't know the frame's open/closed state at
2846
+ // mount, so start at false and wait for the frame to dispatch its real
2847
+ // state via the message handler below. Initializing to
2848
+ // `shouldParentFrameOwnAgentPanel()` here was a category error — that
2849
+ // helper reports ownership (which side renders the sidebar), not whether
2850
+ // the sidebar is currently open. Mixing them up dispatched a stale
2851
+ // "open: true" before the first frame message arrived.
2852
+ const [frameSidebarOpen, setFrameSidebarOpen] = useState(false);
2853
+ // Has the frame told us its sidebar state yet? In frame-owned mode we
2854
+ // don't know whether the sidebar is open or closed until the parent frame
2855
+ // dispatches `agentNative.sidebarMode`. Emitting a synthetic
2856
+ // `{ open: false }` before that message arrives makes downstream listeners
2857
+ // flip a moment later when the real state lands, which is the same
2858
+ // ownership-vs-open-state confusion the previous fix addressed.
2859
+ const [hasFrameSidebarState, setHasFrameSidebarState] = useState(false);
2860
+ const [backgroundPanelActive, setBackgroundPanelActive] = useState(false);
2861
+ const [runningTabIds, setRunningTabIds] = useState<Set<string>>(
2862
+ () => new Set(),
2863
+ );
2864
+ const shouldMountPanel =
2865
+ !presentationMode &&
2866
+ (!frameCodeMode || !shouldParentFrameOwnAgentPanel()) &&
2867
+ (open || backgroundPanelActive || runningTabIds.size > 0);
2868
+ const shouldMountPanelRef = useRef(shouldMountPanel);
2869
+
2870
+ useEffect(() => {
2871
+ shouldMountPanelRef.current = shouldMountPanel;
2872
+ }, [shouldMountPanel]);
2873
+
2874
+ useEffect(() => {
2875
+ const frameOwned = frameCodeMode && shouldParentFrameOwnAgentPanel();
2876
+ // Skip the initial emit in frame-owned mode — wait until the frame has
2877
+ // sent us its real sidebar state. Once we know, this effect re-runs and
2878
+ // dispatches the correct value.
2879
+ if (frameOwned && !hasFrameSidebarState) return;
2880
+ dispatchAgentSidebarStateChange({
2881
+ open: !presentationMode && (frameOwned ? frameSidebarOpen : open),
2882
+ source: frameOwned ? "frame" : "app",
2883
+ mode: frameOwned ? "code" : "app",
2884
+ });
2885
+ }, [
2886
+ frameCodeMode,
2887
+ frameSidebarOpen,
2888
+ open,
2889
+ presentationMode,
2890
+ hasFrameSidebarState,
2891
+ ]);
2892
+
2893
+ useEffect(() => {
2894
+ const preparePanel = () => setBackgroundPanelActive(true);
2895
+ const handleChatRunning = (event: Event) => {
2896
+ const detail = (event as CustomEvent).detail;
2897
+ const tabId =
2898
+ typeof detail?.tabId === "string" && detail.tabId
2899
+ ? detail.tabId
2900
+ : "__default__";
2901
+
2902
+ if (detail?.isRunning === true) {
2903
+ if (openOnChatRunning) setOpen(true);
2904
+ setRunningTabIds((prev) => {
2905
+ const next = new Set(prev);
2906
+ next.add(tabId);
2907
+ return next;
2908
+ });
2909
+ return;
2910
+ }
2911
+
2912
+ if (detail?.isRunning === false) {
2913
+ setRunningTabIds((prev) => {
2914
+ if (!prev.has(tabId)) return prev;
2915
+ const next = new Set(prev);
2916
+ next.delete(tabId);
2917
+ return next;
2918
+ });
2919
+ setBackgroundPanelActive(false);
2920
+ }
2921
+ };
2922
+
2923
+ window.addEventListener(AGENT_PANEL_PREPARE_EVENT, preparePanel);
2924
+ window.addEventListener(AGENT_CHAT_RUNNING_EVENT, handleChatRunning);
2925
+ return () => {
2926
+ window.removeEventListener(AGENT_PANEL_PREPARE_EVENT, preparePanel);
2927
+ window.removeEventListener(AGENT_CHAT_RUNNING_EVENT, handleChatRunning);
2928
+ };
2929
+ }, [openOnChatRunning, setOpenPersisted]);
2930
+
2931
+ useEffect(() => {
2932
+ const replayAfterMount = (type: string, event: Event) => {
2933
+ if (shouldMountPanelRef.current) return;
2934
+
2935
+ const detail = (event as CustomEvent).detail;
2936
+ shouldMountPanelRef.current = true;
2937
+ setBackgroundPanelActive(true);
2938
+ if (type === AGENT_PANEL_OPEN_SETTINGS_EVENT) {
2939
+ setOpenPersisted(true);
2940
+ }
2941
+
2942
+ window.setTimeout(() => {
2943
+ window.dispatchEvent(new CustomEvent(type, { detail }));
2944
+ }, 0);
2945
+ };
2946
+
2947
+ const handleSetMode = (event: Event) => {
2948
+ replayAfterMount(AGENT_PANEL_SET_MODE_EVENT, event);
2949
+ };
2950
+ const handleOpenSettings = (event: Event) => {
2951
+ replayAfterMount(AGENT_PANEL_OPEN_SETTINGS_EVENT, event);
2952
+ };
2953
+
2954
+ window.addEventListener(AGENT_PANEL_SET_MODE_EVENT, handleSetMode);
2955
+ window.addEventListener(
2956
+ AGENT_PANEL_OPEN_SETTINGS_EVENT,
2957
+ handleOpenSettings,
2958
+ );
2959
+ return () => {
2960
+ window.removeEventListener(AGENT_PANEL_SET_MODE_EVENT, handleSetMode);
2961
+ window.removeEventListener(
2962
+ AGENT_PANEL_OPEN_SETTINGS_EVENT,
2963
+ handleOpenSettings,
2964
+ );
2965
+ };
2966
+ }, [setOpenPersisted]);
2967
+
2968
+ useEffect(() => {
2969
+ const toggleHandler = () => {
2970
+ if (frameCodeMode && shouldParentFrameOwnAgentPanel()) {
2971
+ // Forward toggle to frame parent — the frame sidebar handles it
2972
+ window.parent.postMessage(
2973
+ { type: "agentNative.toggleSidebar" },
2974
+ parentFrameTargetOrigin(),
2975
+ );
2976
+ } else {
2977
+ setOpenPersisted((prev) => !prev);
2978
+ }
2979
+ };
2980
+ const openHandler = () => {
2981
+ if (frameCodeMode && shouldParentFrameOwnAgentPanel()) {
2982
+ window.parent.postMessage(
2983
+ { type: "agentNative.toggleSidebar", data: { open: true } },
2984
+ parentFrameTargetOrigin(),
2985
+ );
2986
+ } else {
2987
+ setOpenPersisted(true);
2988
+ }
2989
+ };
2990
+ const closeHandler = () => {
2991
+ if (frameCodeMode && shouldParentFrameOwnAgentPanel()) {
2992
+ window.parent.postMessage(
2993
+ { type: "agentNative.toggleSidebar", data: { open: false } },
2994
+ parentFrameTargetOrigin(),
2995
+ );
2996
+ } else {
2997
+ setOpenPersisted(false);
2998
+ }
2999
+ };
3000
+ window.addEventListener("agent-panel:toggle", toggleHandler);
3001
+ window.addEventListener("agent-panel:open", openHandler);
3002
+ window.addEventListener("agent-panel:close", closeHandler);
3003
+ return () => {
3004
+ window.removeEventListener("agent-panel:toggle", toggleHandler);
3005
+ window.removeEventListener("agent-panel:open", openHandler);
3006
+ window.removeEventListener("agent-panel:close", closeHandler);
3007
+ };
3008
+ }, [setOpenPersisted, frameCodeMode]);
3009
+
3010
+ // Listen for sidebar mode commands from the frame parent.
3011
+ // When frame is in "code" mode, hide the app sidebar.
3012
+ // When frame is in "app" mode, show the app sidebar, sync width and panel mode.
3013
+ useEffect(() => {
3014
+ if (window.parent === window) return; // Not in an iframe
3015
+
3016
+ function handleMessage(event: MessageEvent) {
3017
+ if (event.data?.type !== "agentNative.sidebarMode") return;
3018
+ if (event.source !== window.parent || !isTrustedFrameMessage(event))
3019
+ return;
3020
+ const {
3021
+ mode,
3022
+ appMode,
3023
+ width: frameWidth,
3024
+ open: frameOpen,
3025
+ } = event.data.data || {};
3026
+ if (mode === "code") {
3027
+ // Frame is showing its own sidebar — hide the app's
3028
+ setFrameCodeMode(true);
3029
+ setFrameSidebarOpen(frameOpen !== false);
3030
+ setHasFrameSidebarState(true);
3031
+ setOpenPersisted(false);
3032
+ } else if (mode === "app") {
3033
+ // Frame deferred to the app — show and sync width + mode
3034
+ setFrameCodeMode(false);
3035
+ setFrameSidebarOpen(false);
3036
+ setHasFrameSidebarState(true);
3037
+ setOpenPersisted(frameOpen !== false);
3038
+ if (
3039
+ frameWidth &&
3040
+ frameWidth >= SIDEBAR_MIN &&
3041
+ frameWidth <= SIDEBAR_MAX
3042
+ ) {
3043
+ setWidth(frameWidth);
3044
+ }
3045
+ // Sync the panel mode from frame tab selection
3046
+ if (
3047
+ appMode === "cli" ||
3048
+ appMode === "resources" ||
3049
+ appMode === "chat"
3050
+ ) {
3051
+ window.dispatchEvent(
3052
+ new CustomEvent("agent-panel:set-mode", {
3053
+ detail: { mode: appMode },
3054
+ }),
3055
+ );
3056
+ }
3057
+ }
3058
+ }
3059
+ window.addEventListener("message", handleMessage);
3060
+ return () => window.removeEventListener("message", handleMessage);
3061
+ }, [setOpenPersisted]);
3062
+
3063
+ // Cmd+\ / Ctrl+\ toggles the agent sidebar globally. Cmd+I / Ctrl+I focuses
3064
+ // chat and attaches selected page text as one-shot context for the next turn.
3065
+ useEffect(() => {
3066
+ const handleKeyDown = (e: KeyboardEvent) => {
3067
+ if (
3068
+ (e.metaKey || e.ctrlKey) &&
3069
+ !e.altKey &&
3070
+ !e.shiftKey &&
3071
+ (e.key === "\\" || e.code === "Backslash")
3072
+ ) {
3073
+ e.preventDefault();
3074
+ window.dispatchEvent(new Event("agent-panel:toggle"));
3075
+ return;
3076
+ }
3077
+ if ((e.metaKey || e.ctrlKey) && e.key === "i") {
3078
+ e.preventDefault();
3079
+ let selectionText = "";
3080
+ try {
3081
+ selectionText = window.getSelection()?.toString().trim() ?? "";
3082
+ } catch {}
3083
+ if (selectionText) {
3084
+ fetch(
3085
+ agentNativePath(
3086
+ "/_agent-native/application-state/pending-selection-context",
3087
+ ),
3088
+ {
3089
+ method: "PUT",
3090
+ keepalive: true,
3091
+ headers: { "Content-Type": "application/json" },
3092
+ body: JSON.stringify({
3093
+ text: selectionText,
3094
+ capturedAt: Date.now(),
3095
+ }),
3096
+ },
3097
+ ).catch(() => {});
3098
+ window.dispatchEvent(
3099
+ new CustomEvent("agent-panel:selection-attached", {
3100
+ detail: { text: selectionText, length: selectionText.length },
3101
+ }),
3102
+ );
3103
+ }
3104
+ focusAgentChat();
3105
+ }
3106
+ };
3107
+ document.addEventListener("keydown", handleKeyDown);
3108
+ return () => document.removeEventListener("keydown", handleKeyDown);
3109
+ }, []);
3110
+
3111
+ // Hide sidebar during presentation mode
3112
+ useEffect(() => {
3113
+ const handler = (event: MessageEvent) => {
3114
+ if (event.data?.type !== "agentNative.presentationMode") return;
3115
+ if (event.source !== window.parent || !isTrustedFrameMessage(event))
3116
+ return;
3117
+ setPresentationMode(event.data.data?.active === true);
3118
+ };
3119
+ window.addEventListener("message", handler);
3120
+ return () => window.removeEventListener("message", handler);
3121
+ }, []);
3122
+
3123
+ const handleDrag = useCallback((delta: number) => {
3124
+ setWidth((prev) => {
3125
+ const next = Math.min(SIDEBAR_MAX, Math.max(SIDEBAR_MIN, prev + delta));
3126
+ try {
3127
+ localStorage.setItem(SIDEBAR_STORAGE_KEY, String(next));
3128
+ } catch {}
3129
+ return next;
3130
+ });
3131
+ }, []);
3132
+ const handleResizeStart = useCallback(() => setIsResizing(true), []);
3133
+ const handleResizeEnd = useCallback(() => setIsResizing(false), []);
3134
+
3135
+ const isLeft = position === "left";
3136
+ // Fullscreen only applies on desktop — on mobile the existing overlay is
3137
+ // already viewport-covering, so the maximize button is hidden and the
3138
+ // mounted state ignores any persisted value.
3139
+ const effectiveFullscreen = !onFullscreenRequest && fullscreen && !isMobile;
3140
+ const mobileAnimationEnabled =
3141
+ !presentationMode && !effectiveFullscreen && isMobile && animateMobile;
3142
+ const desktopAnimationEnabled =
3143
+ !presentationMode && !effectiveFullscreen && !isMobile && animateDesktop;
3144
+ const sidebarAnimationEnabled =
3145
+ mobileAnimationEnabled || desktopAnimationEnabled;
3146
+ const [renderAnimatedPanel, setRenderAnimatedPanel] =
3147
+ useState(shouldMountPanel);
3148
+
3149
+ useEffect(() => {
3150
+ if (!sidebarAnimationEnabled) {
3151
+ setRenderAnimatedPanel(shouldMountPanel);
3152
+ return;
3153
+ }
3154
+
3155
+ let unmountTimer: number | undefined;
3156
+
3157
+ if (shouldMountPanel) {
3158
+ setRenderAnimatedPanel(true);
3159
+ } else {
3160
+ unmountTimer = window.setTimeout(() => {
3161
+ setRenderAnimatedPanel(false);
3162
+ }, SIDEBAR_ANIMATION_MS);
3163
+ }
3164
+
3165
+ return () => {
3166
+ if (unmountTimer !== undefined) {
3167
+ window.clearTimeout(unmountTimer);
3168
+ }
3169
+ };
3170
+ }, [shouldMountPanel, sidebarAnimationEnabled]);
3171
+
3172
+ const shouldRenderPanel = sidebarAnimationEnabled
3173
+ ? renderAnimatedPanel
3174
+ : shouldMountPanel;
3175
+ const panelOpen = open && shouldMountPanel;
3176
+ const panelLayout = isMobile
3177
+ ? "mobile"
3178
+ : effectiveFullscreen
3179
+ ? "fullscreen"
3180
+ : "desktop";
3181
+ // On desktop the resize handle is also the visual divider. Avoid painting a
3182
+ // second panel border next to it.
3183
+ const showResizeHandle = !isMobile && !effectiveFullscreen && panelOpen;
3184
+
3185
+ // On mobile the sidebar floats as a fixed overlay so the content below isn't
3186
+ // squashed. On desktop it participates in the flex layout as before, except
3187
+ // in fullscreen mode where it overlays the entire viewport (Claude-style).
3188
+ let panelStyle: AgentPanelStyle;
3189
+ if (isMobile) {
3190
+ panelStyle = {
3191
+ ...AGENT_PANEL_ROOT_STYLE,
3192
+ position: "fixed",
3193
+ top: 0,
3194
+ [isLeft ? "left" : "right"]: 0,
3195
+ height: "100%",
3196
+ width,
3197
+ maxWidth: "85vw",
3198
+ maxHeight: "var(--agent-native-viewport-height, 100vh)",
3199
+ zIndex: SIDEBAR_OVERLAY_Z_INDEX,
3200
+ "--agent-sidebar-background":
3201
+ "var(--agent-native-lower-surface, hsl(var(--background)))",
3202
+ background: "var(--agent-sidebar-background)",
3203
+ borderLeft: isLeft ? "none" : "1px solid hsl(var(--border))",
3204
+ borderRight: isLeft ? "1px solid hsl(var(--border))" : "none",
3205
+ display: mobileAnimationEnabled || panelOpen ? "flex" : "none",
3206
+ "--agent-sidebar-closed-transform": `translateX(${isLeft ? "-" : ""}calc(100% + 1px))`,
3207
+ pointerEvents: mobileAnimationEnabled && !panelOpen ? "none" : undefined,
3208
+ };
3209
+ } else if (effectiveFullscreen) {
3210
+ panelStyle = {
3211
+ ...AGENT_PANEL_ROOT_STYLE,
3212
+ position: "fixed",
3213
+ inset: 0,
3214
+ width: "100%",
3215
+ maxHeight: "var(--agent-native-viewport-height, 100vh)",
3216
+ zIndex: SIDEBAR_FULLSCREEN_Z_INDEX,
3217
+ background: "hsl(var(--background))",
3218
+ display: open ? "flex" : "none",
3219
+ };
3220
+ } else {
3221
+ panelStyle = {
3222
+ ...AGENT_PANEL_ROOT_STYLE,
3223
+ "--agent-sidebar-width": `${width}px`,
3224
+ "--agent-sidebar-inner-closed-transform": `translateX(${isLeft ? "-" : ""}100%)`,
3225
+ "--agent-sidebar-background":
3226
+ "var(--agent-native-lower-surface, hsl(var(--background)))",
3227
+ background: "var(--agent-sidebar-background)",
3228
+ width: desktopAnimationEnabled ? undefined : width,
3229
+ maxHeight: "var(--agent-native-viewport-height, 100vh)",
3230
+ borderLeft:
3231
+ !panelOpen || isLeft || showResizeHandle
3232
+ ? "none"
3233
+ : "1px solid hsl(var(--border))",
3234
+ borderRight:
3235
+ !panelOpen || !isLeft || showResizeHandle
3236
+ ? "none"
3237
+ : "1px solid hsl(var(--border))",
3238
+ display: desktopAnimationEnabled || panelOpen ? "flex" : "none",
3239
+ minWidth: desktopAnimationEnabled ? 0 : undefined,
3240
+ pointerEvents: desktopAnimationEnabled && !panelOpen ? "none" : undefined,
3241
+ };
3242
+ }
3243
+
3244
+ // Mount the live chat surface only while visible or actively needed. Keeping
3245
+ // it mounted while closed starts app-state polling on every public page view.
3246
+ const sidebar = shouldRenderPanel ? (
3247
+ <>
3248
+ {showResizeHandle && !isLeft && (
3249
+ <ResizeHandle
3250
+ position={position}
3251
+ onDrag={handleDrag}
3252
+ onResizeStart={handleResizeStart}
3253
+ onResizeEnd={handleResizeEnd}
3254
+ />
3255
+ )}
3256
+ <div
3257
+ className={cn(
3258
+ "agent-sidebar-panel flex shrink-0 flex-col overflow-hidden text-[13px] leading-[1.2] antialiased",
3259
+ chatViewTransition && AGENT_CHAT_VIEW_TRANSITION_CLASS,
3260
+ mobileAnimationEnabled && "shadow-2xl",
3261
+ )}
3262
+ data-agent-sidebar-animation={
3263
+ mobileAnimationEnabled
3264
+ ? "mobile"
3265
+ : desktopAnimationEnabled
3266
+ ? "desktop"
3267
+ : undefined
3268
+ }
3269
+ data-agent-sidebar-layout={panelLayout}
3270
+ data-agent-sidebar-position={position}
3271
+ data-agent-sidebar-state={panelOpen ? "open" : "closed"}
3272
+ data-agent-sidebar-resizing={isResizing ? "true" : undefined}
3273
+ style={
3274
+ chatViewTransition
3275
+ ? getAgentChatViewTransitionStyle(panelStyle)
3276
+ : panelStyle
3277
+ }
3278
+ inert={sidebarAnimationEnabled && !panelOpen ? true : undefined}
3279
+ aria-hidden={sidebarAnimationEnabled && !panelOpen ? true : undefined}
3280
+ >
3281
+ <div className="agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col">
3282
+ <AgentPanel
3283
+ emptyStateText={emptyStateText}
3284
+ suggestions={suggestions}
3285
+ dynamicSuggestions={dynamicSuggestions}
3286
+ composerToolbarSlot={composerToolbarSlot}
3287
+ composerSlot={composerSlot}
3288
+ onComposerTextChange={onComposerTextChange}
3289
+ imageModelMenu={imageModelMenu}
3290
+ threadFooterSlot={threadFooterSlot}
3291
+ missingApiKeySetupLayout="sidebar"
3292
+ onCollapse={() => setOpenPersisted(false)}
3293
+ isFullscreen={effectiveFullscreen}
3294
+ onToggleFullscreen={
3295
+ isMobile ? undefined : (onFullscreenRequest ?? toggleFullscreen)
3296
+ }
3297
+ storageKey={storageKey}
3298
+ scope={scope}
3299
+ showScopeBadge={showScopeBadge}
3300
+ browserTabId={browserTabId}
3301
+ threadUrlSync={threadUrlSync}
3302
+ agentPageHref={agentPageHref}
3303
+ allowSettingsMode={false}
3304
+ />
3305
+ </div>
3306
+ </div>
3307
+ {showResizeHandle && isLeft && (
3308
+ <ResizeHandle
3309
+ position={position}
3310
+ onDrag={handleDrag}
3311
+ onResizeStart={handleResizeStart}
3312
+ onResizeEnd={handleResizeEnd}
3313
+ />
3314
+ )}
3315
+ </>
3316
+ ) : null;
3317
+
3318
+ return (
3319
+ <RealtimeVoiceModeProvider browserTabId={browserTabId}>
3320
+ <div
3321
+ className="agent-sidebar-shell flex min-w-0 flex-1 h-screen overflow-hidden"
3322
+ data-agent-sidebar-position={position}
3323
+ data-agent-sidebar-resizing={isResizing ? "true" : undefined}
3324
+ >
3325
+ <AgentNativeRouteWarmup />
3326
+ {/* Mobile backdrop — tapping it closes the sidebar */}
3327
+ {isMobile &&
3328
+ !presentationMode &&
3329
+ (mobileAnimationEnabled ? shouldRenderPanel : open) && (
3330
+ <div
3331
+ className={cn(
3332
+ "agent-sidebar-backdrop fixed inset-0 bg-black/40",
3333
+ mobileAnimationEnabled && !panelOpen && "pointer-events-none",
3334
+ )}
3335
+ data-agent-sidebar-animation={
3336
+ mobileAnimationEnabled ? "mobile" : undefined
3337
+ }
3338
+ data-agent-sidebar-state={panelOpen ? "open" : "closed"}
3339
+ style={{ zIndex: SIDEBAR_OVERLAY_Z_INDEX - 1 }}
3340
+ onClick={() => setOpenPersisted(false)}
3341
+ />
3342
+ )}
3343
+ {/* URLSync writes the current URL to application-state so the agent
3344
+ sees what page/filters the user is on, and applies URL-update
3345
+ commands the agent writes via `set-search-params` / `set-url`. */}
3346
+ {shouldMountPanel ? <URLSync browserTabId={browserTabId} /> : null}
3347
+ {isLeft && !presentationMode ? sidebar : null}
3348
+ <div
3349
+ className="agent-sidebar-main-surface flex flex-1 flex-col overflow-auto min-w-0"
3350
+ data-agent-sidebar-main-position={position}
3351
+ data-agent-sidebar-main-state={
3352
+ !isMobile && !effectiveFullscreen && !presentationMode && panelOpen
3353
+ ? "open"
3354
+ : "closed"
3355
+ }
3356
+ data-agent-sidebar-resizing={isResizing ? "true" : undefined}
3357
+ >
3358
+ {/* Screen-refresh key: the agent's `refresh-screen` tool bumps this
3359
+ counter, remounting only the main content subtree so it re-fetches
3360
+ its data. The sidebar above stays mounted, preserving chat state. */}
3361
+ <ScreenRefreshBoundary>{children}</ScreenRefreshBoundary>
3362
+ </div>
3363
+ {!isLeft && !presentationMode ? sidebar : null}
3364
+ </div>
3365
+ </RealtimeVoiceModeProvider>
3366
+ );
3367
+ }
3368
+
3369
+ /**
3370
+ * Focus the agent chat composer input.
3371
+ * Opens the sidebar if closed, then focuses the text input.
3372
+ */
3373
+ export function focusAgentChat() {
3374
+ window.dispatchEvent(
3375
+ new CustomEvent("agent-panel:set-mode", {
3376
+ detail: { mode: "chat" },
3377
+ }),
3378
+ );
3379
+ window.dispatchEvent(new Event("agent-panel:open"));
3380
+ // Wait for sidebar to render, then focus the composer
3381
+ requestAnimationFrame(() => {
3382
+ const panel = document.querySelector(".agent-sidebar-panel");
3383
+ if (!panel) return;
3384
+ const prosemirror = panel.querySelector(
3385
+ ".ProseMirror",
3386
+ ) as HTMLElement | null;
3387
+ if (prosemirror) {
3388
+ prosemirror.focus();
3389
+ return;
3390
+ }
3391
+ const textarea = panel.querySelector("textarea") as HTMLElement | null;
3392
+ if (textarea) textarea.focus();
3393
+ });
3394
+ }
3395
+
3396
+ /**
3397
+ * Button to toggle the agent sidebar. Place this in your app's header/toolbar.
3398
+ * Dispatches a custom event that AgentSidebar listens for.
3399
+ */
3400
+ export function AgentToggleButton({ className }: { className?: string }) {
3401
+ const [open, setOpen] = useState(false);
3402
+ useEffect(() => {
3403
+ const handler = (event: Event) => {
3404
+ const detail = (event as CustomEvent<AgentSidebarStateChangeDetail>)
3405
+ .detail;
3406
+ if (detail && typeof detail.open === "boolean") setOpen(detail.open);
3407
+ };
3408
+ window.addEventListener(SIDEBAR_STATE_CHANGE_EVENT, handler);
3409
+ return () =>
3410
+ window.removeEventListener(SIDEBAR_STATE_CHANGE_EVENT, handler);
3411
+ }, []);
3412
+ // Hide the open-agent button while the agent pane is open; the pane has its
3413
+ // own close button.
3414
+ if (open) return null;
3415
+ return (
3416
+ <TooltipProvider delayDuration={200}>
3417
+ <Tooltip>
3418
+ <TooltipTrigger asChild>
3419
+ <button
3420
+ type="button"
3421
+ aria-label="Toggle agent"
3422
+ onClick={() =>
3423
+ window.dispatchEvent(new Event("agent-panel:toggle"))
3424
+ }
3425
+ className={cn(
3426
+ "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent/40 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3427
+ className,
3428
+ )}
3429
+ >
3430
+ <IconMessageDots size={20} aria-hidden />
3431
+ </button>
3432
+ </TooltipTrigger>
3433
+ <TooltipContent>Toggle agent</TooltipContent>
3434
+ </Tooltip>
3435
+ </TooltipProvider>
3436
+ );
3437
+ }