@agent-native/core 0.72.2 → 0.72.4

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 (169) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +16 -0
  3. package/corpus/core/package.json +6 -6
  4. package/corpus/core/src/a2a/client.ts +7 -3
  5. package/corpus/core/src/client/AgentPanel.tsx +12 -1
  6. package/corpus/core/src/client/MultiTabAssistantChat.tsx +263 -13
  7. package/corpus/core/src/client/api-path.ts +28 -0
  8. package/corpus/core/src/client/embed-auth.ts +26 -0
  9. package/corpus/core/src/client/mcp-app-host.ts +6 -0
  10. package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +227 -33
  11. package/corpus/core/src/client/resources/use-mcp-servers.ts +1 -0
  12. package/corpus/core/src/client/settings/DemoModeSection.tsx +3 -1
  13. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +16 -4
  14. package/corpus/core/src/client/use-chat-threads.ts +86 -5
  15. package/corpus/core/src/demo/fetch-interceptor.ts +1 -1
  16. package/corpus/core/src/embedding/agent.ts +6 -1
  17. package/corpus/core/src/embedding/bridge.ts +34 -1
  18. package/corpus/core/src/mcp/build-server.ts +38 -10
  19. package/corpus/core/src/mcp/embed-app.ts +120 -45
  20. package/corpus/core/src/mcp/index.ts +5 -0
  21. package/corpus/core/src/mcp/org-directory.ts +70 -11
  22. package/corpus/core/src/mcp/server.ts +14 -0
  23. package/corpus/core/src/mcp-client/config.ts +12 -0
  24. package/corpus/core/src/mcp-client/index.ts +2 -0
  25. package/corpus/core/src/mcp-client/manager.ts +268 -9
  26. package/corpus/core/src/mcp-client/remote-store.ts +154 -0
  27. package/corpus/core/src/mcp-client/routes.ts +2 -0
  28. package/corpus/core/src/server/agent-chat-plugin.ts +8 -0
  29. package/corpus/core/src/server/index.ts +1 -0
  30. package/corpus/core/src/shared/mcp-embed-headers.ts +44 -3
  31. package/corpus/core/src/vite/client.ts +53 -0
  32. package/corpus/templates/assets/.agents/skills/a2a-assets/SKILL.md +17 -10
  33. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +15 -6
  34. package/corpus/templates/assets/AGENTS.md +7 -5
  35. package/corpus/templates/assets/actions/generate-asset.ts +161 -0
  36. package/corpus/templates/assets/actions/list-assets.ts +4 -2
  37. package/corpus/templates/assets/actions/navigate.ts +2 -1
  38. package/corpus/templates/assets/actions/open-asset-picker.ts +84 -3
  39. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -2
  40. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +27 -4
  41. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +35 -5
  42. package/corpus/templates/assets/app/routes/_index.tsx +12 -0
  43. package/corpus/templates/assets/app/routes/chat.$threadId.tsx +1 -0
  44. package/corpus/templates/assets/app/routes/library.tsx +243 -40
  45. package/corpus/templates/chat/actions/navigate.ts +3 -0
  46. package/corpus/templates/chat/app/components/layout/Layout.tsx +7 -2
  47. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +27 -4
  48. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +38 -6
  49. package/corpus/templates/chat/app/routes/_index.tsx +13 -0
  50. package/corpus/templates/chat/app/routes/chat.$threadId.tsx +1 -0
  51. package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +9 -0
  52. package/corpus/templates/design/AGENTS.md +10 -5
  53. package/corpus/templates/design/actions/connect-assets-mcp.ts +131 -0
  54. package/corpus/templates/design/actions/insert-asset.ts +219 -0
  55. package/corpus/templates/design/actions/view-screen.ts +3 -1
  56. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +1 -1
  57. package/corpus/templates/design/app/pages/DesignEditor.tsx +53 -0
  58. package/corpus/templates/design/server/plugins/agent-chat.ts +5 -1
  59. package/corpus/templates/design/server/plugins/assets-mcp.ts +60 -0
  60. package/corpus/templates/dispatch/app/routes/chat.$threadId.tsx +26 -0
  61. package/dist/a2a/client.d.ts +1 -0
  62. package/dist/a2a/client.d.ts.map +1 -1
  63. package/dist/a2a/client.js +5 -3
  64. package/dist/a2a/client.js.map +1 -1
  65. package/dist/client/AgentPanel.d.ts +6 -2
  66. package/dist/client/AgentPanel.d.ts.map +1 -1
  67. package/dist/client/AgentPanel.js +4 -4
  68. package/dist/client/AgentPanel.js.map +1 -1
  69. package/dist/client/MultiTabAssistantChat.d.ts +22 -1
  70. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  71. package/dist/client/MultiTabAssistantChat.js +215 -13
  72. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  73. package/dist/client/api-path.d.ts.map +1 -1
  74. package/dist/client/api-path.js +27 -0
  75. package/dist/client/api-path.js.map +1 -1
  76. package/dist/client/embed-auth.d.ts.map +1 -1
  77. package/dist/client/embed-auth.js +27 -0
  78. package/dist/client/embed-auth.js.map +1 -1
  79. package/dist/client/mcp-app-host.d.ts.map +1 -1
  80. package/dist/client/mcp-app-host.js +5 -0
  81. package/dist/client/mcp-app-host.js.map +1 -1
  82. package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
  83. package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  84. package/dist/client/mcp-apps/McpAppRenderer.js +179 -33
  85. package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
  86. package/dist/client/resources/use-mcp-servers.d.ts +1 -0
  87. package/dist/client/resources/use-mcp-servers.d.ts.map +1 -1
  88. package/dist/client/resources/use-mcp-servers.js.map +1 -1
  89. package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
  90. package/dist/client/settings/DemoModeSection.js +3 -1
  91. package/dist/client/settings/DemoModeSection.js.map +1 -1
  92. package/dist/client/use-agent-chat-home-handoff.d.ts +7 -1
  93. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  94. package/dist/client/use-agent-chat-home-handoff.js +9 -5
  95. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  96. package/dist/client/use-chat-threads.d.ts +6 -0
  97. package/dist/client/use-chat-threads.d.ts.map +1 -1
  98. package/dist/client/use-chat-threads.js +79 -5
  99. package/dist/client/use-chat-threads.js.map +1 -1
  100. package/dist/demo/fetch-interceptor.js +1 -1
  101. package/dist/demo/fetch-interceptor.js.map +1 -1
  102. package/dist/embedding/agent.d.ts +1 -1
  103. package/dist/embedding/agent.d.ts.map +1 -1
  104. package/dist/embedding/agent.js +1 -1
  105. package/dist/embedding/agent.js.map +1 -1
  106. package/dist/embedding/bridge.d.ts.map +1 -1
  107. package/dist/embedding/bridge.js +27 -1
  108. package/dist/embedding/bridge.js.map +1 -1
  109. package/dist/extensions/schema.d.ts +2 -2
  110. package/dist/mcp/build-server.d.ts +2 -0
  111. package/dist/mcp/build-server.d.ts.map +1 -1
  112. package/dist/mcp/build-server.js +32 -9
  113. package/dist/mcp/build-server.js.map +1 -1
  114. package/dist/mcp/embed-app.d.ts.map +1 -1
  115. package/dist/mcp/embed-app.js +120 -45
  116. package/dist/mcp/embed-app.js.map +1 -1
  117. package/dist/mcp/index.d.ts +1 -0
  118. package/dist/mcp/index.d.ts.map +1 -1
  119. package/dist/mcp/index.js +1 -0
  120. package/dist/mcp/index.js.map +1 -1
  121. package/dist/mcp/org-directory.d.ts +1 -0
  122. package/dist/mcp/org-directory.d.ts.map +1 -1
  123. package/dist/mcp/org-directory.js +59 -11
  124. package/dist/mcp/org-directory.js.map +1 -1
  125. package/dist/mcp/server.d.ts.map +1 -1
  126. package/dist/mcp/server.js +9 -0
  127. package/dist/mcp/server.js.map +1 -1
  128. package/dist/mcp-client/config.d.ts +12 -0
  129. package/dist/mcp-client/config.d.ts.map +1 -1
  130. package/dist/mcp-client/config.js.map +1 -1
  131. package/dist/mcp-client/index.d.ts +1 -1
  132. package/dist/mcp-client/index.d.ts.map +1 -1
  133. package/dist/mcp-client/index.js +1 -1
  134. package/dist/mcp-client/index.js.map +1 -1
  135. package/dist/mcp-client/manager.d.ts +2 -0
  136. package/dist/mcp-client/manager.d.ts.map +1 -1
  137. package/dist/mcp-client/manager.js +201 -9
  138. package/dist/mcp-client/manager.js.map +1 -1
  139. package/dist/mcp-client/remote-store.d.ts +27 -0
  140. package/dist/mcp-client/remote-store.d.ts.map +1 -1
  141. package/dist/mcp-client/remote-store.js +105 -0
  142. package/dist/mcp-client/remote-store.js.map +1 -1
  143. package/dist/mcp-client/routes.d.ts +1 -0
  144. package/dist/mcp-client/routes.d.ts.map +1 -1
  145. package/dist/mcp-client/routes.js +1 -0
  146. package/dist/mcp-client/routes.js.map +1 -1
  147. package/dist/provider-api/actions/github-repo-files.d.ts +2 -2
  148. package/dist/provider-api/actions/query-staged-dataset.d.ts +3 -3
  149. package/dist/provider-api/corpus-jobs.d.ts +8 -8
  150. package/dist/server/agent-chat-plugin.d.ts +2 -0
  151. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  152. package/dist/server/agent-chat-plugin.js +8 -0
  153. package/dist/server/agent-chat-plugin.js.map +1 -1
  154. package/dist/server/index.d.ts +1 -1
  155. package/dist/server/index.d.ts.map +1 -1
  156. package/dist/server/index.js +1 -1
  157. package/dist/server/index.js.map +1 -1
  158. package/dist/shared/mcp-embed-headers.d.ts +3 -1
  159. package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
  160. package/dist/shared/mcp-embed-headers.js +40 -3
  161. package/dist/shared/mcp-embed-headers.js.map +1 -1
  162. package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
  163. package/dist/sharing/actions/set-resource-visibility.d.ts +2 -2
  164. package/dist/sharing/actions/share-resource.d.ts +1 -1
  165. package/dist/sharing/schema.d.ts +1 -1
  166. package/dist/vite/client.d.ts.map +1 -1
  167. package/dist/vite/client.js +44 -0
  168. package/dist/vite/client.js.map +1 -1
  169. package/package.json +6 -6
