@blocksdiy/react-common 1.52.1 → 1.53.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/hooks/agent-conversation-context.d.ts +13 -0
- package/dist/hooks/agent-conversation-context.d.ts.map +1 -1
- package/dist/hooks/agent-conversation-context.js +22 -1
- package/dist/hooks/agent-thread-store.d.ts +62 -0
- package/dist/hooks/agent-thread-store.d.ts.map +1 -0
- package/dist/hooks/agent-thread-store.js +170 -0
- package/dist/hooks/floating-agent-chat.d.ts +77 -0
- package/dist/hooks/floating-agent-chat.d.ts.map +1 -0
- package/dist/hooks/floating-agent-chat.js +89 -0
- package/dist/hooks/use-agent-thread.d.ts +67 -0
- package/dist/hooks/use-agent-thread.d.ts.map +1 -0
- package/dist/hooks/use-agent-thread.js +193 -0
- package/dist/hooks/use-agent-threads.d.ts +33 -1
- package/dist/hooks/use-agent-threads.d.ts.map +1 -1
- package/dist/hooks/use-agent-threads.js +132 -83
- package/dist/hooks/use-media-query.d.ts +2 -0
- package/dist/hooks/use-media-query.d.ts.map +1 -0
- package/dist/hooks/use-media-query.js +11 -0
- package/dist/hooks/use-resizable-panel.d.ts +66 -0
- package/dist/hooks/use-resizable-panel.d.ts.map +1 -0
- package/dist/hooks/use-resizable-panel.js +184 -0
- package/package.json +2 -2
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// One conversation's row, live — the reader for a surface that shows a single conversation
|
|
2
|
+
// without the agent's index (the floating agent chat on a page). It reads from the agent's
|
|
3
|
+
// shared store: a row the index already walked costs nothing; otherwise one single-thread read,
|
|
4
|
+
// deferred while an index for the agent is still walking so the two never race for the same row.
|
|
5
|
+
// A draft (the new chat, nothing sent) reads nothing: the server has no row for it yet.
|
|
6
|
+
// The result wears the index result's shape (one row or none), so `useAgentConversation` and
|
|
7
|
+
// `AgentConversationProvider`'s root run on it unchanged. The decisions are pure and tested
|
|
8
|
+
// (`singleThreadMountPlan`, `singleThreadEventOutcome`); the hook is the React shell around them.
|
|
9
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
10
|
+
import { agentThreadKey, optimisticThreadAfterSend, overlayOptimisticThreads, retireOptimisticThreadsTakenOver, } from "./agent-thread-live-update.js";
|
|
11
|
+
import { acquireAgentThreadStore } from "./agent-thread-store.js";
|
|
12
|
+
import { agentThreadPath, AgentThreadsRequestError, postAgentThreadRename, useAgentThreadsFetchJson, } from "./use-agent-threads.js";
|
|
13
|
+
/**
|
|
14
|
+
* Mount: the store's row when the index already has it; a draft reads nothing; otherwise one
|
|
15
|
+
* read — deferred while an index is walking, so the two never race for the same row.
|
|
16
|
+
*/
|
|
17
|
+
export const singleThreadMountPlan = (store, key, isDraft) => {
|
|
18
|
+
const cached = store.getRow(key);
|
|
19
|
+
if (cached) {
|
|
20
|
+
return { kind: "cached", thread: cached };
|
|
21
|
+
}
|
|
22
|
+
if (isDraft) {
|
|
23
|
+
return { kind: "draft" };
|
|
24
|
+
}
|
|
25
|
+
return store.isIndexLoading ? { kind: "await-index" } : { kind: "read" };
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The cache is the row's source of truth: it keeps the newest row it has seen, so what it holds
|
|
29
|
+
* is what the surface shows — a stale wire event, or a page an index walked later, never
|
|
30
|
+
* regresses it. `awaitingIndex`: the mount deferred its read to a walking index.
|
|
31
|
+
*/
|
|
32
|
+
export const singleThreadEventOutcome = (event, store, key, awaitingIndex) => {
|
|
33
|
+
if (event.kind === "primed") {
|
|
34
|
+
const primed = store.getRow(key);
|
|
35
|
+
if (primed) {
|
|
36
|
+
return { kind: "row", thread: primed };
|
|
37
|
+
}
|
|
38
|
+
return awaitingIndex && !store.isIndexLoading ? { kind: "read" } : { kind: "ignore" };
|
|
39
|
+
}
|
|
40
|
+
if (event.kind === "removed") {
|
|
41
|
+
return agentThreadKey(event.removed) === key ? { kind: "removed" } : { kind: "ignore" };
|
|
42
|
+
}
|
|
43
|
+
if (agentThreadKey(event.thread) !== key) {
|
|
44
|
+
return { kind: "ignore" };
|
|
45
|
+
}
|
|
46
|
+
return { kind: "row", thread: store.getRow(key) ?? event.thread };
|
|
47
|
+
};
|
|
48
|
+
/** The single-thread read's "no row" answer — a conversation the server has not seen — as opposed to a failure. */
|
|
49
|
+
export const isMissingRowError = (error) => error instanceof AgentThreadsRequestError && error.status === 404;
|
|
50
|
+
const noop = async () => undefined;
|
|
51
|
+
export const useAgentThread = ({ agentId, conversation, isDraft = false, token, headers, baseUrl, }) => {
|
|
52
|
+
const key = agentThreadKey(conversation);
|
|
53
|
+
// Keyed by identity, not by object: a caller handing a fresh `{ chatId, chatThreadId }` each
|
|
54
|
+
// render must not re-acquire the store and refetch each render.
|
|
55
|
+
const target = useMemo(() => ({
|
|
56
|
+
chatId: conversation.chatId,
|
|
57
|
+
chatThreadId: conversation.chatThreadId,
|
|
58
|
+
}), [conversation.chatId, conversation.chatThreadId]);
|
|
59
|
+
const [thread, setThread] = useState(null);
|
|
60
|
+
// The optimistic row after this client's own send, overlaid until the server's row shows the
|
|
61
|
+
// run — the index's rule, applied to a list of one (the same helpers).
|
|
62
|
+
const [optimistic, setOptimistic] = useState(null);
|
|
63
|
+
const [isLoading, setIsLoading] = useState(Boolean(agentId) && !isDraft);
|
|
64
|
+
const [error, setError] = useState(null);
|
|
65
|
+
const fetchJson = useAgentThreadsFetchJson({ baseUrl, token, headers });
|
|
66
|
+
// Read at call time, not a dependency: the draft turning into a thread must not re-acquire
|
|
67
|
+
// the store or re-read — its row comes over the wire.
|
|
68
|
+
const isDraftRef = useRef(isDraft);
|
|
69
|
+
isDraftRef.current = isDraft;
|
|
70
|
+
const fetchRow = useCallback((target) => fetchJson(agentThreadPath(agentId, target)), [agentId, fetchJson]);
|
|
71
|
+
const storeHandleRef = useRef(null);
|
|
72
|
+
const requestSeqRef = useRef(0);
|
|
73
|
+
/** Read the row from the server; a row the server has none for yet reads as null, not an error. A draft reads nothing. */
|
|
74
|
+
const refresh = useCallback(async () => {
|
|
75
|
+
if (!agentId || isDraftRef.current) {
|
|
76
|
+
setIsLoading(false);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const requestSeq = ++requestSeqRef.current;
|
|
80
|
+
setIsLoading(true);
|
|
81
|
+
setError(null);
|
|
82
|
+
try {
|
|
83
|
+
const row = await fetchRow(target);
|
|
84
|
+
if (requestSeqRef.current !== requestSeq) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Through the cache: a live update that landed while the read was in flight is newer
|
|
88
|
+
// than what the read returned, and the cache kept the newer of the two.
|
|
89
|
+
const store = storeHandleRef.current?.store;
|
|
90
|
+
store?.prime([row]);
|
|
91
|
+
setThread(store?.getRow(key) ?? row);
|
|
92
|
+
}
|
|
93
|
+
catch (fetchError) {
|
|
94
|
+
if (requestSeqRef.current !== requestSeq) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (isMissingRowError(fetchError)) {
|
|
98
|
+
setThread(null);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
setError(fetchError instanceof Error ? fetchError : new Error(String(fetchError)));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
if (requestSeqRef.current === requestSeq) {
|
|
106
|
+
setIsLoading(false);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}, [agentId, target, key, fetchRow]);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (!agentId) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const handle = acquireAgentThreadStore(agentId, { fetchThread: fetchRow });
|
|
115
|
+
storeHandleRef.current = handle;
|
|
116
|
+
const { store } = handle;
|
|
117
|
+
setThread(null);
|
|
118
|
+
setOptimistic(null);
|
|
119
|
+
const plan = singleThreadMountPlan(store, key, isDraftRef.current);
|
|
120
|
+
let awaitingIndex = plan.kind === "await-index";
|
|
121
|
+
if (plan.kind === "cached") {
|
|
122
|
+
setThread(plan.thread);
|
|
123
|
+
setIsLoading(false);
|
|
124
|
+
}
|
|
125
|
+
else if (plan.kind === "read") {
|
|
126
|
+
void refresh();
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
setIsLoading(awaitingIndex);
|
|
130
|
+
}
|
|
131
|
+
const unsubscribe = store.subscribe((event) => {
|
|
132
|
+
const outcome = singleThreadEventOutcome(event, store, key, awaitingIndex);
|
|
133
|
+
if (outcome.kind === "row") {
|
|
134
|
+
awaitingIndex = false;
|
|
135
|
+
setThread(outcome.thread);
|
|
136
|
+
setIsLoading(false);
|
|
137
|
+
}
|
|
138
|
+
else if (outcome.kind === "read") {
|
|
139
|
+
awaitingIndex = false;
|
|
140
|
+
void refresh();
|
|
141
|
+
}
|
|
142
|
+
else if (outcome.kind === "removed") {
|
|
143
|
+
// A ghost thread (a run that ended before any turn) is gone; a conversation with turns stays.
|
|
144
|
+
setThread((current) => (current && current.turnCount > 0 ? current : null));
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return () => {
|
|
148
|
+
requestSeqRef.current += 1;
|
|
149
|
+
unsubscribe();
|
|
150
|
+
storeHandleRef.current = null;
|
|
151
|
+
handle.release();
|
|
152
|
+
};
|
|
153
|
+
}, [agentId, key, fetchRow, refresh]);
|
|
154
|
+
const notifyThreadActivity = useCallback((row, prompt) => {
|
|
155
|
+
setOptimistic((previous) => optimisticThreadAfterSend(row, previous ?? undefined, prompt, new Date().toISOString()));
|
|
156
|
+
}, []);
|
|
157
|
+
const renameThread = useCallback(async (target, title) => {
|
|
158
|
+
const trimmed = title.trim();
|
|
159
|
+
if (!trimmed || agentThreadKey(target) !== key) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const previousTitle = thread?.title;
|
|
163
|
+
setThread((current) => (current ? { ...current, title: trimmed } : current));
|
|
164
|
+
try {
|
|
165
|
+
await postAgentThreadRename(fetchJson, agentId, target, trimmed, storeHandleRef.current?.store);
|
|
166
|
+
}
|
|
167
|
+
catch (renameError) {
|
|
168
|
+
setThread((current) => (current ? { ...current, title: previousTitle ?? null } : current));
|
|
169
|
+
throw renameError;
|
|
170
|
+
}
|
|
171
|
+
}, [agentId, fetchJson, key, thread?.title]);
|
|
172
|
+
const threads = useMemo(() => {
|
|
173
|
+
const listed = thread ? [thread] : [];
|
|
174
|
+
if (!optimistic) {
|
|
175
|
+
return listed;
|
|
176
|
+
}
|
|
177
|
+
// Retired for good once the server row shows the run; overlaid until then.
|
|
178
|
+
const remaining = retireOptimisticThreadsTakenOver(new Map([[key, optimistic]]), listed);
|
|
179
|
+
return overlayOptimisticThreads(listed, remaining);
|
|
180
|
+
}, [thread, optimistic, key]);
|
|
181
|
+
return {
|
|
182
|
+
thread: threads[0] ?? null,
|
|
183
|
+
threads,
|
|
184
|
+
notifyThreadActivity,
|
|
185
|
+
renameThread,
|
|
186
|
+
isLoading,
|
|
187
|
+
isLoadingMore: false,
|
|
188
|
+
error,
|
|
189
|
+
hasMore: false,
|
|
190
|
+
loadMore: noop,
|
|
191
|
+
refresh,
|
|
192
|
+
};
|
|
193
|
+
};
|
|
@@ -1,8 +1,40 @@
|
|
|
1
1
|
import { type AgentThreadItem, type AgentThreadSource, type AgentThreadStatus } from "./agent-thread-live-update.js";
|
|
2
|
+
import { type AgentThreadStore } from "./agent-thread-store.js";
|
|
2
3
|
export * from "./agent-thread-live-update.js";
|
|
3
4
|
export { type AgentAdminCaller, BUILD_PERMISSION, isAgentAdmin, readAgentAdminRoles } from "../utils/agent-admin.js";
|
|
4
5
|
export { type AgentConversationChat, type AgentConversationRef, type AgentConversationUrlTarget, agentThreadStandIn, nextRunStaleAt, RUN_STALE_MS, useAgentConversation, type UseAgentConversationOptions, type UseAgentConversationResult, useAgentConversationSelection, type UseAgentConversationSelectionOptions, type UseAgentConversationSelectionResult, } from "./use-agent-conversation.js";
|
|
5
|
-
export { type AgentConversationContextValue, AgentConversationProvider, type AgentConversationProviderProps, type AgentThreadFilters, useAgentConversationContext, } from "./agent-conversation-context.js";
|
|
6
|
+
export { type AgentConversationContextValue, AgentConversationProvider, type AgentConversationProviderProps, AgentThreadConversationProvider, type AgentThreadConversationProviderProps, type AgentThreadFilters, useAgentConversationContext, } from "./agent-conversation-context.js";
|
|
7
|
+
export { useAgentThread, type UseAgentThreadOptions, type UseAgentThreadResult } from "./use-agent-thread.js";
|
|
8
|
+
export { useMediaQuery } from "./use-media-query.js";
|
|
9
|
+
export { type FloatingAgentChatExpandHref, type FloatingAgentChatHandle, type FloatingAgentChatOpenOptions, type FloatingAgentChatOpenRequest, type FloatingAgentChatState, type FloatingAgentChatTarget, floatingAgentChatAfterDraftSent, floatingAgentChatAfterOpen, isFloatingAgentChatDraft, useFloatingAgentChatController, type UseFloatingAgentChatControllerResult, } from "./floating-agent-chat.js";
|
|
10
|
+
export { clampPanelSize, type PanelResizeAxis, type PanelResizeBounds, type PanelResizeHandleProps, type PanelSize, parseStoredPanelSize, useResizablePanel, type UseResizablePanelOptions, type UseResizablePanelResult, } from "./use-resizable-panel.js";
|
|
11
|
+
/** The auth a reader carries — its SDK token and extra headers — read at call time. */
|
|
12
|
+
export interface AgentThreadsAuth {
|
|
13
|
+
token?: string;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
/** A non-2xx answer from the agent-threads API, with its status — a reader tells a 404 (no row) from a failure. */
|
|
17
|
+
export declare class AgentThreadsRequestError extends Error {
|
|
18
|
+
readonly status: number;
|
|
19
|
+
constructor(status: number);
|
|
20
|
+
}
|
|
21
|
+
/** One request against the agent-threads API; every reader (index, single thread) goes through it. */
|
|
22
|
+
export declare const agentThreadsFetchJson: <T>(baseUrl: string | undefined, auth: AgentThreadsAuth, path: string, init?: RequestInit) => Promise<T>;
|
|
23
|
+
/**
|
|
24
|
+
* The request function a reader holds. Its auth is read at call time: a token refresh changes
|
|
25
|
+
* nothing a reader depends on, so it never re-acquires a store or re-reads for it.
|
|
26
|
+
*/
|
|
27
|
+
export declare const useAgentThreadsFetchJson: ({ baseUrl, token, headers, }: AgentThreadsAuth & {
|
|
28
|
+
baseUrl?: string;
|
|
29
|
+
}) => (<T>(path: string, init?: RequestInit) => Promise<T>);
|
|
30
|
+
/** The single-thread read: one row by its (chatId, chatThreadId) identity under the agent. */
|
|
31
|
+
export declare const agentThreadPath: (agentId: string, conversation: Pick<AgentThreadItem, "chatId" | "chatThreadId">) => string;
|
|
32
|
+
/**
|
|
33
|
+
* The rename, as the server applies it. The `x-socket-id` lets the publish skip this tab — the
|
|
34
|
+
* caller already shows the new title optimistically — so, that publish never coming back, the
|
|
35
|
+
* renamed row is handed to the agent's shared store here, for every other reader of it.
|
|
36
|
+
*/
|
|
37
|
+
export declare const postAgentThreadRename: (fetchJson: <T>(path: string, init?: RequestInit) => Promise<T>, agentId: string, thread: Pick<AgentThreadItem, "chatId" | "chatThreadId">, title: string, store?: Pick<AgentThreadStore, "getRow" | "prime">) => Promise<void>;
|
|
6
38
|
export interface AgentThreadsPage {
|
|
7
39
|
items: AgentThreadItem[];
|
|
8
40
|
nextCursor: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent-threads.d.ts","sourceRoot":"","sources":["../../src/hooks/use-agent-threads.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,
|
|
1
|
+
{"version":3,"file":"use-agent-threads.d.ts","sourceRoot":"","sources":["../../src/hooks/use-agent-threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,79 @@
|
|
|
1
1
|
import { getApiHost } from "@blocksdiy/blocks-client-api/envService";
|
|
2
2
|
import { websocketsService } from "@blocksdiy/blocks-client-api/websocketService";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { agentThreadKey, applyAgentThreadUpdate,
|
|
4
|
+
import { agentThreadKey, applyAgentThreadUpdate, matchesAgentThreadFilters, optimisticThreadAfterSend, overlayOptimisticThreads, reconcileWalkWithLive, removeAgentThread, retireOptimisticThreadsTakenOver, } from "./agent-thread-live-update.js";
|
|
5
|
+
import { acquireAgentThreadStore } from "./agent-thread-store.js";
|
|
5
6
|
export * from "./agent-thread-live-update.js";
|
|
6
7
|
export { BUILD_PERMISSION, isAgentAdmin, readAgentAdminRoles } from "../utils/agent-admin.js";
|
|
7
8
|
export { agentThreadStandIn, nextRunStaleAt, RUN_STALE_MS, useAgentConversation, useAgentConversationSelection, } from "./use-agent-conversation.js";
|
|
8
|
-
export { AgentConversationProvider, useAgentConversationContext, } from "./agent-conversation-context.js";
|
|
9
|
-
|
|
9
|
+
export { AgentConversationProvider, AgentThreadConversationProvider, useAgentConversationContext, } from "./agent-conversation-context.js";
|
|
10
|
+
export { useAgentThread } from "./use-agent-thread.js";
|
|
11
|
+
export { useMediaQuery } from "./use-media-query.js";
|
|
12
|
+
export { floatingAgentChatAfterDraftSent, floatingAgentChatAfterOpen, isFloatingAgentChatDraft, useFloatingAgentChatController, } from "./floating-agent-chat.js";
|
|
13
|
+
export { clampPanelSize, parseStoredPanelSize, useResizablePanel, } from "./use-resizable-panel.js";
|
|
14
|
+
/** A non-2xx answer from the agent-threads API, with its status — a reader tells a 404 (no row) from a failure. */
|
|
15
|
+
export class AgentThreadsRequestError extends Error {
|
|
16
|
+
status;
|
|
17
|
+
constructor(status) {
|
|
18
|
+
super(`Agent threads request failed with status ${status}`);
|
|
19
|
+
this.status = status;
|
|
20
|
+
this.name = "AgentThreadsRequestError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** One request against the agent-threads API; every reader (index, single thread) goes through it. */
|
|
24
|
+
export const agentThreadsFetchJson = async (baseUrl, auth, path, init) => {
|
|
25
|
+
const response = await fetch(`${baseUrl ?? getApiHost()}${path}`, {
|
|
26
|
+
...init,
|
|
27
|
+
headers: {
|
|
28
|
+
...(auth.token ? { Authorization: `Bearer ${auth.token}` } : {}),
|
|
29
|
+
...auth.headers,
|
|
30
|
+
...init?.headers,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new AgentThreadsRequestError(response.status);
|
|
35
|
+
}
|
|
36
|
+
return (await response.json());
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The request function a reader holds. Its auth is read at call time: a token refresh changes
|
|
40
|
+
* nothing a reader depends on, so it never re-acquires a store or re-reads for it.
|
|
41
|
+
*/
|
|
42
|
+
export const useAgentThreadsFetchJson = ({ baseUrl, token, headers, }) => {
|
|
43
|
+
const authRef = useRef({ token, headers });
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
authRef.current = { token, headers };
|
|
46
|
+
});
|
|
47
|
+
return useCallback((path, init) => agentThreadsFetchJson(baseUrl, authRef.current, path, init), [baseUrl]);
|
|
48
|
+
};
|
|
49
|
+
/** The single-thread read: one row by its (chatId, chatThreadId) identity under the agent. */
|
|
50
|
+
export const agentThreadPath = (agentId, conversation) => `/agent-threads/${encodeURIComponent(agentId)}/thread?${new URLSearchParams({
|
|
51
|
+
chatId: conversation.chatId,
|
|
52
|
+
chatThreadId: conversation.chatThreadId,
|
|
53
|
+
})}`;
|
|
54
|
+
/**
|
|
55
|
+
* The rename, as the server applies it. The `x-socket-id` lets the publish skip this tab — the
|
|
56
|
+
* caller already shows the new title optimistically — so, that publish never coming back, the
|
|
57
|
+
* renamed row is handed to the agent's shared store here, for every other reader of it.
|
|
58
|
+
*/
|
|
59
|
+
export const postAgentThreadRename = async (fetchJson, agentId, thread, title, store) => {
|
|
60
|
+
const socketId = websocketsService.getSocketId();
|
|
61
|
+
const { updated } = await fetchJson(`/agent-threads/${encodeURIComponent(agentId)}/thread`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
...(socketId ? { "x-socket-id": socketId } : {}),
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify({ chatId: thread.chatId, chatThreadId: thread.chatThreadId, title }),
|
|
68
|
+
});
|
|
69
|
+
if (!updated) {
|
|
70
|
+
throw new Error("Thread rename was not applied");
|
|
71
|
+
}
|
|
72
|
+
const cached = store?.getRow(agentThreadKey(thread));
|
|
73
|
+
if (store && cached) {
|
|
74
|
+
store.prime([{ ...cached, title }]);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
10
77
|
export const AGENT_INBOX_CHAT_PREFIX = "agent-inbox-";
|
|
11
78
|
export const agentInboxChatId = (agentId) => `${AGENT_INBOX_CHAT_PREFIX}${agentId}`;
|
|
12
79
|
export const agentThreadShortLabel = (thread) => {
|
|
@@ -81,26 +148,17 @@ export const useAgentThreads = (options) => {
|
|
|
81
148
|
next.set(agentThreadKey(thread), optimisticThreadAfterSend(thread, prev.get(agentThreadKey(thread)), prompt, new Date().toISOString()));
|
|
82
149
|
applyOptimisticThreads(next);
|
|
83
150
|
}, [applyOptimisticThreads]);
|
|
84
|
-
const
|
|
85
|
-
authRef.current = { token: options.token, headers: options.headers };
|
|
151
|
+
const fetchJson = useAgentThreadsFetchJson({ baseUrl, token: options.token, headers: options.headers });
|
|
86
152
|
const requestSeqRef = useRef(0);
|
|
87
|
-
const fetchJson = useCallback(async (path, init) => {
|
|
88
|
-
const { token, headers } = authRef.current;
|
|
89
|
-
const response = await fetch(`${baseUrl ?? getApiHost()}${path}`, {
|
|
90
|
-
...init,
|
|
91
|
-
headers: {
|
|
92
|
-
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
93
|
-
...headers,
|
|
94
|
-
...init?.headers,
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
if (!response.ok) {
|
|
98
|
-
throw new Error(`Agent threads request failed with status ${response.status}`);
|
|
99
|
-
}
|
|
100
|
-
return (await response.json());
|
|
101
|
-
}, [baseUrl]);
|
|
102
153
|
const fetchPage = useCallback((cursor) => fetchJson(`/agent-threads/${encodeURIComponent(agentId)}${buildQuery({ status, source, chatId, limit, sort, order }, cursor)}`), [agentId, status, source, chatId, limit, sort, order, fetchJson]);
|
|
103
|
-
|
|
154
|
+
// The agent's shared store: one live subscription and one row cache for every reader in the
|
|
155
|
+
// app. This index is its priming reader — its pages land in the cache, so a single-conversation
|
|
156
|
+
// reader beside it (the floating agent chat over a mini-app page) never fetches what the index
|
|
157
|
+
// already has. Acquired per agent; the handlers below read refs, so the handle outlives filter
|
|
158
|
+
// changes.
|
|
159
|
+
const storeHandleRef = useRef(null);
|
|
160
|
+
const liveUpdateOptionsRef = useRef(liveUpdateOptions);
|
|
161
|
+
liveUpdateOptionsRef.current = liveUpdateOptions;
|
|
104
162
|
const renameThread = useCallback(async (thread, title) => {
|
|
105
163
|
const trimmed = title.trim();
|
|
106
164
|
if (!trimmed) {
|
|
@@ -123,18 +181,7 @@ export const useAgentThreads = (options) => {
|
|
|
123
181
|
const previousEntryTitle = optimisticThreadsRef.current.get(key)?.title;
|
|
124
182
|
setLocalTitle(trimmed, trimmed);
|
|
125
183
|
try {
|
|
126
|
-
|
|
127
|
-
const { updated } = await fetchJson(`/agent-threads/${encodeURIComponent(agentId)}/thread`, {
|
|
128
|
-
method: "POST",
|
|
129
|
-
headers: {
|
|
130
|
-
"Content-Type": "application/json",
|
|
131
|
-
...(socketId ? { "x-socket-id": socketId } : {}),
|
|
132
|
-
},
|
|
133
|
-
body: JSON.stringify({ chatId: thread.chatId, chatThreadId: thread.chatThreadId, title: trimmed }),
|
|
134
|
-
});
|
|
135
|
-
if (!updated) {
|
|
136
|
-
throw new Error("Thread rename was not applied");
|
|
137
|
-
}
|
|
184
|
+
await postAgentThreadRename(fetchJson, agentId, thread, trimmed, storeHandleRef.current?.store);
|
|
138
185
|
}
|
|
139
186
|
catch (error) {
|
|
140
187
|
setLocalTitle(previousRowTitle, previousEntryTitle);
|
|
@@ -151,6 +198,7 @@ export const useAgentThreads = (options) => {
|
|
|
151
198
|
const requestSeq = ++requestSeqRef.current;
|
|
152
199
|
liveWhileWalkingRef.current = new Map();
|
|
153
200
|
setIsLoading(true);
|
|
201
|
+
storeHandleRef.current?.setIndexLoading(true);
|
|
154
202
|
setError(null);
|
|
155
203
|
const run = (async () => {
|
|
156
204
|
try {
|
|
@@ -181,6 +229,7 @@ export const useAgentThreads = (options) => {
|
|
|
181
229
|
}
|
|
182
230
|
applyThreads(items);
|
|
183
231
|
applyCursor(cursor);
|
|
232
|
+
storeHandleRef.current?.store.prime(items);
|
|
184
233
|
}
|
|
185
234
|
catch (fetchError) {
|
|
186
235
|
if (requestSeqRef.current !== requestSeq) {
|
|
@@ -191,6 +240,7 @@ export const useAgentThreads = (options) => {
|
|
|
191
240
|
finally {
|
|
192
241
|
if (requestSeqRef.current === requestSeq) {
|
|
193
242
|
setIsLoading(false);
|
|
243
|
+
storeHandleRef.current?.setIndexLoading(false);
|
|
194
244
|
liveWhileWalkingRef.current = null;
|
|
195
245
|
}
|
|
196
246
|
}
|
|
@@ -223,6 +273,7 @@ export const useAgentThreads = (options) => {
|
|
|
223
273
|
}
|
|
224
274
|
applyThreads(appendPage(threadsRef.current, page.items));
|
|
225
275
|
applyCursor(page.nextCursor);
|
|
276
|
+
storeHandleRef.current?.store.prime(page.items);
|
|
226
277
|
}
|
|
227
278
|
catch (fetchError) {
|
|
228
279
|
if (requestSeqRef.current !== requestSeq) {
|
|
@@ -235,69 +286,67 @@ export const useAgentThreads = (options) => {
|
|
|
235
286
|
setIsLoadingMore(false);
|
|
236
287
|
}
|
|
237
288
|
}, [agentId, fetchPage, applyThreads, applyCursor]);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
applyCursor(null);
|
|
241
|
-
applyOptimisticThreads(new Map());
|
|
242
|
-
void loadFirstPage();
|
|
243
|
-
return () => {
|
|
244
|
-
requestSeqRef.current += 1;
|
|
245
|
-
};
|
|
246
|
-
}, [loadFirstPage, applyThreads, applyCursor, applyOptimisticThreads]);
|
|
289
|
+
// The store handle and this index's view of its events. Declared before the load effect below,
|
|
290
|
+
// so the first page's walk finds the handle (effects run in declaration order).
|
|
247
291
|
useEffect(() => {
|
|
248
292
|
if (!agentId) {
|
|
249
293
|
return;
|
|
250
294
|
}
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
liveWhileWalkingRef.current?.set(key, { removed });
|
|
268
|
-
if (removal.threads !== threadsRef.current) {
|
|
269
|
-
applyThreads(removal.threads);
|
|
270
|
-
}
|
|
271
|
-
if (removal.optimistic !== optimisticThreadsRef.current) {
|
|
272
|
-
applyOptimisticThreads(removal.optimistic);
|
|
273
|
-
}
|
|
295
|
+
const handle = acquireAgentThreadStore(agentId, {
|
|
296
|
+
fetchThread: (conversation) => fetchJson(agentThreadPath(agentId, conversation)),
|
|
297
|
+
isIndex: true,
|
|
298
|
+
});
|
|
299
|
+
storeHandleRef.current = handle;
|
|
300
|
+
const onStoreEvent = (event) => {
|
|
301
|
+
if (event.kind === "primed") {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const liveUpdateOptions = liveUpdateOptionsRef.current;
|
|
305
|
+
if (event.kind === "removed") {
|
|
306
|
+
const { removed } = event;
|
|
307
|
+
const removal = removeAgentThread(threadsRef.current, optimisticThreadsRef.current, removed);
|
|
308
|
+
if (!removal) {
|
|
274
309
|
return;
|
|
275
310
|
}
|
|
276
|
-
|
|
277
|
-
if (
|
|
278
|
-
|
|
311
|
+
liveWhileWalkingRef.current?.set(agentThreadKey(removed), { removed });
|
|
312
|
+
if (removal.threads !== threadsRef.current) {
|
|
313
|
+
applyThreads(removal.threads);
|
|
279
314
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const next = new Map(optimisticThreadsRef.current);
|
|
283
|
-
next.delete(key);
|
|
284
|
-
applyOptimisticThreads(next);
|
|
315
|
+
if (removal.optimistic !== optimisticThreadsRef.current) {
|
|
316
|
+
applyOptimisticThreads(removal.optimistic);
|
|
285
317
|
}
|
|
286
|
-
|
|
287
|
-
applyThreads(applyAgentThreadUpdate(threadsRef.current, incoming, {
|
|
288
|
-
...liveUpdateOptions,
|
|
289
|
-
hasMore: nextCursorRef.current !== null,
|
|
290
|
-
}));
|
|
318
|
+
return;
|
|
291
319
|
}
|
|
292
|
-
|
|
293
|
-
|
|
320
|
+
const incoming = event.thread;
|
|
321
|
+
const key = agentThreadKey(incoming);
|
|
322
|
+
const entry = optimisticThreadsRef.current.get(key);
|
|
323
|
+
if (entry && !matchesAgentThreadFilters(incoming, liveUpdateOptions) && incoming.turnCount >= entry.turnCount) {
|
|
324
|
+
const next = new Map(optimisticThreadsRef.current);
|
|
325
|
+
next.delete(key);
|
|
326
|
+
applyOptimisticThreads(next);
|
|
294
327
|
}
|
|
328
|
+
liveWhileWalkingRef.current?.set(key, incoming);
|
|
329
|
+
applyThreads(applyAgentThreadUpdate(threadsRef.current, incoming, {
|
|
330
|
+
...liveUpdateOptions,
|
|
331
|
+
hasMore: nextCursorRef.current !== null,
|
|
332
|
+
}));
|
|
295
333
|
};
|
|
296
|
-
|
|
334
|
+
const unsubscribe = handle.store.subscribe(onStoreEvent);
|
|
297
335
|
return () => {
|
|
298
|
-
|
|
336
|
+
unsubscribe();
|
|
337
|
+
storeHandleRef.current = null;
|
|
338
|
+
handle.release();
|
|
299
339
|
};
|
|
300
|
-
}, [agentId,
|
|
340
|
+
}, [agentId, fetchJson, applyThreads, applyOptimisticThreads]);
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
applyThreads([]);
|
|
343
|
+
applyCursor(null);
|
|
344
|
+
applyOptimisticThreads(new Map());
|
|
345
|
+
void loadFirstPage();
|
|
346
|
+
return () => {
|
|
347
|
+
requestSeqRef.current += 1;
|
|
348
|
+
};
|
|
349
|
+
}, [loadFirstPage, applyThreads, applyCursor, applyOptimisticThreads]);
|
|
301
350
|
// Realtime has no replay — reconnect re-walks; last socket id lives in a ref so a filter switch while offline does not look like a first connect.
|
|
302
351
|
const lastSocketIdRef = useRef(undefined);
|
|
303
352
|
useEffect(() => websocketsService.onSocketIdChange((socketId) => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-media-query.d.ts","sourceRoot":"","sources":["../../src/hooks/use-media-query.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,OAc7C,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// A media query's current match, subscribed — for layout decisions that CSS alone cannot make
|
|
2
|
+
// (which surface a pick opens, not how it looks). False where there is no window.
|
|
3
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
4
|
+
export const useMediaQuery = (query) => {
|
|
5
|
+
const subscribe = useCallback((onChange) => {
|
|
6
|
+
const list = window.matchMedia(query);
|
|
7
|
+
list.addEventListener("change", onChange);
|
|
8
|
+
return () => list.removeEventListener("change", onChange);
|
|
9
|
+
}, [query]);
|
|
10
|
+
return useSyncExternalStore(subscribe, () => window.matchMedia(query).matches, () => false);
|
|
11
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export interface PanelSize {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
}
|
|
5
|
+
/** Which dimension a handle resizes: the top edge the height, the end edge the width, the corner both. */
|
|
6
|
+
export type PanelResizeAxis = "width" | "height" | "both";
|
|
7
|
+
export interface PanelResizeBounds {
|
|
8
|
+
/** Pixel floor: the smallest usable panel. */
|
|
9
|
+
minSize: PanelSize;
|
|
10
|
+
/** The gap the panel keeps from the viewport's edges, in px. */
|
|
11
|
+
inset: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A size the panel can take inside `viewport`: never below the floor, never past the viewport
|
|
15
|
+
* minus the inset on both sides — unless the viewport itself is smaller than the floor, where
|
|
16
|
+
* the floor wins (CSS still caps the box to the viewport).
|
|
17
|
+
*/
|
|
18
|
+
export declare const clampPanelSize: (size: PanelSize, viewport: PanelSize, { minSize, inset }: PanelResizeBounds) => PanelSize;
|
|
19
|
+
/** A stored size, or null for anything but two finite positive numbers. */
|
|
20
|
+
export declare const parseStoredPanelSize: (raw: string | null | undefined) => PanelSize | null;
|
|
21
|
+
interface ResizeDrag {
|
|
22
|
+
pointerId: number;
|
|
23
|
+
axis: PanelResizeAxis;
|
|
24
|
+
startX: number;
|
|
25
|
+
startY: number;
|
|
26
|
+
startSize: PanelSize;
|
|
27
|
+
viewport: PanelSize;
|
|
28
|
+
isRtl: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The size a drag has reached. The panel is anchored at the viewport's bottom-start corner, so
|
|
32
|
+
* growth is toward the end edge and upward — the horizontal delta flips in RTL, and a drag up
|
|
33
|
+
* counts as more height.
|
|
34
|
+
*/
|
|
35
|
+
export declare const panelSizeFromDrag: (drag: ResizeDrag, pointer: {
|
|
36
|
+
clientX: number;
|
|
37
|
+
clientY: number;
|
|
38
|
+
}, bounds: PanelResizeBounds) => PanelSize;
|
|
39
|
+
/** The key's step for a handle: toward the end edge and up grow; null for a key the axis ignores. */
|
|
40
|
+
export declare const panelKeyboardDelta: (key: string, axis: PanelResizeAxis, step: number, isRtl: boolean) => {
|
|
41
|
+
dx: number;
|
|
42
|
+
dy: number;
|
|
43
|
+
} | null;
|
|
44
|
+
export interface UseResizablePanelOptions extends PanelResizeBounds {
|
|
45
|
+
/** The size before anyone resizes. */
|
|
46
|
+
defaultSize: PanelSize;
|
|
47
|
+
/** Where the size is remembered (localStorage); omit for a size that forgets on unmount. */
|
|
48
|
+
storageKey?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface PanelResizeHandleProps {
|
|
51
|
+
onPointerDown: (event: React.PointerEvent<HTMLElement>) => void;
|
|
52
|
+
onPointerMove: (event: React.PointerEvent<HTMLElement>) => void;
|
|
53
|
+
onPointerUp: (event: React.PointerEvent<HTMLElement>) => void;
|
|
54
|
+
onPointerCancel: () => void;
|
|
55
|
+
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
|
|
56
|
+
}
|
|
57
|
+
export interface UseResizablePanelResult {
|
|
58
|
+
size: PanelSize;
|
|
59
|
+
/** A pointer drag is in progress (the component disables text selection meanwhile). */
|
|
60
|
+
isResizing: boolean;
|
|
61
|
+
/** The handlers for a handle that resizes `axis`; spread them on the handle element. */
|
|
62
|
+
getHandleProps: (axis: PanelResizeAxis) => PanelResizeHandleProps;
|
|
63
|
+
}
|
|
64
|
+
export declare const useResizablePanel: (panelRef: React.RefObject<HTMLElement | null>, { defaultSize, minSize, inset, storageKey }: UseResizablePanelOptions) => UseResizablePanelResult;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=use-resizable-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-resizable-panel.d.ts","sourceRoot":"","sources":["../../src/hooks/use-resizable-panel.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,0GAA0G;AAC1G,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE1D,MAAM,WAAW,iBAAiB;IAChC,8CAA8C;IAC9C,OAAO,EAAE,SAAS,CAAC;IACnB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;CACf;AAID;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,MAAM,SAAS,EACf,UAAU,SAAS,EACnB,oBAAoB,iBAAiB,KACpC,SAOF,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,SAAS,GAAG,IAwBjF,CAAC;AAEF,UAAU,UAAU;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,UAAU,EAChB,SAAS;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAC7C,QAAQ,iBAAiB,KACxB,SAWF,CAAC;AAEF,qGAAqG;AACrG,eAAO,MAAM,kBAAkB,GAC7B,KAAK,MAAM,EACX,MAAM,eAAe,EACrB,MAAM,MAAM,EACZ,OAAO,OAAO,KACb;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAgB/B,CAAC;AAqCF,MAAM,WAAW,wBAAyB,SAAQ,iBAAiB;IACjE,sCAAsC;IACtC,WAAW,EAAE,SAAS,CAAC;IACvB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAChE,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAChE,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAC9D,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,SAAS,CAAC;IAChB,uFAAuF;IACvF,UAAU,EAAE,OAAO,CAAC;IACpB,wFAAwF;IACxF,cAAc,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,sBAAsB,CAAC;CACnE;AAED,eAAO,MAAM,iBAAiB,GAC5B,UAAU,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAC7C,6CAA6C,wBAAwB,KACpE,uBAiFF,CAAC"}
|