@agent-native/core 0.72.1 → 0.72.3

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 (37) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/AgentPanel.tsx +12 -1
  5. package/corpus/core/src/client/MultiTabAssistantChat.tsx +263 -13
  6. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +16 -4
  7. package/corpus/core/src/client/use-chat-threads.ts +86 -5
  8. package/corpus/templates/assets/actions/navigate.ts +2 -1
  9. package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -2
  10. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +27 -4
  11. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +35 -5
  12. package/corpus/templates/assets/app/routes/_index.tsx +12 -0
  13. package/corpus/templates/assets/app/routes/chat.$threadId.tsx +1 -0
  14. package/corpus/templates/chat/actions/navigate.ts +3 -0
  15. package/corpus/templates/chat/app/components/layout/Layout.tsx +7 -2
  16. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +27 -4
  17. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +38 -6
  18. package/corpus/templates/chat/app/routes/_index.tsx +13 -0
  19. package/corpus/templates/chat/app/routes/chat.$threadId.tsx +1 -0
  20. package/corpus/templates/dispatch/app/routes/chat.$threadId.tsx +26 -0
  21. package/dist/client/AgentPanel.d.ts +6 -2
  22. package/dist/client/AgentPanel.d.ts.map +1 -1
  23. package/dist/client/AgentPanel.js +4 -4
  24. package/dist/client/AgentPanel.js.map +1 -1
  25. package/dist/client/MultiTabAssistantChat.d.ts +22 -1
  26. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  27. package/dist/client/MultiTabAssistantChat.js +215 -13
  28. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  29. package/dist/client/use-agent-chat-home-handoff.d.ts +7 -1
  30. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  31. package/dist/client/use-agent-chat-home-handoff.js +9 -5
  32. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  33. package/dist/client/use-chat-threads.d.ts +6 -0
  34. package/dist/client/use-chat-threads.d.ts.map +1 -1
  35. package/dist/client/use-chat-threads.js +79 -5
  36. package/dist/client/use-chat-threads.js.map +1 -1
  37. package/package.json +1 -1
@@ -11,6 +11,12 @@ export interface UseAgentChatHomeHandoffLinksOptions {
11
11
  storageKey?: string | null;
12
12
  /** Router-local path for the full-page chat route. Defaults to "/". */
13
13
  chatPath?: string;
14
+ /**
15
+ * Matches every router-local pathname owned by the full-page chat surface.
16
+ * Defaults to an exact match against `chatPath`. Pass this to cover deep
17
+ * links such as `/chat/:threadId`.
18
+ */
19
+ isChatPath?: (pathname: string) => boolean;
14
20
  /** Disable link interception without changing hook call order. */
15
21
  enabled?: boolean;
16
22
  }