@@ -10,6 +10,21 @@ export interface ChatTab {
10
10
  /** Short name for sub-agent tabs (e.g. "Research", "Draft email") */
11
11
  subAgentName?: string;
12
12
  }
13
+ export interface ChatThreadUrlSyncOptions {
14
+ /** Query-string parameter used by the generic URL adapter. Default: `thread`. */
15
+ paramName?: string;
16
+ /**
17
+ * Route-owned thread id. Pass `null` for the create route and a string for
18
+ * thread routes like `/chat/:threadId`.
19
+ */
20
+ routeThreadId?: string | null;
21
+ /** Build the URL path for a thread id, or for create mode when id is null. */
22
+ getPath?: (threadId: string | null) => string;
23
+ /** Optional router navigation callback used with `getPath`. */
24
+ navigate?: (path: string, options?: {
25
+ replace?: boolean;
26
+ }) => void;
27
+ }
13
28
  export interface MultiTabAssistantChatHeaderProps {
14
29
  tabs: ChatTab[];
15
30
  activeTabId: string;
@@ -41,6 +56,12 @@ export type MultiTabAssistantChatProps = Omit<AssistantChatProps, "tabId" | "thr
41
56
  restoreActiveThread?: boolean;
42
57
  /** Stable browser tab id used for tab-scoped app-state context. */
43
58
  browserTabId?: string;
59
+ /**
60
+ * Keep the active thread in URL state. `true` uses the generic `?thread=id`
61
+ * adapter; passing `routeThreadId` + `getPath` lets an app bind chats to
62
+ * route params such as `/chat/:threadId`.
63
+ */
64
+ threadUrlSync?: boolean | ChatThreadUrlSyncOptions;
44
65
  /**
45
66
  * Bind new chats to a resource (deck, design, dashboard, etc.). When set,
46
67
  * new chats automatically inherit this scope and scoped chats tuck away when
@@ -50,5 +71,5 @@ export type MultiTabAssistantChatProps = Omit<AssistantChatProps, "tabId" | "thr
50
71
  */
51
72
  scope?: ChatThreadScope | null;
52
73
  };
