@agent-native/core 0.12.40 → 0.13.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.
- package/dist/agent/run-manager.d.ts +5 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +48 -2
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +8 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +36 -5
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +42 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ConnectBuilderCard.js +1 -1
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +27 -16
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts +35 -0
- package/dist/client/RunStuckBanner.d.ts.map +1 -0
- package/dist/client/RunStuckBanner.js +66 -0
- package/dist/client/RunStuckBanner.js.map +1 -0
- package/dist/client/components/CodeRequiredDialog.js +1 -1
- package/dist/client/components/CodeRequiredDialog.js.map +1 -1
- package/dist/client/composer/ComposerPlusMenu.d.ts +3 -3
- package/dist/client/composer/ComposerPlusMenu.js +3 -3
- package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
- package/dist/client/composer/TiptapComposer.js +1 -1
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +2 -2
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts +7 -0
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +91 -43
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-run-stuck-detection.d.ts +47 -0
- package/dist/client/use-run-stuck-detection.d.ts.map +1 -0
- package/dist/client/use-run-stuck-detection.js +102 -0
- package/dist/client/use-run-stuck-detection.js.map +1 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +2 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
2
2
|
import { agentNativePath } from "./api-path.js";
|
|
3
3
|
const ACTIVE_THREAD_KEY = "agent-chat-active-thread";
|
|
4
|
+
const THREAD_DATA_CACHE_PREFIX = "agent-chat-thread-cache:";
|
|
5
|
+
/**
|
|
6
|
+
* Key for the per-thread message cache in localStorage. AssistantChat reads
|
|
7
|
+
* this synchronously on mount so existing chats can hydrate from cache and
|
|
8
|
+
* paint immediately, then refreshes from the server in the background.
|
|
9
|
+
*/
|
|
10
|
+
export function getThreadCacheKey(threadId) {
|
|
11
|
+
return `${THREAD_DATA_CACHE_PREFIX}${threadId}`;
|
|
12
|
+
}
|
|
4
13
|
export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-chat"), storageKey) {
|
|
5
14
|
const activeThreadKey = storageKey
|
|
6
15
|
? `${ACTIVE_THREAD_KEY}:${storageKey}`
|
|
7
16
|
: ACTIVE_THREAD_KEY;
|
|
8
17
|
const [threads, setThreads] = useState([]);
|
|
18
|
+
// IDs we generated client-side this session — consumers use this to know
|
|
19
|
+
// whether to skip the per-thread restore skeleton. Tracked by ref instead
|
|
20
|
+
// of state because the consumer reads it inside the render path and we
|
|
21
|
+
// never need to re-render when the set changes.
|
|
22
|
+
const newlyCreatedRef = useRef(new Set());
|
|
9
23
|
const [activeThreadId, setActiveThreadId] = useState(() => {
|
|
24
|
+
if (typeof window === "undefined")
|
|
25
|
+
return null;
|
|
10
26
|
try {
|
|
11
|
-
|
|
27
|
+
const saved = localStorage.getItem(activeThreadKey);
|
|
28
|
+
if (saved)
|
|
29
|
+
return saved;
|
|
12
30
|
}
|
|
13
|
-
catch {
|
|
14
|
-
|
|
31
|
+
catch { }
|
|
32
|
+
// No saved thread — generate one synchronously so the chat shell + composer
|
|
33
|
+
// can paint on first render instead of after a network round-trip.
|
|
34
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
35
|
+
const id = crypto.randomUUID();
|
|
36
|
+
newlyCreatedRef.current.add(id);
|
|
37
|
+
return id;
|
|
15
38
|
}
|
|
39
|
+
return null;
|
|
16
40
|
});
|
|
17
41
|
const [isLoading, setIsLoading] = useState(true);
|
|
18
42
|
const fetchedRef = useRef(false);
|
|
43
|
+
const activeThreadIdRef = useRef(activeThreadId);
|
|
44
|
+
activeThreadIdRef.current = activeThreadId;
|
|
19
45
|
// Persist active thread ID
|
|
20
46
|
useEffect(() => {
|
|
21
47
|
try {
|
|
@@ -34,46 +60,24 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
34
60
|
if (!res.ok)
|
|
35
61
|
return;
|
|
36
62
|
const data = await res.json();
|
|
37
|
-
setThreads(
|
|
63
|
+
setThreads((prev) => {
|
|
64
|
+
const loaded = (data.threads ?? []);
|
|
65
|
+
// Preserve any optimistic threads we've created this session that
|
|
66
|
+
// haven't shown up in the server list yet (POST still in-flight).
|
|
67
|
+
const loadedIds = new Set(loaded.map((t) => t.id));
|
|
68
|
+
const optimisticOnly = prev.filter((t) => newlyCreatedRef.current.has(t.id) && !loadedIds.has(t.id));
|
|
69
|
+
return [...optimisticOnly, ...loaded];
|
|
70
|
+
});
|
|
38
71
|
return data.threads;
|
|
39
72
|
}
|
|
40
73
|
catch {
|
|
41
74
|
return undefined;
|
|
42
75
|
}
|
|
43
76
|
}, [apiUrl]);
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
fetchedRef.current = true;
|
|
49
|
-
(async () => {
|
|
50
|
-
setIsLoading(true);
|
|
51
|
-
const loadedThreads = await fetchThreads();
|
|
52
|
-
if (loadedThreads && loadedThreads.length > 0) {
|
|
53
|
-
// If the saved active thread still exists, keep it. Otherwise use the most recent.
|
|
54
|
-
const savedId = activeThreadId;
|
|
55
|
-
if (!savedId || !loadedThreads.find((t) => t.id === savedId)) {
|
|
56
|
-
setActiveThreadId(loadedThreads[0].id);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
// No threads — create the first one
|
|
61
|
-
try {
|
|
62
|
-
const res = await fetch(`${apiUrl}/threads`, { method: "POST" });
|
|
63
|
-
if (res.ok) {
|
|
64
|
-
const thread = await res.json();
|
|
65
|
-
setThreads([thread]);
|
|
66
|
-
setActiveThreadId(thread.id);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
catch { }
|
|
70
|
-
}
|
|
71
|
-
setIsLoading(false);
|
|
72
|
-
})();
|
|
73
|
-
}, [fetchThreads, apiUrl, activeThreadId]);
|
|
74
|
-
const createThread = useCallback((preferredId) => {
|
|
75
|
-
// Generate ID client-side for instant UI response
|
|
76
|
-
const id = preferredId || crypto.randomUUID();
|
|
77
|
+
// Persist a client-generated thread to the server in the background.
|
|
78
|
+
// Optimistically adds it to the local thread list so callers can render
|
|
79
|
+
// immediately; rolls back on failure.
|
|
80
|
+
const persistNewThread = useCallback((id) => {
|
|
77
81
|
const now = Date.now();
|
|
78
82
|
const optimistic = {
|
|
79
83
|
id,
|
|
@@ -83,9 +87,7 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
83
87
|
createdAt: now,
|
|
84
88
|
updatedAt: now,
|
|
85
89
|
};
|
|
86
|
-
setThreads((prev) => [optimistic, ...prev]);
|
|
87
|
-
setActiveThreadId(id);
|
|
88
|
-
// Persist to server in the background
|
|
90
|
+
setThreads((prev) => prev.some((t) => t.id === id) ? prev : [optimistic, ...prev]);
|
|
89
91
|
fetch(`${apiUrl}/threads`, {
|
|
90
92
|
method: "POST",
|
|
91
93
|
headers: { "Content-Type": "application/json" },
|
|
@@ -103,13 +105,47 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
103
105
|
setThreads((prev) => prev.map((thread) => (thread.id === id ? created : thread)));
|
|
104
106
|
})
|
|
105
107
|
.catch(() => {
|
|
106
|
-
// If server fails, remove the optimistic thread instead of leaving a
|
|
107
|
-
// phantom active tab that disappears on the next refresh.
|
|
108
108
|
setThreads((prev) => prev.filter((t) => t.id !== id));
|
|
109
|
+
newlyCreatedRef.current.delete(id);
|
|
109
110
|
setActiveThreadId((current) => (current === id ? null : current));
|
|
110
111
|
});
|
|
111
|
-
return Promise.resolve(id);
|
|
112
112
|
}, [apiUrl]);
|
|
113
|
+
// Initial load. Runs in the background — does NOT gate the consumer's
|
|
114
|
+
// first paint. The composer renders against the optimistic active thread
|
|
115
|
+
// we set up in useState above; this fetch just populates the history list
|
|
116
|
+
// and reconciles a stale saved active id.
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (fetchedRef.current)
|
|
119
|
+
return;
|
|
120
|
+
fetchedRef.current = true;
|
|
121
|
+
// Persist any thread we optimistically created during the initial render.
|
|
122
|
+
for (const id of newlyCreatedRef.current) {
|
|
123
|
+
persistNewThread(id);
|
|
124
|
+
}
|
|
125
|
+
(async () => {
|
|
126
|
+
const loadedThreads = await fetchThreads();
|
|
127
|
+
if (loadedThreads && loadedThreads.length > 0) {
|
|
128
|
+
const savedId = activeThreadIdRef.current;
|
|
129
|
+
// If the saved active thread isn't on the server (and isn't one we
|
|
130
|
+
// just created client-side), fall back to the most recent.
|
|
131
|
+
if (savedId &&
|
|
132
|
+
!newlyCreatedRef.current.has(savedId) &&
|
|
133
|
+
!loadedThreads.find((t) => t.id === savedId)) {
|
|
134
|
+
setActiveThreadId(loadedThreads[0].id);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
setIsLoading(false);
|
|
138
|
+
})();
|
|
139
|
+
}, [fetchThreads, persistNewThread]);
|
|
140
|
+
const createThread = useCallback((preferredId) => {
|
|
141
|
+
// Generate ID client-side for instant UI response
|
|
142
|
+
const id = preferredId || crypto.randomUUID();
|
|
143
|
+
newlyCreatedRef.current.add(id);
|
|
144
|
+
setActiveThreadId(id);
|
|
145
|
+
persistNewThread(id);
|
|
146
|
+
return Promise.resolve(id);
|
|
147
|
+
}, [persistNewThread]);
|
|
148
|
+
const isNewThread = useCallback((id) => newlyCreatedRef.current.has(id), []);
|
|
113
149
|
const switchThread = useCallback((id) => {
|
|
114
150
|
setActiveThreadId(id);
|
|
115
151
|
}, []);
|
|
@@ -120,6 +156,10 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
120
156
|
});
|
|
121
157
|
}
|
|
122
158
|
catch { }
|
|
159
|
+
try {
|
|
160
|
+
localStorage.removeItem(getThreadCacheKey(id));
|
|
161
|
+
}
|
|
162
|
+
catch { }
|
|
123
163
|
setThreads((prev) => {
|
|
124
164
|
const next = prev.filter((t) => t.id !== id);
|
|
125
165
|
if (id === activeThreadId) {
|
|
@@ -136,6 +176,13 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
136
176
|
});
|
|
137
177
|
}, [apiUrl, activeThreadId, createThread]);
|
|
138
178
|
const saveThreadData = useCallback(async (id, data) => {
|
|
179
|
+
// Cache locally so the next mount of this thread can hydrate
|
|
180
|
+
// synchronously and skip the per-message restore skeleton. Quota errors
|
|
181
|
+
// (5–10MB cap) are swallowed — the thread just falls back to fetching.
|
|
182
|
+
try {
|
|
183
|
+
localStorage.setItem(getThreadCacheKey(id), data.threadData);
|
|
184
|
+
}
|
|
185
|
+
catch { }
|
|
139
186
|
try {
|
|
140
187
|
await fetch(`${apiUrl}/threads/${encodeURIComponent(id)}`, {
|
|
141
188
|
method: "PUT",
|
|
@@ -238,6 +285,7 @@ export function useChatThreads(apiUrl = agentNativePath("/_agent-native/agent-ch
|
|
|
238
285
|
generateTitle,
|
|
239
286
|
searchThreads,
|
|
240
287
|
refreshThreads,
|
|
288
|
+
isNewThread,
|
|
241
289
|
};
|
|
242
290
|
}
|
|
243
291
|
//# sourceMappingURL=use-chat-threads.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat-threads.js","sourceRoot":"","sources":["../../src/client/use-chat-threads.ts"],"names":[],"mappings":"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","sourcesContent":["import { useState, useEffect, useCallback, useRef } from \"react\";\nimport { agentNativePath } from \"./api-path.js\";\n\nexport interface ChatThreadSummary {\n id: string;\n title: string;\n preview: string;\n messageCount: number;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface ChatThreadData {\n id: string;\n ownerEmail: string;\n title: string;\n preview: string;\n threadData: string;\n messageCount: number;\n createdAt: number;\n updatedAt: number;\n}\n\nconst ACTIVE_THREAD_KEY = \"agent-chat-active-thread\";\n\nexport function useChatThreads(\n apiUrl = agentNativePath(\"/_agent-native/agent-chat\"),\n storageKey?: string,\n) {\n const activeThreadKey = storageKey\n ? `${ACTIVE_THREAD_KEY}:${storageKey}`\n : ACTIVE_THREAD_KEY;\n const [threads, setThreads] = useState<ChatThreadSummary[]>([]);\n const [activeThreadId, setActiveThreadId] = useState<string | null>(() => {\n try {\n return localStorage.getItem(activeThreadKey);\n } catch {\n return null;\n }\n });\n const [isLoading, setIsLoading] = useState(true);\n const fetchedRef = useRef(false);\n\n // Persist active thread ID\n useEffect(() => {\n try {\n if (activeThreadId) {\n localStorage.setItem(activeThreadKey, activeThreadId);\n } else {\n localStorage.removeItem(activeThreadKey);\n }\n } catch {}\n }, [activeThreadId, activeThreadKey]);\n\n const fetchThreads = useCallback(async () => {\n try {\n const res = await fetch(`${apiUrl}/threads`);\n if (!res.ok) return;\n const data = await res.json();\n setThreads(data.threads ?? []);\n return data.threads as ChatThreadSummary[];\n } catch {\n return undefined;\n }\n }, [apiUrl]);\n\n // Initial load\n useEffect(() => {\n if (fetchedRef.current) return;\n fetchedRef.current = true;\n\n (async () => {\n setIsLoading(true);\n const loadedThreads = await fetchThreads();\n\n if (loadedThreads && loadedThreads.length > 0) {\n // If the saved active thread still exists, keep it. Otherwise use the most recent.\n const savedId = activeThreadId;\n if (!savedId || !loadedThreads.find((t) => t.id === savedId)) {\n setActiveThreadId(loadedThreads[0].id);\n }\n } else {\n // No threads — create the first one\n try {\n const res = await fetch(`${apiUrl}/threads`, { method: \"POST\" });\n if (res.ok) {\n const thread = await res.json();\n setThreads([thread]);\n setActiveThreadId(thread.id);\n }\n } catch {}\n }\n setIsLoading(false);\n })();\n }, [fetchThreads, apiUrl, activeThreadId]);\n\n const createThread = useCallback(\n (preferredId?: string): Promise<string | null> => {\n // Generate ID client-side for instant UI response\n const id = preferredId || crypto.randomUUID();\n const now = Date.now();\n const optimistic: ChatThreadSummary = {\n id,\n title: \"\",\n preview: \"\",\n messageCount: 0,\n createdAt: now,\n updatedAt: now,\n };\n setThreads((prev) => [optimistic, ...prev]);\n setActiveThreadId(id);\n\n // Persist to server in the background\n fetch(`${apiUrl}/threads`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ id }),\n })\n .then(async (res) => {\n if (!res.ok) {\n throw new Error(`Thread create failed with ${res.status}`);\n }\n const created = (await res\n .json()\n .catch(() => null)) as ChatThreadSummary | null;\n if (!created) return;\n setThreads((prev) =>\n prev.map((thread) => (thread.id === id ? created : thread)),\n );\n })\n .catch(() => {\n // If server fails, remove the optimistic thread instead of leaving a\n // phantom active tab that disappears on the next refresh.\n setThreads((prev) => prev.filter((t) => t.id !== id));\n setActiveThreadId((current) => (current === id ? null : current));\n });\n\n return Promise.resolve(id);\n },\n [apiUrl],\n );\n\n const switchThread = useCallback((id: string) => {\n setActiveThreadId(id);\n }, []);\n\n const removeThread = useCallback(\n async (id: string) => {\n try {\n await fetch(`${apiUrl}/threads/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n });\n } catch {}\n setThreads((prev) => {\n const next = prev.filter((t) => t.id !== id);\n if (id === activeThreadId) {\n // Switch to the next available thread, or create new if empty\n if (next.length > 0) {\n setActiveThreadId(next[0].id);\n } else {\n // Create a new thread\n createThread();\n }\n }\n return next;\n });\n },\n [apiUrl, activeThreadId, createThread],\n );\n\n const saveThreadData = useCallback(\n async (\n id: string,\n data: {\n threadData: string;\n title: string;\n preview: string;\n messageCount?: number;\n },\n ) => {\n try {\n await fetch(`${apiUrl}/threads/${encodeURIComponent(id)}`, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data),\n });\n // Update local thread list metadata\n setThreads((prev) =>\n prev.map((t) =>\n t.id === id\n ? {\n ...t,\n title: data.title,\n preview: data.preview,\n ...(data.messageCount != null && {\n messageCount: data.messageCount,\n }),\n updatedAt: Date.now(),\n }\n : t,\n ),\n );\n } catch {}\n },\n [apiUrl],\n );\n\n const generateTitle = useCallback(\n async (threadId: string, message: string): Promise<string | null> => {\n try {\n const res = await fetch(`${apiUrl}/generate-title`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message }),\n });\n if (!res.ok) return null;\n const data = await res.json();\n const title = data.title;\n if (!title) return null;\n // Update the title in local state\n setThreads((prev) =>\n prev.map((t) => (t.id === threadId ? { ...t, title } : t)),\n );\n return title;\n } catch {\n return null;\n }\n },\n [apiUrl],\n );\n\n const forkThread = useCallback(\n async (sourceId: string): Promise<string | null> => {\n const id = crypto.randomUUID();\n try {\n const res = await fetch(\n `${apiUrl}/threads/${encodeURIComponent(sourceId)}/fork`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ id }),\n },\n );\n if (!res.ok) {\n // Surface failures so a click on the Fork button isn't a silent\n // no-op when the source thread can't be found or auth has lapsed.\n console.error(\n `[chat] fork failed for ${sourceId}: ${res.status} ${res.statusText}`,\n );\n return null;\n }\n const thread = await res.json();\n setThreads((prev) => [\n {\n id: thread.id,\n title: thread.title,\n preview: thread.preview,\n messageCount: thread.messageCount,\n createdAt: thread.createdAt,\n updatedAt: thread.updatedAt,\n },\n ...prev,\n ]);\n return thread.id;\n } catch (err) {\n console.error(`[chat] fork threw for ${sourceId}:`, err);\n return null;\n }\n },\n [apiUrl],\n );\n\n const searchThreads = useCallback(\n async (query: string): Promise<ChatThreadSummary[]> => {\n try {\n const res = await fetch(\n `${apiUrl}/threads?q=${encodeURIComponent(query)}`,\n );\n if (!res.ok) return [];\n const data = await res.json();\n return data.threads ?? [];\n } catch {\n return [];\n }\n },\n [apiUrl],\n );\n\n const refreshThreads = useCallback(() => {\n fetchThreads();\n }, [fetchThreads]);\n\n return {\n threads,\n activeThreadId,\n isLoading,\n createThread,\n switchThread,\n deleteThread: removeThread,\n forkThread,\n saveThreadData,\n generateTitle,\n searchThreads,\n refreshThreads,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"use-chat-threads.js","sourceRoot":"","sources":["../../src/client/use-chat-threads.ts"],"names":[],"mappings":"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","sourcesContent":["import { useState, useEffect, useCallback, useRef } from \"react\";\nimport { agentNativePath } from \"./api-path.js\";\n\nexport interface ChatThreadSummary {\n id: string;\n title: string;\n preview: string;\n messageCount: number;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface ChatThreadData {\n id: string;\n ownerEmail: string;\n title: string;\n preview: string;\n threadData: string;\n messageCount: number;\n createdAt: number;\n updatedAt: number;\n}\n\nconst ACTIVE_THREAD_KEY = \"agent-chat-active-thread\";\nconst THREAD_DATA_CACHE_PREFIX = \"agent-chat-thread-cache:\";\n\n/**\n * Key for the per-thread message cache in localStorage. AssistantChat reads\n * this synchronously on mount so existing chats can hydrate from cache and\n * paint immediately, then refreshes from the server in the background.\n */\nexport function getThreadCacheKey(threadId: string): string {\n return `${THREAD_DATA_CACHE_PREFIX}${threadId}`;\n}\n\nexport function useChatThreads(\n apiUrl = agentNativePath(\"/_agent-native/agent-chat\"),\n storageKey?: string,\n) {\n const activeThreadKey = storageKey\n ? `${ACTIVE_THREAD_KEY}:${storageKey}`\n : ACTIVE_THREAD_KEY;\n const [threads, setThreads] = useState<ChatThreadSummary[]>([]);\n\n // IDs we generated client-side this session — consumers use this to know\n // whether to skip the per-thread restore skeleton. Tracked by ref instead\n // of state because the consumer reads it inside the render path and we\n // never need to re-render when the set changes.\n const newlyCreatedRef = useRef<Set<string>>(new Set());\n\n const [activeThreadId, setActiveThreadId] = useState<string | null>(() => {\n if (typeof window === \"undefined\") return null;\n try {\n const saved = localStorage.getItem(activeThreadKey);\n if (saved) return saved;\n } catch {}\n // No saved thread — generate one synchronously so the chat shell + composer\n // can paint on first render instead of after a network round-trip.\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) {\n const id = crypto.randomUUID();\n newlyCreatedRef.current.add(id);\n return id;\n }\n return null;\n });\n const [isLoading, setIsLoading] = useState(true);\n const fetchedRef = useRef(false);\n const activeThreadIdRef = useRef(activeThreadId);\n activeThreadIdRef.current = activeThreadId;\n\n // Persist active thread ID\n useEffect(() => {\n try {\n if (activeThreadId) {\n localStorage.setItem(activeThreadKey, activeThreadId);\n } else {\n localStorage.removeItem(activeThreadKey);\n }\n } catch {}\n }, [activeThreadId, activeThreadKey]);\n\n const fetchThreads = useCallback(async () => {\n try {\n const res = await fetch(`${apiUrl}/threads`);\n if (!res.ok) return;\n const data = await res.json();\n setThreads((prev) => {\n const loaded = (data.threads ?? []) as ChatThreadSummary[];\n // Preserve any optimistic threads we've created this session that\n // haven't shown up in the server list yet (POST still in-flight).\n const loadedIds = new Set(loaded.map((t) => t.id));\n const optimisticOnly = prev.filter(\n (t) => newlyCreatedRef.current.has(t.id) && !loadedIds.has(t.id),\n );\n return [...optimisticOnly, ...loaded];\n });\n return data.threads as ChatThreadSummary[];\n } catch {\n return undefined;\n }\n }, [apiUrl]);\n\n // Persist a client-generated thread to the server in the background.\n // Optimistically adds it to the local thread list so callers can render\n // immediately; rolls back on failure.\n const persistNewThread = useCallback(\n (id: string) => {\n const now = Date.now();\n const optimistic: ChatThreadSummary = {\n id,\n title: \"\",\n preview: \"\",\n messageCount: 0,\n createdAt: now,\n updatedAt: now,\n };\n setThreads((prev) =>\n prev.some((t) => t.id === id) ? prev : [optimistic, ...prev],\n );\n fetch(`${apiUrl}/threads`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ id }),\n })\n .then(async (res) => {\n if (!res.ok) {\n throw new Error(`Thread create failed with ${res.status}`);\n }\n const created = (await res\n .json()\n .catch(() => null)) as ChatThreadSummary | null;\n if (!created) return;\n setThreads((prev) =>\n prev.map((thread) => (thread.id === id ? created : thread)),\n );\n })\n .catch(() => {\n setThreads((prev) => prev.filter((t) => t.id !== id));\n newlyCreatedRef.current.delete(id);\n setActiveThreadId((current) => (current === id ? null : current));\n });\n },\n [apiUrl],\n );\n\n // Initial load. Runs in the background — does NOT gate the consumer's\n // first paint. The composer renders against the optimistic active thread\n // we set up in useState above; this fetch just populates the history list\n // and reconciles a stale saved active id.\n useEffect(() => {\n if (fetchedRef.current) return;\n fetchedRef.current = true;\n\n // Persist any thread we optimistically created during the initial render.\n for (const id of newlyCreatedRef.current) {\n persistNewThread(id);\n }\n\n (async () => {\n const loadedThreads = await fetchThreads();\n if (loadedThreads && loadedThreads.length > 0) {\n const savedId = activeThreadIdRef.current;\n // If the saved active thread isn't on the server (and isn't one we\n // just created client-side), fall back to the most recent.\n if (\n savedId &&\n !newlyCreatedRef.current.has(savedId) &&\n !loadedThreads.find((t) => t.id === savedId)\n ) {\n setActiveThreadId(loadedThreads[0].id);\n }\n }\n setIsLoading(false);\n })();\n }, [fetchThreads, persistNewThread]);\n\n const createThread = useCallback(\n (preferredId?: string): Promise<string | null> => {\n // Generate ID client-side for instant UI response\n const id = preferredId || crypto.randomUUID();\n newlyCreatedRef.current.add(id);\n setActiveThreadId(id);\n persistNewThread(id);\n return Promise.resolve(id);\n },\n [persistNewThread],\n );\n\n const isNewThread = useCallback(\n (id: string) => newlyCreatedRef.current.has(id),\n [],\n );\n\n const switchThread = useCallback((id: string) => {\n setActiveThreadId(id);\n }, []);\n\n const removeThread = useCallback(\n async (id: string) => {\n try {\n await fetch(`${apiUrl}/threads/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n });\n } catch {}\n try {\n localStorage.removeItem(getThreadCacheKey(id));\n } catch {}\n setThreads((prev) => {\n const next = prev.filter((t) => t.id !== id);\n if (id === activeThreadId) {\n // Switch to the next available thread, or create new if empty\n if (next.length > 0) {\n setActiveThreadId(next[0].id);\n } else {\n // Create a new thread\n createThread();\n }\n }\n return next;\n });\n },\n [apiUrl, activeThreadId, createThread],\n );\n\n const saveThreadData = useCallback(\n async (\n id: string,\n data: {\n threadData: string;\n title: string;\n preview: string;\n messageCount?: number;\n },\n ) => {\n // Cache locally so the next mount of this thread can hydrate\n // synchronously and skip the per-message restore skeleton. Quota errors\n // (5–10MB cap) are swallowed — the thread just falls back to fetching.\n try {\n localStorage.setItem(getThreadCacheKey(id), data.threadData);\n } catch {}\n try {\n await fetch(`${apiUrl}/threads/${encodeURIComponent(id)}`, {\n method: \"PUT\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(data),\n });\n // Update local thread list metadata\n setThreads((prev) =>\n prev.map((t) =>\n t.id === id\n ? {\n ...t,\n title: data.title,\n preview: data.preview,\n ...(data.messageCount != null && {\n messageCount: data.messageCount,\n }),\n updatedAt: Date.now(),\n }\n : t,\n ),\n );\n } catch {}\n },\n [apiUrl],\n );\n\n const generateTitle = useCallback(\n async (threadId: string, message: string): Promise<string | null> => {\n try {\n const res = await fetch(`${apiUrl}/generate-title`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ message }),\n });\n if (!res.ok) return null;\n const data = await res.json();\n const title = data.title;\n if (!title) return null;\n // Update the title in local state\n setThreads((prev) =>\n prev.map((t) => (t.id === threadId ? { ...t, title } : t)),\n );\n return title;\n } catch {\n return null;\n }\n },\n [apiUrl],\n );\n\n const forkThread = useCallback(\n async (sourceId: string): Promise<string | null> => {\n const id = crypto.randomUUID();\n try {\n const res = await fetch(\n `${apiUrl}/threads/${encodeURIComponent(sourceId)}/fork`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ id }),\n },\n );\n if (!res.ok) {\n // Surface failures so a click on the Fork button isn't a silent\n // no-op when the source thread can't be found or auth has lapsed.\n console.error(\n `[chat] fork failed for ${sourceId}: ${res.status} ${res.statusText}`,\n );\n return null;\n }\n const thread = await res.json();\n setThreads((prev) => [\n {\n id: thread.id,\n title: thread.title,\n preview: thread.preview,\n messageCount: thread.messageCount,\n createdAt: thread.createdAt,\n updatedAt: thread.updatedAt,\n },\n ...prev,\n ]);\n return thread.id;\n } catch (err) {\n console.error(`[chat] fork threw for ${sourceId}:`, err);\n return null;\n }\n },\n [apiUrl],\n );\n\n const searchThreads = useCallback(\n async (query: string): Promise<ChatThreadSummary[]> => {\n try {\n const res = await fetch(\n `${apiUrl}/threads?q=${encodeURIComponent(query)}`,\n );\n if (!res.ok) return [];\n const data = await res.json();\n return data.threads ?? [];\n } catch {\n return [];\n }\n },\n [apiUrl],\n );\n\n const refreshThreads = useCallback(() => {\n fetchThreads();\n }, [fetchThreads]);\n\n return {\n threads,\n activeThreadId,\n isLoading,\n createThread,\n switchThread,\n deleteThread: removeThread,\n forkThread,\n saveThreadData,\n generateTitle,\n searchThreads,\n refreshThreads,\n isNewThread,\n };\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-thread chat run health, derived from the durable `last_progress_at`
|
|
3
|
+
* timestamp on the server. Drives the user-visible "this chat looks stuck"
|
|
4
|
+
* affordance — distinct from the silent reconnect logic in
|
|
5
|
+
* `agent-chat-adapter.ts`, which keeps trying in the background. When
|
|
6
|
+
* automatic recovery isn't making progress (for whatever reason), this
|
|
7
|
+
* hook surfaces a Retry / Cancel button to the user instead of leaving
|
|
8
|
+
* them staring at a frozen spinner.
|
|
9
|
+
*/
|
|
10
|
+
export interface RunStuckState {
|
|
11
|
+
/** True when an active run hasn't emitted an event for `stuckThresholdMs`. */
|
|
12
|
+
isStuck: boolean;
|
|
13
|
+
/** ID of the active run, or null when nothing is in flight. */
|
|
14
|
+
runId: string | null;
|
|
15
|
+
/** Server-side run status ("running" / "completed" / "errored" / etc.). */
|
|
16
|
+
status: string | null;
|
|
17
|
+
/** Server timestamp (ms) of the last emitted event, or null if none yet. */
|
|
18
|
+
lastProgressAt: number | null;
|
|
19
|
+
/** Milliseconds since `lastProgressAt`, or null. */
|
|
20
|
+
stuckSinceMs: number | null;
|
|
21
|
+
/** Server timestamp (ms) of the last process-alive heartbeat. */
|
|
22
|
+
heartbeatAt: number | null;
|
|
23
|
+
}
|
|
24
|
+
export interface UseRunStuckDetectionOptions {
|
|
25
|
+
/** The thread to monitor. Pass null/undefined to disable polling. */
|
|
26
|
+
threadId: string | null | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Threshold above which an in-flight run is considered stuck. The default
|
|
29
|
+
* sits comfortably above the adapter's 75s no-progress reconnect — by then
|
|
30
|
+
* automatic recovery has already had its chance.
|
|
31
|
+
*/
|
|
32
|
+
stuckThresholdMs?: number;
|
|
33
|
+
/** Poll interval. Default 5_000ms. */
|
|
34
|
+
pollIntervalMs?: number;
|
|
35
|
+
/** API base path. Default `/_agent-native/agent-chat`. */
|
|
36
|
+
apiUrl?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function useRunStuckDetection({ threadId, stuckThresholdMs, pollIntervalMs, apiUrl, }: UseRunStuckDetectionOptions): RunStuckState;
|
|
39
|
+
/**
|
|
40
|
+
* POST `/runs/:id/abort` so the server flips the run to "aborted" and the
|
|
41
|
+
* adapter's reconnect loop exits cleanly. Returns the run id that was
|
|
42
|
+
* aborted (or null on failure) so callers can correlate observability
|
|
43
|
+
* events. Best-effort — failures are swallowed, since the user's intent
|
|
44
|
+
* is already captured locally.
|
|
45
|
+
*/
|
|
46
|
+
export declare function useAbortRun(apiUrl?: string): (runId: string, reason?: string) => Promise<string | null>;
|
|
47
|
+
//# sourceMappingURL=use-run-stuck-detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-run-stuck-detection.d.ts","sourceRoot":"","sources":["../../src/client/use-run-stuck-detection.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,4EAA4E;IAC5E,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,oDAAoD;IACpD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iEAAiE;IACjE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,2BAA2B;IAC1C,qEAAqE;IACrE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sCAAsC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAuBD,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,gBAA6C,EAC7C,cAAyC,EACzC,MAAM,GACP,EAAE,2BAA2B,GAAG,aAAa,CAoE7C;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,WAEzB,MAAM,WAAU,MAAM,KAAY,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoBzE"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useEffect, useState, useCallback } from "react";
|
|
2
|
+
import { agentNativePath } from "./api-path.js";
|
|
3
|
+
const DEFAULT_STUCK_THRESHOLD_MS = 90_000;
|
|
4
|
+
const DEFAULT_POLL_INTERVAL_MS = 5_000;
|
|
5
|
+
const IDLE_BACKOFF_INTERVAL_MS = 15_000;
|
|
6
|
+
const EMPTY_STATE = {
|
|
7
|
+
isStuck: false,
|
|
8
|
+
runId: null,
|
|
9
|
+
status: null,
|
|
10
|
+
lastProgressAt: null,
|
|
11
|
+
stuckSinceMs: null,
|
|
12
|
+
heartbeatAt: null,
|
|
13
|
+
};
|
|
14
|
+
export function useRunStuckDetection({ threadId, stuckThresholdMs = DEFAULT_STUCK_THRESHOLD_MS, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, apiUrl, }) {
|
|
15
|
+
const [state, setState] = useState(EMPTY_STATE);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
// Reset on every thread change so the previous thread's stuck banner
|
|
18
|
+
// doesn't bleed onto the new one before the first poll completes.
|
|
19
|
+
setState(EMPTY_STATE);
|
|
20
|
+
if (!threadId)
|
|
21
|
+
return;
|
|
22
|
+
const base = apiUrl ?? agentNativePath("/_agent-native/agent-chat");
|
|
23
|
+
let cancelled = false;
|
|
24
|
+
let timer = null;
|
|
25
|
+
const poll = async () => {
|
|
26
|
+
if (cancelled)
|
|
27
|
+
return;
|
|
28
|
+
let nextDelay = pollIntervalMs;
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetch(`${base}/runs/active?threadId=${encodeURIComponent(threadId)}`, { credentials: "same-origin" });
|
|
31
|
+
if (cancelled)
|
|
32
|
+
return;
|
|
33
|
+
if (res.ok) {
|
|
34
|
+
const data = (await res.json());
|
|
35
|
+
const lastProgressAt = data.lastProgressAt ?? null;
|
|
36
|
+
const stuckSinceMs = lastProgressAt != null ? Date.now() - lastProgressAt : null;
|
|
37
|
+
const isStuck = Boolean(data.active &&
|
|
38
|
+
data.status === "running" &&
|
|
39
|
+
stuckSinceMs != null &&
|
|
40
|
+
stuckSinceMs > stuckThresholdMs);
|
|
41
|
+
setState({
|
|
42
|
+
isStuck,
|
|
43
|
+
runId: data.runId ?? null,
|
|
44
|
+
status: data.status ?? null,
|
|
45
|
+
lastProgressAt,
|
|
46
|
+
stuckSinceMs,
|
|
47
|
+
heartbeatAt: data.heartbeatAt ?? null,
|
|
48
|
+
});
|
|
49
|
+
// Back off polling when nothing is in flight — there's no point
|
|
50
|
+
// hammering the endpoint while the chat is idle. We still poll
|
|
51
|
+
// occasionally so a fresh run started in another tab is picked up.
|
|
52
|
+
if (!data.active || data.status !== "running") {
|
|
53
|
+
nextDelay = IDLE_BACKOFF_INTERVAL_MS;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Network blip — leave previous state. Next tick will retry.
|
|
59
|
+
}
|
|
60
|
+
if (!cancelled) {
|
|
61
|
+
timer = setTimeout(poll, nextDelay);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
// Stagger the first poll so a freshly-started run isn't immediately
|
|
65
|
+
// classified as stuck before the server has had a chance to record
|
|
66
|
+
// any progress events.
|
|
67
|
+
timer = setTimeout(poll, 2_000);
|
|
68
|
+
return () => {
|
|
69
|
+
cancelled = true;
|
|
70
|
+
if (timer)
|
|
71
|
+
clearTimeout(timer);
|
|
72
|
+
};
|
|
73
|
+
}, [threadId, stuckThresholdMs, pollIntervalMs, apiUrl]);
|
|
74
|
+
return state;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* POST `/runs/:id/abort` so the server flips the run to "aborted" and the
|
|
78
|
+
* adapter's reconnect loop exits cleanly. Returns the run id that was
|
|
79
|
+
* aborted (or null on failure) so callers can correlate observability
|
|
80
|
+
* events. Best-effort — failures are swallowed, since the user's intent
|
|
81
|
+
* is already captured locally.
|
|
82
|
+
*/
|
|
83
|
+
export function useAbortRun(apiUrl) {
|
|
84
|
+
return useCallback(async (runId, reason = "user") => {
|
|
85
|
+
const base = apiUrl ?? agentNativePath("/_agent-native/agent-chat");
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch(`${base}/runs/${encodeURIComponent(runId)}/abort`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
headers: { "Content-Type": "application/json" },
|
|
90
|
+
credentials: "same-origin",
|
|
91
|
+
body: JSON.stringify({ reason }),
|
|
92
|
+
});
|
|
93
|
+
if (!res.ok)
|
|
94
|
+
return null;
|
|
95
|
+
return runId;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}, [apiUrl]);
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=use-run-stuck-detection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-run-stuck-detection.js","sourceRoot":"","sources":["../../src/client/use-run-stuck-detection.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState, useCallback } from \"react\";\nimport { agentNativePath } from \"./api-path.js\";\n\n/**\n * Per-thread chat run health, derived from the durable `last_progress_at`\n * timestamp on the server. Drives the user-visible \"this chat looks stuck\"\n * affordance — distinct from the silent reconnect logic in\n * `agent-chat-adapter.ts`, which keeps trying in the background. When\n * automatic recovery isn't making progress (for whatever reason), this\n * hook surfaces a Retry / Cancel button to the user instead of leaving\n * them staring at a frozen spinner.\n */\nexport interface RunStuckState {\n /** True when an active run hasn't emitted an event for `stuckThresholdMs`. */\n isStuck: boolean;\n /** ID of the active run, or null when nothing is in flight. */\n runId: string | null;\n /** Server-side run status (\"running\" / \"completed\" / \"errored\" / etc.). */\n status: string | null;\n /** Server timestamp (ms) of the last emitted event, or null if none yet. */\n lastProgressAt: number | null;\n /** Milliseconds since `lastProgressAt`, or null. */\n stuckSinceMs: number | null;\n /** Server timestamp (ms) of the last process-alive heartbeat. */\n heartbeatAt: number | null;\n}\n\nexport interface UseRunStuckDetectionOptions {\n /** The thread to monitor. Pass null/undefined to disable polling. */\n threadId: string | null | undefined;\n /**\n * Threshold above which an in-flight run is considered stuck. The default\n * sits comfortably above the adapter's 75s no-progress reconnect — by then\n * automatic recovery has already had its chance.\n */\n stuckThresholdMs?: number;\n /** Poll interval. Default 5_000ms. */\n pollIntervalMs?: number;\n /** API base path. Default `/_agent-native/agent-chat`. */\n apiUrl?: string;\n}\n\nconst DEFAULT_STUCK_THRESHOLD_MS = 90_000;\nconst DEFAULT_POLL_INTERVAL_MS = 5_000;\nconst IDLE_BACKOFF_INTERVAL_MS = 15_000;\n\ninterface ActiveRunResponse {\n active: boolean;\n runId?: string;\n status?: string;\n heartbeatAt: number | null;\n lastProgressAt?: number | null;\n}\n\nconst EMPTY_STATE: RunStuckState = {\n isStuck: false,\n runId: null,\n status: null,\n lastProgressAt: null,\n stuckSinceMs: null,\n heartbeatAt: null,\n};\n\nexport function useRunStuckDetection({\n threadId,\n stuckThresholdMs = DEFAULT_STUCK_THRESHOLD_MS,\n pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,\n apiUrl,\n}: UseRunStuckDetectionOptions): RunStuckState {\n const [state, setState] = useState<RunStuckState>(EMPTY_STATE);\n\n useEffect(() => {\n // Reset on every thread change so the previous thread's stuck banner\n // doesn't bleed onto the new one before the first poll completes.\n setState(EMPTY_STATE);\n if (!threadId) return;\n\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n let cancelled = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const poll = async () => {\n if (cancelled) return;\n let nextDelay = pollIntervalMs;\n try {\n const res = await fetch(\n `${base}/runs/active?threadId=${encodeURIComponent(threadId)}`,\n { credentials: \"same-origin\" },\n );\n if (cancelled) return;\n if (res.ok) {\n const data = (await res.json()) as ActiveRunResponse;\n const lastProgressAt = data.lastProgressAt ?? null;\n const stuckSinceMs =\n lastProgressAt != null ? Date.now() - lastProgressAt : null;\n const isStuck = Boolean(\n data.active &&\n data.status === \"running\" &&\n stuckSinceMs != null &&\n stuckSinceMs > stuckThresholdMs,\n );\n setState({\n isStuck,\n runId: data.runId ?? null,\n status: data.status ?? null,\n lastProgressAt,\n stuckSinceMs,\n heartbeatAt: data.heartbeatAt ?? null,\n });\n // Back off polling when nothing is in flight — there's no point\n // hammering the endpoint while the chat is idle. We still poll\n // occasionally so a fresh run started in another tab is picked up.\n if (!data.active || data.status !== \"running\") {\n nextDelay = IDLE_BACKOFF_INTERVAL_MS;\n }\n }\n } catch {\n // Network blip — leave previous state. Next tick will retry.\n }\n if (!cancelled) {\n timer = setTimeout(poll, nextDelay);\n }\n };\n\n // Stagger the first poll so a freshly-started run isn't immediately\n // classified as stuck before the server has had a chance to record\n // any progress events.\n timer = setTimeout(poll, 2_000);\n\n return () => {\n cancelled = true;\n if (timer) clearTimeout(timer);\n };\n }, [threadId, stuckThresholdMs, pollIntervalMs, apiUrl]);\n\n return state;\n}\n\n/**\n * POST `/runs/:id/abort` so the server flips the run to \"aborted\" and the\n * adapter's reconnect loop exits cleanly. Returns the run id that was\n * aborted (or null on failure) so callers can correlate observability\n * events. Best-effort — failures are swallowed, since the user's intent\n * is already captured locally.\n */\nexport function useAbortRun(apiUrl?: string) {\n return useCallback(\n async (runId: string, reason: string = \"user\"): Promise<string | null> => {\n const base = apiUrl ?? agentNativePath(\"/_agent-native/agent-chat\");\n try {\n const res = await fetch(\n `${base}/runs/${encodeURIComponent(runId)}/abort`,\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n credentials: \"same-origin\",\n body: JSON.stringify({ reason }),\n },\n );\n if (!res.ok) return null;\n return runId;\n } catch {\n return null;\n }\n },\n [apiUrl],\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-chat-plugin.d.ts","sourceRoot":"","sources":["../../src/server/agent-chat-plugin.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"agent-chat-plugin.d.ts","sourceRoot":"","sources":["../../src/server/agent-chat-plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -3789,6 +3789,7 @@ Non-code requests are still fine on this surface — read data, navigate the UI,
|
|
|
3789
3789
|
threadId,
|
|
3790
3790
|
status: "idle",
|
|
3791
3791
|
heartbeatAt: null,
|
|
3792
|
+
lastProgressAt: null,
|
|
3792
3793
|
};
|
|
3793
3794
|
}
|
|
3794
3795
|
return {
|
|
@@ -3797,6 +3798,7 @@ Non-code requests are still fine on this surface — read data, navigate the UI,
|
|
|
3797
3798
|
threadId: run.threadId,
|
|
3798
3799
|
status: run.status,
|
|
3799
3800
|
heartbeatAt: run.heartbeatAt,
|
|
3801
|
+
lastProgressAt: run.lastProgressAt,
|
|
3800
3802
|
};
|
|
3801
3803
|
}
|
|
3802
3804
|
setResponseStatus(event, 405);
|