@@ -23,5 +29,5 @@ export declare function useAgentChatHomeHandoff({ storageKey, activePath, enable
23
29
  * Intercepts ordinary in-app links clicked from a full-page chat route so the
24
30
  * page chat can morph into the destination AgentSidebar and keep its thread.
25
31
  */
26
- export declare function useAgentChatHomeHandoffLinks({ storageKey, chatPath, enabled, }: UseAgentChatHomeHandoffLinksOptions): void;
32
+ export declare function useAgentChatHomeHandoffLinks({ storageKey, chatPath, isChatPath, enabled, }: UseAgentChatHomeHandoffLinksOptions): void;
27
33
  //# sourceMappingURL=use-agent-chat-home-handoff.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-chat-home-handoff.d.ts","sourceRoot":"","sources":["../../src/client/use-agent-chat-home-handoff.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,8BAA8B;IAC7C,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mCAAmC;IAClD,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAkED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,UAAU,EACV,UAAU,EACV,OAAc,GACf,EAAE,8BAA8B,GAAG,OAAO,CAc1C;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,UAAU,EACV,QAAc,EACd,OAAc,GACf,EAAE,mCAAmC,GAAG,IAAI,CAmC5C"}
1
+ {"version":3,"file":"use-agent-chat-home-handoff.d.ts","sourceRoot":"","sources":["../../src/client/use-agent-chat-home-handoff.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,8BAA8B;IAC7C,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mCAAmC;IAClD,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3C,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAkED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,UAAU,EACV,UAAU,EACV,OAAc,GACf,EAAE,8BAA8B,GAAG,OAAO,CAc1C;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,EAC3C,UAAU,EACV,QAAc,EACd,UAAU,EACV,OAAc,GACf,EAAE,mCAAmC,GAAG,IAAI,CAwC5C"}
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from "react";
1
+ import { useEffect, useRef, useState } from "react";
2
2
  import { useLocation, useNavigate } from "react-router";
3
3
  import { appBasePath } from "./api-path.js";
4
4
  import { consumeAgentChatHomeHandoff, markAgentChatHomeHandoff, navigateWithAgentChatViewTransition, } from "./chat-view-transition.js";
@@ -82,11 +82,15 @@ export function useAgentChatHomeHandoff({ storageKey, activePath, enabled = true
82
82
  * Intercepts ordinary in-app links clicked from a full-page chat route so the
83
83
  * page chat can morph into the destination AgentSidebar and keep its thread.
84
84
  */
85
- export function useAgentChatHomeHandoffLinks({ storageKey, chatPath = "/", enabled = true, }) {
85
+ export function useAgentChatHomeHandoffLinks({ storageKey, chatPath = "/", isChatPath, enabled = true, }) {
86
86
  const location = useLocation();
87
87
  const navigate = useNavigate();
88
+ const matchesChatPathRef = useRef(() => false);
89
+ matchesChatPathRef.current =
90
+ isChatPath ?? ((pathname) => pathname === chatPath);
88
91
  useEffect(() => {
89
- if (!enabled || stripBasePath(location.pathname) !== chatPath)
92
+ const matchesChatPath = (pathname) => matchesChatPathRef.current(pathname);
93
+ if (!enabled || !matchesChatPath(stripBasePath(location.pathname)))
90
94
  return;
91
95
  if (typeof document === "undefined")
92
96
  return;
@@ -104,7 +108,7 @@ export function useAgentChatHomeHandoffLinks({ storageKey, chatPath = "/", enabl
104
108
  const path = localPathFromAnchor(anchor);
105
109
  const pathname = path ? pathnameFromLocalPath(path) : "";
106
110
  if (!path ||
107
- pathname === chatPath ||
111
+ matchesChatPath(pathname) ||
108
112
  isFrameworkOrApiPath(pathname) ||
109
113
  isStaticAssetPath(pathname)) {
110
114
  return;
@@ -115,6 +119,6 @@ export function useAgentChatHomeHandoffLinks({ storageKey, chatPath = "/", enabl
115
119
  }
116
120
  document.addEventListener("click", handleClick, true);
117
121
  return () => document.removeEventListener("click", handleClick, true);
118
- }, [chatPath, enabled, location.pathname, navigate, storageKey]);
122
+ }, [enabled, location.pathname, navigate, storageKey]);
119
123
  }
120
124
  //# sourceMappingURL=use-agent-chat-home-handoff.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-chat-home-handoff.js","sourceRoot":"","sources":["../../src/client/use-agent-chat-home-handoff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,mCAAmC,GACpC,MAAM,2BAA2B,CAAC;AAoBnC,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,IAAI,MAAM,KAAK,QAAQ;YAAE,OAAO,GAAG,CAAC;QACpC,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;YAC9C,SAAS;QACX,CAAC;QACD,IACE,MAAM,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC;YACjC,MAAM,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,EACjC,CAAC;YACD,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7C,SAAS;QACX,CAAC;QACD,MAAM;IACR,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;AACzC,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAgB;IAC5C,OAAO,CACL,QAAQ,KAAK,gBAAgB;QAC7B,QAAQ,CAAC,UAAU,CAAC,iBAAiB,CAAC;QACtC,QAAQ,KAAK,MAAM;QACnB,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAC7B,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB;IACzC,MAAM,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACpD,OAAO,sBAAsB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAyB;IACpD,IAAI,CAAC,MAAM,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,QAAQ,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACvD,OAAO,aAAa,CAAC,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAC9B,KAAiB,EACjB,MAAyB;IAEzB,IAAI,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACrE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO;QAAE,OAAO,KAAK,CAAC;IAC7D,IAAI,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAClD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,EACtC,UAAU,EACV,UAAU,EACV,OAAO,GAAG,IAAI,GACiB;IAC/B,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAEpE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,cAAc,CAAC,IAAI,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,IAAI,2BAA2B,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5C,cAAc,CAAC,UAAU,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IAEtC,OAAO,OAAO,IAAI,WAAW,KAAK,UAAU,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4BAA4B,CAAC,EAC3C,UAAU,EACV,QAAQ,GAAG,GAAG,EACd,OAAO,GAAG,IAAI,GACsB;IACpC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAE/B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,IAAI,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,QAAQ;YAAE,OAAO;QACtE,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE,OAAO;QAE5C,SAAS,WAAW,CAAC,KAAiB;YACpC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;YAC5B,IAAI,CAAC,CAAC,MAAM,YAAY,OAAO,CAAC;gBAAE,OAAO;YACzC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACzC,IAAI,CAAC,CAAC,MAAM,YAAY,iBAAiB,CAAC;gBAAE,OAAO;YACnD,IAAI,CAAC,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC;gBAAE,OAAO;YACpD,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC;gBAAE,OAAO;YAEhD,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;YACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,IACE,CAAC,IAAI;gBACL,QAAQ,KAAK,QAAQ;gBACrB,oBAAoB,CAAC,QAAQ,CAAC;gBAC9B,iBAAiB,CAAC,QAAQ,CAAC,EAC3B,CAAC;gBACD,OAAO;YACT,CAAC;YAED,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,wBAAwB,CAAC,UAAU,CAAC,CAAC;YACrC,mCAAmC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACtD,CAAC;QAED,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;QACtD,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IACxE,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;AACnE,CAAC","sourcesContent":["import { useEffect, useState } from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\nimport { appBasePath } from \"./api-path.js\";\nimport {\n consumeAgentChatHomeHandoff,\n markAgentChatHomeHandoff,\n navigateWithAgentChatViewTransition,\n} from \"./chat-view-transition.js\";\n\nexport interface UseAgentChatHomeHandoffOptions {\n /** Namespace shared by the full-page chat and AgentSidebar surfaces. */\n storageKey?: string | null;\n /** The current destination path that receives the sidebar handoff. */\n activePath: string;\n /** Disable consumption without changing hook call order. */\n enabled?: boolean;\n}\n\nexport interface UseAgentChatHomeHandoffLinksOptions {\n /** Namespace shared by the full-page chat and AgentSidebar surfaces. */\n storageKey?: string | null;\n /** Router-local path for the full-page chat route. Defaults to \"/\". */\n chatPath?: string;\n /** Disable link interception without changing hook call order. */\n enabled?: boolean;\n}\n\nfunction stripBasePath(path: string): string {\n const basePath = appBasePath();\n if (!basePath) return path;\n let result = path;\n for (let i = 0; i < 4; i += 1) {\n if (result === basePath) return \"/\";\n if (result.startsWith(`${basePath}/`)) {\n result = result.slice(basePath.length) || \"/\";\n continue;\n }\n if (\n result.startsWith(`${basePath}?`) ||\n result.startsWith(`${basePath}#`)\n ) {\n result = `/${result.slice(basePath.length)}`;\n continue;\n }\n break;\n }\n return result;\n}\n\nfunction pathnameFromLocalPath(path: string): string {\n return path.split(/[?#]/, 1)[0] || \"/\";\n}\n\nfunction isFrameworkOrApiPath(pathname: string): boolean {\n return (\n pathname === \"/_agent-native\" ||\n pathname.startsWith(\"/_agent-native/\") ||\n pathname === \"/api\" ||\n pathname.startsWith(\"/api/\")\n );\n}\n\nfunction isStaticAssetPath(pathname: string): boolean {\n const lastSegment = pathname.split(\"/\").pop() ?? \"\";\n return /\\.[A-Za-z0-9]{1,12}$/.test(lastSegment);\n}\n\nfunction localPathFromAnchor(anchor: HTMLAnchorElement): string | null {\n if (!anchor.href) return null;\n try {\n const url = new URL(anchor.href);\n if (url.origin !== window.location.origin) return null;\n return stripBasePath(`${url.pathname}${url.search}${url.hash}`);\n } catch {\n return null;\n }\n}\n\nfunction shouldHandleAnchorClick(\n event: MouseEvent,\n anchor: HTMLAnchorElement,\n): boolean {\n if (event.defaultPrevented || event.button !== 0) return false;\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return false;\n }\n if (anchor.target && anchor.target !== \"_self\") return false;\n if (anchor.hasAttribute(\"download\")) return false;\n return true;\n}\n\n/**\n * Returns true for the route that has just received a full-page-chat handoff.\n * Pass the result to `AgentSidebar openOnChatRunning`.\n */\nexport function useAgentChatHomeHandoff({\n storageKey,\n activePath,\n enabled = true,\n}: UseAgentChatHomeHandoffOptions): boolean {\n const [handoffPath, setHandoffPath] = useState<string | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n setHandoffPath(null);\n return;\n }\n if (consumeAgentChatHomeHandoff(storageKey)) {\n setHandoffPath(activePath);\n }\n }, [activePath, enabled, storageKey]);\n\n return enabled && handoffPath === activePath;\n}\n\n/**\n * Intercepts ordinary in-app links clicked from a full-page chat route so the\n * page chat can morph into the destination AgentSidebar and keep its thread.\n */\nexport function useAgentChatHomeHandoffLinks({\n storageKey,\n chatPath = \"/\",\n enabled = true,\n}: UseAgentChatHomeHandoffLinksOptions): void {\n const location = useLocation();\n const navigate = useNavigate();\n\n useEffect(() => {\n if (!enabled || stripBasePath(location.pathname) !== chatPath) return;\n if (typeof document === \"undefined\") return;\n\n function handleClick(event: MouseEvent) {\n const target = event.target;\n if (!(target instanceof Element)) return;\n const anchor = target.closest(\"a[href]\");\n if (!(anchor instanceof HTMLAnchorElement)) return;\n if (!shouldHandleAnchorClick(event, anchor)) return;\n if (anchor.closest(\".agent-panel-root\")) return;\n\n const path = localPathFromAnchor(anchor);\n const pathname = path ? pathnameFromLocalPath(path) : \"\";\n if (\n !path ||\n pathname === chatPath ||\n isFrameworkOrApiPath(pathname) ||\n isStaticAssetPath(pathname)\n ) {\n return;\n }\n\n event.preventDefault();\n markAgentChatHomeHandoff(storageKey);\n navigateWithAgentChatViewTransition(navigate, path);\n }\n\n document.addEventListener(\"click\", handleClick, true);\n return () => document.removeEventListener(\"click\", handleClick, true);\n }, [chatPath, enabled, location.pathname, navigate, storageKey]);\n}\n"]}
1
+ {"version":3,"file":"use-agent-chat-home-handoff.js","sourceRoot":"","sources":["../../src/client/use-agent-chat-home-handoff.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\nimport { appBasePath } from \"./api-path.js\";\nimport {\n consumeAgentChatHomeHandoff,\n markAgentChatHomeHandoff,\n navigateWithAgentChatViewTransition,\n} from \"./chat-view-transition.js\";\n\nexport interface UseAgentChatHomeHandoffOptions {\n /** Namespace shared by the full-page chat and AgentSidebar surfaces. */\n storageKey?: string | null;\n /** The current destination path that receives the sidebar handoff. */\n activePath: string;\n /** Disable consumption without changing hook call order. */\n enabled?: boolean;\n}\n\nexport interface UseAgentChatHomeHandoffLinksOptions {\n /** Namespace shared by the full-page chat and AgentSidebar surfaces. */\n storageKey?: string | null;\n /** Router-local path for the full-page chat route. Defaults to \"/\". */\n chatPath?: string;\n /**\n * Matches every router-local pathname owned by the full-page chat surface.\n * Defaults to an exact match against `chatPath`. Pass this to cover deep\n * links such as `/chat/:threadId`.\n */\n isChatPath?: (pathname: string) => boolean;\n /** Disable link interception without changing hook call order. */\n enabled?: boolean;\n}\n\nfunction stripBasePath(path: string): string {\n const basePath = appBasePath();\n if (!basePath) return path;\n let result = path;\n for (let i = 0; i < 4; i += 1) {\n if (result === basePath) return \"/\";\n if (result.startsWith(`${basePath}/`)) {\n result = result.slice(basePath.length) || \"/\";\n continue;\n }\n if (\n result.startsWith(`${basePath}?`) ||\n result.startsWith(`${basePath}#`)\n ) {\n result = `/${result.slice(basePath.length)}`;\n continue;\n }\n break;\n }\n return result;\n}\n\nfunction pathnameFromLocalPath(path: string): string {\n return path.split(/[?#]/, 1)[0] || \"/\";\n}\n\nfunction isFrameworkOrApiPath(pathname: string): boolean {\n return (\n pathname === \"/_agent-native\" ||\n pathname.startsWith(\"/_agent-native/\") ||\n pathname === \"/api\" ||\n pathname.startsWith(\"/api/\")\n );\n}\n\nfunction isStaticAssetPath(pathname: string): boolean {\n const lastSegment = pathname.split(\"/\").pop() ?? \"\";\n return /\\.[A-Za-z0-9]{1,12}$/.test(lastSegment);\n}\n\nfunction localPathFromAnchor(anchor: HTMLAnchorElement): string | null {\n if (!anchor.href) return null;\n try {\n const url = new URL(anchor.href);\n if (url.origin !== window.location.origin) return null;\n return stripBasePath(`${url.pathname}${url.search}${url.hash}`);\n } catch {\n return null;\n }\n}\n\nfunction shouldHandleAnchorClick(\n event: MouseEvent,\n anchor: HTMLAnchorElement,\n): boolean {\n if (event.defaultPrevented || event.button !== 0) return false;\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {\n return false;\n }\n if (anchor.target && anchor.target !== \"_self\") return false;\n if (anchor.hasAttribute(\"download\")) return false;\n return true;\n}\n\n/**\n * Returns true for the route that has just received a full-page-chat handoff.\n * Pass the result to `AgentSidebar openOnChatRunning`.\n */\nexport function useAgentChatHomeHandoff({\n storageKey,\n activePath,\n enabled = true,\n}: UseAgentChatHomeHandoffOptions): boolean {\n const [handoffPath, setHandoffPath] = useState<string | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n setHandoffPath(null);\n return;\n }\n if (consumeAgentChatHomeHandoff(storageKey)) {\n setHandoffPath(activePath);\n }\n }, [activePath, enabled, storageKey]);\n\n return enabled && handoffPath === activePath;\n}\n\n/**\n * Intercepts ordinary in-app links clicked from a full-page chat route so the\n * page chat can morph into the destination AgentSidebar and keep its thread.\n */\nexport function useAgentChatHomeHandoffLinks({\n storageKey,\n chatPath = \"/\",\n isChatPath,\n enabled = true,\n}: UseAgentChatHomeHandoffLinksOptions): void {\n const location = useLocation();\n const navigate = useNavigate();\n const matchesChatPathRef = useRef<(pathname: string) => boolean>(() => false);\n matchesChatPathRef.current =\n isChatPath ?? ((pathname: string) => pathname === chatPath);\n\n useEffect(() => {\n const matchesChatPath = (pathname: string) =>\n matchesChatPathRef.current(pathname);\n if (!enabled || !matchesChatPath(stripBasePath(location.pathname))) return;\n if (typeof document === \"undefined\") return;\n\n function handleClick(event: MouseEvent) {\n const target = event.target;\n if (!(target instanceof Element)) return;\n const anchor = target.closest(\"a[href]\");\n if (!(anchor instanceof HTMLAnchorElement)) return;\n if (!shouldHandleAnchorClick(event, anchor)) return;\n if (anchor.closest(\".agent-panel-root\")) return;\n\n const path = localPathFromAnchor(anchor);\n const pathname = path ? pathnameFromLocalPath(path) : \"\";\n if (\n !path ||\n matchesChatPath(pathname) ||\n isFrameworkOrApiPath(pathname) ||\n isStaticAssetPath(pathname)\n ) {\n return;\n }\n\n event.preventDefault();\n markAgentChatHomeHandoff(storageKey);\n navigateWithAgentChatViewTransition(navigate, path);\n }\n\n document.addEventListener(\"click\", handleClick, true);\n return () => document.removeEventListener(\"click\", handleClick, true);\n }, [enabled, location.pathname, navigate, storageKey]);\n}\n"]}
@@ -49,6 +49,12 @@ export interface UseChatThreadsOptions {
49
49
  autoCreate?: boolean;
50
50
  /** Restore the active thread from localStorage. Defaults to true. */
51
51
  restoreActiveThread?: boolean;
52
+ /**
53
+ * Route-owned active thread. `undefined` preserves the legacy localStorage
54
+ * source of truth; a string opens that thread; `null` means the URL is in
55
+ * create/new-chat mode.
56
+ */
57
+ routeThreadId?: string | null;
52
58
  }
53
59
  export declare function useChatThreads(apiUrl?: string, storageKey?: string, scope?: ChatThreadScope | null, options?: UseChatThreadsOptions): {
54
60
  threads: ChatThreadSummary[];
@@ -1 +1 @@
1
- {"version":3,"file":"use-chat-threads.d.ts","sourceRoot":"","sources":["../../src/client/use-chat-threads.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAoB,SAAQ,oBAAoB;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,KAAK,iBAAiB,GAAG,WAAW,GAAG,WAAW,CAAC;AAMnD,MAAM,WAAW,qBAAqB;IACpC,+EAA+E;IAC/E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qEAAqE;IACrE,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAoFD,wBAAgB,cAAc,CAC5B,MAAM,SAA+C,EACrD,UAAU,CAAC,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,eAAe,GAAG,IAAI,EAC9B,OAAO,CAAC,EAAE,qBAAqB;;;;iCA0Xd,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;uBAoNV,MAAM;uBAK/B,MAAM;6BAtMA,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC;0BAkBtB,MAAM,UAAU,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;8BAuD1C,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;6BAgEzB,MAAM,SAAS,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;2BA4M7C,MAAM,mBACC,kBAAkB,GAAG,IAAI,KACzC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;yBA/GnB,MAAM,QACJ;QACJ,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,iBAAiB,CAAC;KACjC;8BA4Ec,MAAM,WAAW,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;2BAgInD,MAAM,KAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;oCAgBlC,MAAM,KAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;sCAgB7C,MAAM,KAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;sCAkB5C,MAAM,KAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;;sBAhTzD,MAAM;EAyVd"}
1
+ {"version":3,"file":"use-chat-threads.d.ts","sourceRoot":"","sources":["../../src/client/use-chat-threads.ts"],"names":[],"mappings":"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"}
@@ -42,6 +42,12 @@ function createLocalThreadId() {
42
42
  }
43
43
  return `thread-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
44
44
  }
45
+ function normalizeThreadId(value) {
46
+ if (typeof value !== "string")
47
+ return null;
48
+ const trimmed = value.trim();
49
+ return trimmed || null;
50
+ }
45
51
  function scopesMatch(a, b) {
46
52
  if (!a || !b)
47
53
  return false;
@@ -69,6 +75,8 @@ function nextThreadTitle(currentTitle, incomingTitle, incomingPreview, source =
69
75
  export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-chat"), storageKey, scope, options) {
70
76
  const autoCreate = options?.autoCreate !== false;
71
77
  const restoreActiveThread = options?.restoreActiveThread !== false;
78
+ const routeControlsActiveThread = options?.routeThreadId !== undefined;
79
+ const routeThreadId = normalizeThreadId(options?.routeThreadId);
72
80
  // Each (storageKey, scope) pair gets its own active-thread localStorage key
73
81
  // for chats that belong to a resource. General chats keep using the unscoped
74
82
  // key even while the user is looking at a resource, so clicking into a deck,
@@ -88,11 +96,18 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
88
96
  let id = null;
89
97
  let isNew = false;
90
98
  if (typeof window !== "undefined") {
91
- try {
92
- id = restoreActiveThread ? localStorage.getItem(activeThreadKey) : null;
99
+ if (routeControlsActiveThread) {
100
+ id = routeThreadId;
93
101
  }
94
- catch {
95
- id = null;
102
+ else {
103
+ try {
104
+ id = restoreActiveThread
105
+ ? localStorage.getItem(activeThreadKey)
106
+ : null;
107
+ }
108
+ catch {
109
+ id = null;
110
+ }
96
111
  }
97
112
  if (!id && autoCreate) {
98
113
  id = createLocalThreadId();
@@ -204,6 +219,10 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
204
219
  const persistedKeyRef = useRef(activeThreadKey);
205
220
  useEffect(() => {
206
221
  if (persistedKeyRef.current !== activeThreadKey) {
222
+ if (routeControlsActiveThread) {
223
+ persistedKeyRef.current = activeThreadKey;
224
+ return;
225
+ }
207
226
  const currentId = activeThreadIdRef.current;
208
227
  if (currentId) {
209
228
  const currentThreadScope = readKnownThreadScope(currentId);
@@ -239,6 +258,11 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
239
258
  return;
240
259
  }
241
260
  try {
261
+ if (routeControlsActiveThread && !routeThreadId) {
262
+ localStorage.removeItem(activeThreadKey);
263
+ localStorage.removeItem(activeThreadSeenKey);
264
+ return;
265
+ }
242
266
  if (activeThreadId) {
243
267
  const threadScope = readKnownThreadScope(activeThreadId);
244
268
  if (threadScope === undefined)
@@ -260,6 +284,8 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
260
284
  addOptimisticThread,
261
285
  autoCreate,
262
286
  readKnownThreadScope,
287
+ routeControlsActiveThread,
288
+ routeThreadId,
263
289
  storageKey,
264
290
  threads,
265
291
  ]);
@@ -365,11 +391,20 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
365
391
  return;
366
392
  }
367
393
  const loadedHasSavedId = Boolean(savedId && loadedThreads.some((t) => t.id === savedId));
394
+ const savedIdCameFromRoute = Boolean(savedId) &&
395
+ routeControlsActiveThread &&
396
+ routeThreadId === savedId;
368
397
  if (savedId &&
369
398
  newlyCreatedRef.current.has(savedId) &&
370
399
  !loadedHasSavedId) {
371
400
  addOptimisticThread(savedId, scopeRef.current ?? null);
372
401
  }
402
+ else if (savedId && savedIdCameFromRoute && !loadedHasSavedId) {
403
+ // A deep link may point to a thread that is not in the current list
404
+ // response. Keep it route-owned so AssistantChat can restore it via
405
+ // /threads/:id instead of reclassifying it as a new empty tab.
406
+ setActiveThreadId(savedId);
407
+ }
373
408
  else if (savedId &&
374
409
  !newlyCreatedRef.current.has(savedId) &&
375
410
  !loadedHasSavedId) {
@@ -406,7 +441,13 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
406
441
  }
407
442
  setIsLoading(false);
408
443
  })();
409
- }, [fetchThreads, addOptimisticThread, autoCreate]);
444
+ }, [
445
+ fetchThreads,
446
+ addOptimisticThread,
447
+ autoCreate,
448
+ routeControlsActiveThread,
449
+ routeThreadId,
450
+ ]);
410
451
  const createThread = useCallback((preferredId) => {
411
452
  // Generate ID client-side for instant UI response. No POST — the
412
453
  // server creates the row when the user actually sends a message,
@@ -418,6 +459,39 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
418
459
  setActiveThreadId(id);
419
460
  return Promise.resolve(id);
420
461
  }, [addOptimisticThread]);
462
+ useEffect(() => {
463
+ if (!routeControlsActiveThread)
464
+ return;
465
+ if (routeThreadId) {
466
+ if (activeThreadIdRef.current !== routeThreadId) {
467
+ setActiveThreadId(routeThreadId);
468
+ }
469
+ return;
470
+ }
471
+ const currentId = activeThreadIdRef.current;
472
+ const currentThread = currentId
473
+ ? threadsRef.current.find((thread) => thread.id === currentId)
474
+ : undefined;
475
+ const currentIsUnsavedNewThread = currentId !== null &&
476
+ newlyCreatedRef.current.has(currentId) &&
477
+ (currentThread?.messageCount ?? 0) === 0;
478
+ if (currentIsUnsavedNewThread)
479
+ return;
480
+ if (!autoCreate) {
481
+ if (currentId !== null)
482
+ setActiveThreadId(null);
483
+ return;
484
+ }
485
+ const id = createLocalThreadId();
486
+ newlyCreatedRef.current.add(id);
487
+ addOptimisticThread(id, scopeRef.current ?? null);
488
+ setActiveThreadId(id);
489
+ }, [
490
+ addOptimisticThread,
491
+ autoCreate,
492
+ routeControlsActiveThread,
493
+ routeThreadId,
494
+ ]);
421
495
  // Drop a thread's scope so it becomes a general (cross-resource) chat.
422
496
  // This is the "Detach from <deck>" escape hatch in the UI. The PUT
423
497
  // also bumps the thread's updatedAt so it surfaces in the All Chats