53
- export declare function MultiTabAssistantChat({ showTabBar, renderHeader, renderOverlay, contentHidden, apiUrl, storageKey, restoreActiveThread, browserTabId, scope, ...props }: MultiTabAssistantChatProps): import("react/jsx-runtime").JSX.Element;
74
+ export declare function MultiTabAssistantChat({ showTabBar, renderHeader, renderOverlay, contentHidden, apiUrl, storageKey, restoreActiveThread, browserTabId, threadUrlSync, scope, ...props }: MultiTabAssistantChatProps): import("react/jsx-runtime").JSX.Element;
54
75
  //# sourceMappingURL=MultiTabAssistantChat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MultiTabAssistantChat.d.ts","sourceRoot":"","sources":["../../src/client/MultiTabAssistantChat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AAUf,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAe5B,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,uBAAuB,CAAC;AAqqB/B,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;IACzC,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAsFD,MAAM,WAAW,gCAAgC;IAC/C,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,+BAA+B;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC3C,kBAAkB,EAClB,OAAO,GAAG,UAAU,CACrB,GAAG;IACF,sCAAsC;IACtC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,gCAAgC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC5E,2DAA2D;IAC3D,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gCAAgC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC7E,sGAAsG;IACtG,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EACpC,UAAiB,EACjB,YAAY,EACZ,aAAa,EACb,aAAqB,EACrB,MAAqD,EACrD,UAAU,EACV,mBAA0B,EAC1B,YAAY,EACZ,KAAY,EACZ,GAAG,KAAK,EACT,EAAE,0BAA0B,2CA4rD5B"}
