@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +12 -1
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +263 -13
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +16 -4
- package/corpus/core/src/client/use-chat-threads.ts +86 -5
- package/corpus/templates/assets/actions/navigate.ts +2 -1
- package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -2
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +27 -4
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +35 -5
- package/corpus/templates/assets/app/routes/_index.tsx +12 -0
- package/corpus/templates/assets/app/routes/chat.$threadId.tsx +1 -0
- package/corpus/templates/chat/actions/navigate.ts +3 -0
- package/corpus/templates/chat/app/components/layout/Layout.tsx +7 -2
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +27 -4
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +38 -6
- package/corpus/templates/chat/app/routes/_index.tsx +13 -0
- package/corpus/templates/chat/app/routes/chat.$threadId.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/chat.$threadId.tsx +26 -0
- package/dist/client/AgentPanel.d.ts +6 -2
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +4 -4
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts +22 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +215 -13
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +7 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +9 -5
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts +6 -0
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +79 -5
- package/dist/client/use-chat-threads.js.map +1 -1
- 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,
|
|
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
|
-
|
|
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
|
|
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
|
-
}, [
|
|
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;
|
|
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;
|
|
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
|
-
|
|
92
|
-
id =
|
|
99
|
+
if (routeControlsActiveThread) {
|
|
100
|
+
id = routeThreadId;
|
|
93
101
|
}
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
}, [
|
|
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
|