1
+ {"version":3,"file":"MultiTabAssistantChat.d.ts","sourceRoot":"","sources":["../../src/client/MultiTabAssistantChat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AAUf,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAe5B,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,uBAAuB,CAAC;AAqqB/B,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;IACzC,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAsGD,MAAM,WAAW,wBAAwB;IACvC,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,MAAM,CAAC;IAC9C,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;CACpE;AAyED,MAAM,WAAW,gCAAgC;IAC/C,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,+BAA+B;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAC3C,kBAAkB,EAClB,OAAO,GAAG,UAAU,CACrB,GAAG;IACF,sCAAsC;IACtC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,gCAAgC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC5E,2DAA2D;IAC3D,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gCAAgC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC7E,sGAAsG;IACtG,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,GAAG,wBAAwB,CAAC;IACnD;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EACpC,UAAiB,EACjB,YAAY,EACZ,aAAa,EACb,aAAqB,EACrB,MAAqD,EACrD,UAAU,EACV,mBAA0B,EAC1B,YAAY,EACZ,aAAqB,EACrB,KAAY,EACZ,GAAG,KAAK,EACT,EAAE,0BAA0B,2CA00D5B"}
@@ -7,7 +7,7 @@ import { cn } from "./utils.js";
7
7
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./components/ui/tooltip.js";
8
8
  import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger, } from "./components/ui/popover.js";
9
9
  import { useChatThreads, } from "./use-chat-threads.js";
10
- import { agentNativePath } from "./api-path.js";
10
+ import { agentNativePath, appPath } from "./api-path.js";
11
11
  import { callAction } from "./use-action.js";
12
12
  import { RunStuckBanner } from "./RunStuckBanner.js";
13
13
  import { DEFAULT_MODEL } from "../agent/default-model.js";
@@ -300,6 +300,83 @@ function chatTabStatusFromAgentTeamStatus(status) {
300
300
  return isActiveAgentTeamStatus(status) ? "running" : "completed";
301
301
  }
302
302
  const STALE_THREAD_THRESHOLD_MS = 12 * 60 * 60 * 1000;
303
+ const DEFAULT_THREAD_URL_PARAM = "thread";
304
+ const THREAD_URL_CHANGED_EVENT = "agent-chat:url-thread-changed";
305
+ const hasOwn = Object.prototype.hasOwnProperty;
306
+ // The history patch is installed once and shared via a ref count so that
307
+ // multiple synced chats (or a remount) don't restore a stale `pushState`
308
+ // reference and silently drop a wrapper installed by another instance.
309
+ let historyPatchRefCount = 0;
310
+ let restoreHistoryPatch = null;
311
+ function installHistoryThreadUrlPatch() {
312
+ if (typeof window === "undefined")
313
+ return () => { };
314
+ historyPatchRefCount += 1;
315
+ if (historyPatchRefCount === 1) {
316
+ const originalPushState = window.history.pushState;
317
+ const originalReplaceState = window.history.replaceState;
318
+ const dispatchUrlChange = () => {
319
+ window.dispatchEvent(new Event(THREAD_URL_CHANGED_EVENT));
320
+ };
321
+ window.history.pushState = function pushState(...args) {
322
+ const result = originalPushState.apply(this, args);
323
+ dispatchUrlChange();
324
+ return result;
325
+ };
326
+ window.history.replaceState = function replaceState(...args) {
327
+ const result = originalReplaceState.apply(this, args);
328
+ dispatchUrlChange();
329
+ return result;
330
+ };
331
+ restoreHistoryPatch = () => {
332
+ window.history.pushState = originalPushState;
333
+ window.history.replaceState = originalReplaceState;
334
+ };
335
+ }
336
+ return () => {
337
+ historyPatchRefCount = Math.max(0, historyPatchRefCount - 1);
338
+ if (historyPatchRefCount === 0) {
339
+ restoreHistoryPatch?.();
340
+ restoreHistoryPatch = null;
341
+ }
342
+ };
343
+ }
344
+ function normalizeUrlThreadId(value) {
345
+ if (typeof value !== "string")
346
+ return null;
347
+ const trimmed = value.trim();
348
+ return trimmed || null;
349
+ }
350
+ function resolveThreadUrlSync(value) {
351
+ if (!value)
352
+ return { enabled: false, paramName: DEFAULT_THREAD_URL_PARAM };
353
+ if (value === true) {
354
+ return { enabled: true, paramName: DEFAULT_THREAD_URL_PARAM };
355
+ }
356
+ return {
357
+ enabled: true,
358
+ paramName: value.paramName?.trim() || DEFAULT_THREAD_URL_PARAM,
359
+ ...(hasOwn.call(value, "routeThreadId")
360
+ ? { routeThreadId: normalizeUrlThreadId(value.routeThreadId) }
361
+ : {}),
362
+ ...(value.getPath ? { getPath: value.getPath } : {}),
363
+ ...(value.navigate ? { navigate: value.navigate } : {}),
364
+ };
365
+ }
366
+ function readUrlThreadId(paramName) {
367
+ if (typeof window === "undefined")
368
+ return null;
369
+ try {
370
+ const params = new URLSearchParams(window.location.search);
371
+ return normalizeUrlThreadId(params.get(paramName) ??
372
+ (paramName === DEFAULT_THREAD_URL_PARAM
373
+ ? params.get("threadId")
374
+ : null));
375
+ }
376
+ catch {
377
+ return null;
378
+ }
379
+ }
303
380
  function runToAgentTeamTabInfo(run) {
304
381
  if (run.sourceRecord?.type && run.sourceRecord.type !== "agent-team-task") {
305
382
  return null;
@@ -321,8 +398,102 @@ function runToAgentTeamTabInfo(run) {
321
398
  status: run.status ?? "unknown",
322
399
  };
323
400
  }
324
- export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderOverlay, contentHidden = false, apiUrl = agentNativePath("/_agent-native/agent-chat"), storageKey, restoreActiveThread = true, browserTabId, scope = null, ...props }) {
325
- const { threads, activeThreadId, isLoading, createThread, switchThread, detachThread, forkThread, saveThreadData, generateTitle, searchThreads, refreshThreads, isNewThread, } = useChatThreads(apiUrl, storageKey, scope, { restoreActiveThread });
401
+ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderOverlay, contentHidden = false, apiUrl = agentNativePath("/_agent-native/agent-chat"), storageKey, restoreActiveThread = true, browserTabId, threadUrlSync = false, scope = null, ...props }) {
402
+ const { enabled: threadUrlSyncEnabled, paramName: threadUrlParamName, routeThreadId, getPath: getThreadPath, navigate: navigateThreadUrl, } = resolveThreadUrlSync(threadUrlSync);
403
+ const threadRouteControlsActiveThread = threadUrlSyncEnabled &&
404
+ threadUrlSync !== true &&
405
+ typeof threadUrlSync === "object" &&
406
+ hasOwn.call(threadUrlSync, "routeThreadId");
407
+ const [urlThreadId, setUrlThreadId] = useState(() => threadUrlSyncEnabled
408
+ ? threadRouteControlsActiveThread
409
+ ? (routeThreadId ?? null)
410
+ : readUrlThreadId(threadUrlParamName)
411
+ : null);
412
+ const urlThreadIdRef = useRef(urlThreadId);
413
+ urlThreadIdRef.current = urlThreadId;
414
+ useEffect(() => {
415
+ if (!threadUrlSyncEnabled || threadRouteControlsActiveThread)
416
+ return;
417
+ const update = () => setUrlThreadId(readUrlThreadId(threadUrlParamName));
418
+ const uninstallHistoryPatch = installHistoryThreadUrlPatch();
419
+ update();
420
+ window.addEventListener("popstate", update);
421
+ window.addEventListener(THREAD_URL_CHANGED_EVENT, update);
422
+ return () => {
423
+ uninstallHistoryPatch();
424
+ window.removeEventListener("popstate", update);
425
+ window.removeEventListener(THREAD_URL_CHANGED_EVENT, update);
426
+ };
427
+ }, [
428
+ threadRouteControlsActiveThread,
429
+ threadUrlParamName,
430
+ threadUrlSyncEnabled,
431
+ ]);
432
+ useEffect(() => {
433
+ if (!threadUrlSyncEnabled || !threadRouteControlsActiveThread)
434
+ return;
435
+ setUrlThreadId(routeThreadId ?? null);
436
+ }, [routeThreadId, threadRouteControlsActiveThread, threadUrlSyncEnabled]);
437
+ const writeThreadUrl = useCallback((threadId, options = {}) => {
438
+ if (!threadUrlSyncEnabled || typeof window === "undefined")
439
+ return;
440
+ try {
441
+ const normalizedThreadId = normalizeUrlThreadId(threadId);
442
+ let next;
443
+ if (getThreadPath) {
444
+ next = getThreadPath(normalizedThreadId);
445
+ }
446
+ else {
447
+ const url = new URL(window.location.href);
448
+ if (normalizedThreadId) {
449
+ url.searchParams.set(threadUrlParamName, normalizedThreadId);
450
+ }
451
+ else {
452
+ url.searchParams.delete(threadUrlParamName);
453
+ }
454
+ if (threadUrlParamName !== "threadId") {
455
+ url.searchParams.delete("threadId");
456
+ }
457
+ next = `${url.pathname}${url.search}${url.hash}`;
458
+ }
459
+ const current = `${window.location.pathname}${window.location.search}${window.location.hash}`;
460
+ if (next === current) {
461
+ setUrlThreadId(normalizedThreadId);
462
+ return;
463
+ }
464
+ if (getThreadPath && navigateThreadUrl) {
465
+ navigateThreadUrl(next, { replace: options.replace === true });
466
+ setUrlThreadId(normalizedThreadId);
467
+ return;
468
+ }
469
+ const method = options.replace ? "replaceState" : "pushState";
470
+ // `getThreadPath` returns a router-local path (no app basename). When we
471
+ // fall back to the raw History API instead of a router navigate, resolve
472
+ // the basename so deep-link reloads work in mounted apps.
473
+ const historyTarget = getThreadPath ? appPath(next) : next;
474
+ window.history[method](window.history.state, "", historyTarget);
475
+ setUrlThreadId(normalizedThreadId);
476
+ window.dispatchEvent(new Event(THREAD_URL_CHANGED_EVENT));
477
+ const popstate = typeof PopStateEvent === "function"
478
+ ? new PopStateEvent("popstate", { state: window.history.state })
479
+ : new Event("popstate");
480
+ window.dispatchEvent(popstate);
481
+ }
482
+ catch { }
483
+ }, [
484
+ getThreadPath,
485
+ navigateThreadUrl,
486
+ threadUrlParamName,
487
+ threadUrlSyncEnabled,
488
+ ]);
489
+ const { threads, activeThreadId, isLoading, createThread, switchThread: switchThreadState, detachThread, forkThread, saveThreadData, generateTitle, searchThreads, refreshThreads, isNewThread, } = useChatThreads(apiUrl, storageKey, scope, {
490
+ restoreActiveThread,
491
+ routeThreadId: threadUrlSyncEnabled ? urlThreadId : undefined,
492
+ });
493
+ const switchThread = useCallback((threadId, options = {}) => {
494
+ switchThreadState(threadId);
495
+ writeThreadUrl(threadId, options);
496
+ }, [switchThreadState, writeThreadUrl]);
326
497
  // Namespace all localStorage keys by storageKey when provided (for per-app isolation in frame)
327
498
  const keyPrefix = storageKey ? `:${storageKey}` : "";
328
499
  const modelSelectionKey = `${MODEL_SELECTION_STORAGE_KEY}${keyPrefix}`;
@@ -740,9 +911,19 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
740
911
  });
741
912
  // If active thread is stale, start fresh
742
913
  if (!parentMap[activeThreadId] && isStale(activeThreadId)) {
743
- createThread();
914
+ createThread().then((id) => {
915
+ if (id)
916
+ writeThreadUrl(null);
917
+ });
744
918
  }
745
- }, [activeThreadId, threads, parentMap, switchThread, createThread]);
919
+ }, [
920
+ activeThreadId,
921
+ threads,
922
+ parentMap,
923
+ switchThread,
924
+ createThread,
925
+ writeThreadUrl,
926
+ ]);
746
927
  // Ensure active thread is always in open tabs.
747
928
  // Use functional update to check inside the setter — avoids race with the
748
929
  // initialization effect that may have already added the ID in the same batch.
@@ -777,10 +958,11 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
777
958
  if (id) {
778
959
  newThreadIds.current.add(id);
779
960
  setOpenTabIds([id]);
961
+ writeThreadUrl(null, { replace: true });
780
962
  }
781
963
  });
782
964
  }
783
- }, [isLoading, openTabIds, activeThreadId, createThread]);
965
+ }, [isLoading, openTabIds, activeThreadId, createThread, writeThreadUrl]);
784
966
  useEffect(() => {
785
967
  let stopped = false;
786
968
  let timer = null;
@@ -1046,9 +1228,12 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1046
1228
  mountedTabsRef.current.add(newId);
1047
1229
  }
1048
1230
  setOpenTabIds((prev) => prev.includes(newId) ? prev : [...prev, newId]);
1231
+ if (!background) {
1232
+ writeThreadUrl(newId);
1233
+ }
1049
1234
  sendToTab(newId);
1050
1235
  if (background && previousTabId) {
1051
- switchThread(previousTabId);
1236
+ switchThreadState(previousTabId);
1052
1237
  }
1053
1238
  }
1054
1239
  });
@@ -1077,6 +1262,8 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1077
1262
  removeContextInTab,
1078
1263
  setContextInTab,
1079
1264
  switchThread,
1265
+ switchThreadState,
1266
+ writeThreadUrl,
1080
1267
  ]);
1081
1268
  // Replay submits posted before this lazy panel's listener attached. Dedup in
1082
1269
  // the handler keeps a live + replayed message single.
@@ -1147,9 +1334,10 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1147
1334
  const id = await createThread();
1148
1335
  if (id) {
1149
1336
  newThreadIds.current.add(id);
1337
+ writeThreadUrl(null);
1150
1338
  }
1151
1339
  return id;
1152
- }, [createThread]);
1340
+ }, [createThread, writeThreadUrl]);
1153
1341
  const cleanupClosedTab = useCallback((tabId) => {
1154
1342
  if (parentMapRef.current[tabId]) {
1155
1343
  dismissedSubAgentTabsRef.current.add(tabId);
@@ -1187,6 +1375,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1187
1375
  if (newId) {
1188
1376
  newThreadIds.current.add(newId);
1189
1377
  setOpenTabIds([newId]);
1378
+ writeThreadUrl(null);
1190
1379
  }
1191
1380
  });
1192
1381
  return prev; // Keep old tab until new one is ready
@@ -1199,7 +1388,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1199
1388
  return next;
1200
1389
  });
1201
1390
  cleanupClosedTab(tabId);
1202
- }, [switchThread, createThread, cleanupClosedTab]);
1391
+ }, [switchThread, createThread, cleanupClosedTab, writeThreadUrl]);
1203
1392
  const closeOtherTabs = useCallback((tabId) => {
1204
1393
  for (const id of openTabIdsRef.current) {
1205
1394
  if (id !== tabId && parentMapRef.current[id]) {
@@ -1245,7 +1434,8 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1245
1434
  if (id) {
1246
1435
  newThreadIds.current.add(id);
1247
1436
  setOpenTabIds([id]);
1248
- switchThread(id);
1437
+ switchThreadState(id);
1438
+ writeThreadUrl(null);
1249
1439
  dismissedSubAgentTabsRef.current.clear();
1250
1440
  // Clean up all old refs
1251
1441
  chatRefs.current.clear();
@@ -1256,7 +1446,7 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1256
1446
  setSubAgentNames({});
1257
1447
  setSubAgentStatuses({});
1258
1448
  }
1259
- }, [createThread, switchThread]);
1449
+ }, [createThread, switchThreadState, writeThreadUrl]);
1260
1450
  // Keyboard shortcuts dispatched from AgentPanel based on the active mode
1261
1451
  useEffect(() => {
1262
1452
  const handleCloseCurrent = () => {
@@ -1287,13 +1477,20 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1287
1477
  return;
1288
1478
  if (detail?.newThread === true) {
1289
1479
  newThreadIds.current.add(threadId);
1480
+ void createThread(threadId).then((createdId) => {
1481
+ if (!createdId)
1482
+ return;
1483
+ setOpenTabIds((prev) => prev.includes(createdId) ? prev : [...prev, createdId]);
1484
+ writeThreadUrl(null);
1485
+ });
1486
+ return;
1290
1487
  }
1291
1488
  setOpenTabIds((prev) => prev.includes(threadId) ? prev : [...prev, threadId]);
1292
1489
  switchThread(threadId);
1293
1490
  };
1294
1491
  window.addEventListener("agent-chat:open-thread", handleOpenThread);
1295
1492
  return () => window.removeEventListener("agent-chat:open-thread", handleOpenThread);
1296
- }, [switchThread]);
1493
+ }, [createThread, switchThread, writeThreadUrl]);
1297
1494
  const clearActiveTab = useCallback(() => {
1298
1495
  const tabIdToClear = activeThreadIdRef.current;
1299
1496
  void addTab().then((newTabId) => {
@@ -1421,7 +1618,12 @@ export function MultiTabAssistantChat({ showTabBar = true, renderHeader, renderO
1421
1618
  }, [generateTitle, saveThreadData]);
1422
1619
  const handleSaveThread = useCallback((threadId, data) => {
1423
1620
  saveThreadData(threadId, data);
1424
- }, [saveThreadData]);
1621
+ if (data.messageCount > 0 &&
1622
+ threadId === activeThreadIdRef.current &&
1623
+ urlThreadIdRef.current !== threadId) {
1624
+ writeThreadUrl(threadId);
1625
+ }
1626
+ }, [saveThreadData, writeThreadUrl]);
1425
1627
  // ─── Slash command handler ──────────────────────────────────────────
1426
1628
  const [helpVisible, setHelpVisible] = useState(false);
1427
1629
  const handleSlashCommand = useCallback((command) => {