@tangle-network/sandbox-ui 0.2.2 → 0.3.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/README.md +201 -10
- package/dist/auth.js +2 -2
- package/dist/chat-container-C8eHLw8z.d.ts +67 -0
- package/dist/chat.d.ts +70 -78
- package/dist/chat.js +8 -8
- package/dist/chunk-4F2GJRGU.js +756 -0
- package/dist/{chunk-HYEAX3DC.js → chunk-5LV6DZZF.js} +445 -114
- package/dist/chunk-67C53XVV.js +1106 -0
- package/dist/{chunk-QSQBDR3N.js → chunk-BX6AQMUS.js} +5 -2
- package/dist/chunk-CCKNIAS7.js +124 -0
- package/dist/chunk-CJ2RYVZH.js +128 -0
- package/dist/{chunk-KMXV7DDX.js → chunk-CNWVHQFY.js} +1 -1
- package/dist/{chunk-OU4TRNQZ.js → chunk-COCSO7FG.js} +3 -3
- package/dist/chunk-FJSVPBKY.js +85 -0
- package/dist/chunk-FRGMMANX.js +102 -0
- package/dist/{chunk-E6FS7R4X.js → chunk-HWLX5NME.js} +1 -1
- package/dist/chunk-JF6E2DS5.js +610 -0
- package/dist/chunk-MUOL44AE.js +121 -0
- package/dist/chunk-MXCSSOGH.js +105 -0
- package/dist/{chunk-J4OADEUK.js → chunk-OM6ON27W.js} +24 -9
- package/dist/{chunk-NI2EI43H.js → chunk-PDV7W4NY.js} +9 -124
- package/dist/chunk-TQN3VR4F.js +92 -0
- package/dist/{chunk-SOT2V7TX.js → chunk-TXI4MZAZ.js} +62 -144
- package/dist/chunk-WUR652Y3.js +1140 -0
- package/dist/chunk-YDBXQQLC.js +336 -0
- package/dist/{chunk-4EIWPJMJ.js → chunk-ZP6GSX4D.js} +36 -27
- package/dist/dashboard.d.ts +5 -2
- package/dist/dashboard.js +5 -4
- package/dist/{expanded-tool-detail-OkXGqTHe.d.ts → expanded-tool-detail-BDi_h_dZ.d.ts} +11 -4
- package/dist/file-tabs-CmaoDVBI.d.ts +72 -0
- package/dist/files.d.ts +25 -44
- package/dist/files.js +8 -3
- package/{src/styles → dist}/globals.css +16 -67
- package/dist/hooks.d.ts +5 -4
- package/dist/hooks.js +14 -9
- package/dist/index.d.ts +38 -9
- package/dist/index.js +100 -126
- package/dist/markdown.d.ts +1 -24
- package/dist/markdown.js +1 -7
- package/dist/openui.d.ts +115 -0
- package/dist/openui.js +11 -0
- package/dist/pages.d.ts +3 -2
- package/dist/pages.js +19 -16
- package/dist/primitives.js +25 -19
- package/dist/run.d.ts +2 -2
- package/dist/run.js +8 -7
- package/dist/{use-sidecar-auth-Bb0-w3lX.d.ts → sdk-hooks.d.ts} +61 -72
- package/dist/sdk-hooks.js +29 -0
- package/dist/styles.css +179 -0
- package/dist/tokens.css +165 -0
- package/dist/{tool-display-BvsVW_Ur.d.ts → tool-display-Ct9nFAzJ.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/{usage-chart-DINgSVL5.d.ts → usage-chart-CY9xo3KX.d.ts} +8 -3
- package/dist/use-pty-session-DeZSxOCN.d.ts +69 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/dist/workspace.d.ts +171 -33
- package/dist/workspace.js +25 -1
- package/package.json +10 -3
- package/dist/chunk-2UHPE5T7.js +0 -201
- package/dist/chunk-6MQIDUPA.js +0 -502
- package/dist/chunk-KYY2X6LY.js +0 -318
- package/dist/chunk-L6ZDH5F4.js +0 -334
- package/dist/chunk-M34OA6PQ.js +0 -233
- package/dist/chunk-M6VLC32S.js +0 -219
- package/dist/chunk-U62G5TS7.js +0 -472
- package/src/styles/tokens.css +0 -73
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseToolEvent
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CCKNIAS7.js";
|
|
4
4
|
|
|
5
5
|
// src/hooks/use-tool-call-stream.ts
|
|
6
6
|
import { useState, useCallback, useRef } from "react";
|
|
@@ -66,88 +66,8 @@ function useToolCallStream() {
|
|
|
66
66
|
return { segments, pushEvent, pushText, completeToolCall, reset };
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
// src/hooks/use-auth.ts
|
|
70
|
-
import * as React from "react";
|
|
71
|
-
function useAuth({
|
|
72
|
-
apiBaseUrl,
|
|
73
|
-
revalidateOnFocus = false,
|
|
74
|
-
shouldRetryOnError = false
|
|
75
|
-
}) {
|
|
76
|
-
const [user, setUser] = React.useState(null);
|
|
77
|
-
const [isLoading, setIsLoading] = React.useState(true);
|
|
78
|
-
const [error, setError] = React.useState(null);
|
|
79
|
-
const fetchSession = React.useCallback(async () => {
|
|
80
|
-
setIsLoading(true);
|
|
81
|
-
setError(null);
|
|
82
|
-
try {
|
|
83
|
-
const res = await fetch(`${apiBaseUrl}/auth/session`, {
|
|
84
|
-
credentials: "include"
|
|
85
|
-
});
|
|
86
|
-
if (!res.ok) {
|
|
87
|
-
throw new Error("Not authenticated");
|
|
88
|
-
}
|
|
89
|
-
const data = await res.json();
|
|
90
|
-
if (data.success && data.data) {
|
|
91
|
-
setUser(data.data);
|
|
92
|
-
} else {
|
|
93
|
-
setUser(null);
|
|
94
|
-
}
|
|
95
|
-
} catch (err) {
|
|
96
|
-
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
97
|
-
setUser(null);
|
|
98
|
-
if (shouldRetryOnError) {
|
|
99
|
-
setTimeout(fetchSession, 5e3);
|
|
100
|
-
}
|
|
101
|
-
} finally {
|
|
102
|
-
setIsLoading(false);
|
|
103
|
-
}
|
|
104
|
-
}, [apiBaseUrl, shouldRetryOnError]);
|
|
105
|
-
React.useEffect(() => {
|
|
106
|
-
fetchSession();
|
|
107
|
-
}, [fetchSession]);
|
|
108
|
-
React.useEffect(() => {
|
|
109
|
-
if (!revalidateOnFocus) return;
|
|
110
|
-
const handleFocus = () => {
|
|
111
|
-
fetchSession();
|
|
112
|
-
};
|
|
113
|
-
window.addEventListener("focus", handleFocus);
|
|
114
|
-
return () => window.removeEventListener("focus", handleFocus);
|
|
115
|
-
}, [revalidateOnFocus, fetchSession]);
|
|
116
|
-
return {
|
|
117
|
-
user,
|
|
118
|
-
isLoading,
|
|
119
|
-
isError: !!error,
|
|
120
|
-
error,
|
|
121
|
-
mutate: fetchSession
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
function createAuthFetcher(_apiBaseUrl) {
|
|
125
|
-
return async function authFetcher(url, options) {
|
|
126
|
-
const res = await fetch(url, {
|
|
127
|
-
...options,
|
|
128
|
-
credentials: "include",
|
|
129
|
-
headers: {
|
|
130
|
-
...options?.headers
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
if (!res.ok) {
|
|
134
|
-
throw new Error(`Request failed with status ${res.status}`);
|
|
135
|
-
}
|
|
136
|
-
return res.json();
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function useApiKey() {
|
|
140
|
-
const [apiKey, setApiKey] = React.useState(null);
|
|
141
|
-
React.useEffect(() => {
|
|
142
|
-
if (typeof window !== "undefined") {
|
|
143
|
-
setApiKey(localStorage.getItem("apiKey"));
|
|
144
|
-
}
|
|
145
|
-
}, []);
|
|
146
|
-
return apiKey;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
69
|
// src/hooks/use-sse-stream.ts
|
|
150
|
-
import * as
|
|
70
|
+
import * as React from "react";
|
|
151
71
|
function useSSEStream(options) {
|
|
152
72
|
const {
|
|
153
73
|
url,
|
|
@@ -162,27 +82,27 @@ function useSSEStream(options) {
|
|
|
162
82
|
headers,
|
|
163
83
|
enabled = true
|
|
164
84
|
} = options;
|
|
165
|
-
const [state, setState] =
|
|
166
|
-
const [events, setEvents] =
|
|
167
|
-
const [lastEvent, setLastEvent] =
|
|
168
|
-
const [error, setError] =
|
|
169
|
-
const [retryCount, setRetryCount] =
|
|
170
|
-
const [lastEventTime, setLastEventTime] =
|
|
171
|
-
const [timeSinceLastEvent, setTimeSinceLastEvent] =
|
|
172
|
-
const eventSourceRef =
|
|
173
|
-
const abortControllerRef =
|
|
174
|
-
const reconnectTimeoutRef =
|
|
175
|
-
const lastEventIdRef =
|
|
176
|
-
|
|
85
|
+
const [state, setState] = React.useState("disconnected");
|
|
86
|
+
const [events, setEvents] = React.useState([]);
|
|
87
|
+
const [lastEvent, setLastEvent] = React.useState(null);
|
|
88
|
+
const [error, setError] = React.useState(null);
|
|
89
|
+
const [retryCount, setRetryCount] = React.useState(0);
|
|
90
|
+
const [lastEventTime, setLastEventTime] = React.useState(Date.now());
|
|
91
|
+
const [timeSinceLastEvent, setTimeSinceLastEvent] = React.useState(0);
|
|
92
|
+
const eventSourceRef = React.useRef(null);
|
|
93
|
+
const abortControllerRef = React.useRef(null);
|
|
94
|
+
const reconnectTimeoutRef = React.useRef(null);
|
|
95
|
+
const lastEventIdRef = React.useRef(void 0);
|
|
96
|
+
React.useEffect(() => {
|
|
177
97
|
const interval = setInterval(() => {
|
|
178
98
|
setTimeSinceLastEvent(Date.now() - lastEventTime);
|
|
179
99
|
}, 1e3);
|
|
180
100
|
return () => clearInterval(interval);
|
|
181
101
|
}, [lastEventTime]);
|
|
182
|
-
|
|
102
|
+
React.useEffect(() => {
|
|
183
103
|
onStateChange?.(state);
|
|
184
104
|
}, [state, onStateChange]);
|
|
185
|
-
const handleEvent =
|
|
105
|
+
const handleEvent = React.useCallback(
|
|
186
106
|
(eventType, data, id) => {
|
|
187
107
|
const event = {
|
|
188
108
|
id,
|
|
@@ -200,7 +120,7 @@ function useSSEStream(options) {
|
|
|
200
120
|
},
|
|
201
121
|
[onEvent]
|
|
202
122
|
);
|
|
203
|
-
const disconnect =
|
|
123
|
+
const disconnect = React.useCallback(() => {
|
|
204
124
|
if (reconnectTimeoutRef.current) {
|
|
205
125
|
clearTimeout(reconnectTimeoutRef.current);
|
|
206
126
|
reconnectTimeoutRef.current = null;
|
|
@@ -215,7 +135,7 @@ function useSSEStream(options) {
|
|
|
215
135
|
}
|
|
216
136
|
setState("disconnected");
|
|
217
137
|
}, []);
|
|
218
|
-
const connect =
|
|
138
|
+
const connect = React.useCallback(() => {
|
|
219
139
|
disconnect();
|
|
220
140
|
if (!url || !enabled) {
|
|
221
141
|
return;
|
|
@@ -388,13 +308,13 @@ function useSSEStream(options) {
|
|
|
388
308
|
onError,
|
|
389
309
|
disconnect
|
|
390
310
|
]);
|
|
391
|
-
|
|
311
|
+
React.useEffect(() => {
|
|
392
312
|
if (enabled) {
|
|
393
313
|
connect();
|
|
394
314
|
}
|
|
395
315
|
return () => disconnect();
|
|
396
316
|
}, [enabled, connect, disconnect]);
|
|
397
|
-
const clearEvents =
|
|
317
|
+
const clearEvents = React.useCallback(() => {
|
|
398
318
|
setEvents([]);
|
|
399
319
|
setLastEvent(null);
|
|
400
320
|
}, []);
|
|
@@ -411,8 +331,421 @@ function useSSEStream(options) {
|
|
|
411
331
|
};
|
|
412
332
|
}
|
|
413
333
|
|
|
334
|
+
// src/hooks/use-sdk-session.ts
|
|
335
|
+
import { useCallback as useCallback3, useMemo, useRef as useRef3, useState as useState3 } from "react";
|
|
336
|
+
function uid() {
|
|
337
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
338
|
+
return crypto.randomUUID();
|
|
339
|
+
}
|
|
340
|
+
return `sdk-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
341
|
+
}
|
|
342
|
+
function toMillis(value) {
|
|
343
|
+
if (value == null) return void 0;
|
|
344
|
+
if (typeof value === "number") return value;
|
|
345
|
+
if (value instanceof Date) return value.getTime();
|
|
346
|
+
const millis = new Date(value).getTime();
|
|
347
|
+
return Number.isFinite(millis) ? millis : void 0;
|
|
348
|
+
}
|
|
349
|
+
function asRecord(value) {
|
|
350
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
351
|
+
}
|
|
352
|
+
function asString(value) {
|
|
353
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
354
|
+
}
|
|
355
|
+
function textPartsFromContent(content, attachments) {
|
|
356
|
+
const attachmentText = attachments?.length ? `
|
|
357
|
+
|
|
358
|
+
Attachments:
|
|
359
|
+
${attachments.map((attachment) => `- ${attachment.name}`).join("\n")}` : "";
|
|
360
|
+
const text = `${content}${attachmentText}`.trim();
|
|
361
|
+
return text ? [{ type: "text", text }] : [];
|
|
362
|
+
}
|
|
363
|
+
function normalizeTime(value) {
|
|
364
|
+
const record = asRecord(value);
|
|
365
|
+
if (!record) return void 0;
|
|
366
|
+
const start = Number(record.start ?? record.startedAt ?? record.started_at);
|
|
367
|
+
const end = Number(record.end ?? record.completedAt ?? record.completed_at);
|
|
368
|
+
if (!Number.isFinite(start) && !Number.isFinite(end)) {
|
|
369
|
+
return void 0;
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
start: Number.isFinite(start) ? start : void 0,
|
|
373
|
+
end: Number.isFinite(end) ? end : void 0
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function normalizeStatus(value, output, error) {
|
|
377
|
+
if (value === "pending" || value === "running" || value === "completed" || value === "error") {
|
|
378
|
+
return value;
|
|
379
|
+
}
|
|
380
|
+
if (typeof error === "string" && error.length > 0) {
|
|
381
|
+
return "error";
|
|
382
|
+
}
|
|
383
|
+
if (output !== void 0) {
|
|
384
|
+
return "completed";
|
|
385
|
+
}
|
|
386
|
+
return "running";
|
|
387
|
+
}
|
|
388
|
+
function resolveToolIdentity(rawPart) {
|
|
389
|
+
return String(
|
|
390
|
+
rawPart.id ?? rawPart.callID ?? rawPart.callId ?? rawPart.toolUseId ?? rawPart.toolCallId ?? rawPart.tool ?? rawPart.name ?? "tool"
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
function normalizePart(rawPart) {
|
|
394
|
+
const type = String(rawPart.type ?? "");
|
|
395
|
+
if (type === "text") {
|
|
396
|
+
return {
|
|
397
|
+
type: "text",
|
|
398
|
+
text: asString(rawPart.text) ?? asString(rawPart.content) ?? ""
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (type === "reasoning") {
|
|
402
|
+
return {
|
|
403
|
+
type: "reasoning",
|
|
404
|
+
text: asString(rawPart.text) ?? asString(rawPart.content) ?? "",
|
|
405
|
+
time: normalizeTime(rawPart.time)
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
if (type === "tool") {
|
|
409
|
+
const stateRecord = asRecord(rawPart.state);
|
|
410
|
+
const input = stateRecord?.input ?? rawPart.input;
|
|
411
|
+
const output = stateRecord?.output ?? rawPart.output;
|
|
412
|
+
const error = stateRecord?.error ?? rawPart.error;
|
|
413
|
+
return {
|
|
414
|
+
type: "tool",
|
|
415
|
+
id: resolveToolIdentity(rawPart),
|
|
416
|
+
tool: String(rawPart.tool ?? rawPart.name ?? "tool"),
|
|
417
|
+
callID: rawPart.callID != null || rawPart.callId != null ? String(rawPart.callID ?? rawPart.callId) : void 0,
|
|
418
|
+
state: {
|
|
419
|
+
status: normalizeStatus(stateRecord?.status ?? rawPart.status, output, error),
|
|
420
|
+
input,
|
|
421
|
+
output,
|
|
422
|
+
error: typeof error === "string" ? error : void 0,
|
|
423
|
+
metadata: asRecord(stateRecord?.metadata) ?? asRecord(rawPart.metadata),
|
|
424
|
+
time: normalizeTime(stateRecord?.time ?? rawPart.time)
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
function getPartKey(rawPart) {
|
|
431
|
+
const type = String(rawPart.type ?? "unknown");
|
|
432
|
+
if (type === "tool") {
|
|
433
|
+
return `tool:${resolveToolIdentity(rawPart)}`;
|
|
434
|
+
}
|
|
435
|
+
if (type === "reasoning") {
|
|
436
|
+
return `reasoning:${String(rawPart.id ?? rawPart.partId ?? rawPart.index ?? "current")}`;
|
|
437
|
+
}
|
|
438
|
+
return `text:${String(rawPart.id ?? rawPart.partId ?? rawPart.index ?? "current")}`;
|
|
439
|
+
}
|
|
440
|
+
function mergePart(existing, incoming, delta) {
|
|
441
|
+
if (!existing) {
|
|
442
|
+
if (incoming.type === "text" && delta) {
|
|
443
|
+
return { type: "text", text: delta };
|
|
444
|
+
}
|
|
445
|
+
return incoming;
|
|
446
|
+
}
|
|
447
|
+
if (existing.type === "text" && incoming.type === "text") {
|
|
448
|
+
return {
|
|
449
|
+
type: "text",
|
|
450
|
+
text: delta ? `${existing.text}${delta}` : incoming.text,
|
|
451
|
+
synthetic: incoming.synthetic ?? existing.synthetic
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (existing.type === "reasoning" && incoming.type === "reasoning") {
|
|
455
|
+
return {
|
|
456
|
+
...existing,
|
|
457
|
+
...incoming,
|
|
458
|
+
text: delta && incoming.text === existing.text ? `${existing.text}${delta}` : incoming.text || existing.text,
|
|
459
|
+
time: incoming.time ?? existing.time
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (existing.type === "tool" && incoming.type === "tool") {
|
|
463
|
+
return {
|
|
464
|
+
...existing,
|
|
465
|
+
...incoming,
|
|
466
|
+
state: {
|
|
467
|
+
...existing.state,
|
|
468
|
+
...incoming.state,
|
|
469
|
+
time: incoming.state.time ?? existing.state.time
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
return incoming;
|
|
474
|
+
}
|
|
475
|
+
function mapSeeds(messages) {
|
|
476
|
+
return {
|
|
477
|
+
messages: messages.map((message, index) => ({
|
|
478
|
+
id: message.id,
|
|
479
|
+
role: message.role,
|
|
480
|
+
_insertionIndex: index,
|
|
481
|
+
time: {
|
|
482
|
+
created: toMillis(message.createdAt) ?? Date.now()
|
|
483
|
+
}
|
|
484
|
+
})),
|
|
485
|
+
partMap: Object.fromEntries(
|
|
486
|
+
messages.map((message) => [
|
|
487
|
+
message.id,
|
|
488
|
+
message.parts ?? textPartsFromContent(message.content ?? "", message.attachments)
|
|
489
|
+
])
|
|
490
|
+
)
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function useSdkSession({
|
|
494
|
+
initialMessages = []
|
|
495
|
+
} = {}) {
|
|
496
|
+
const initialConversation = useMemo(
|
|
497
|
+
() => mapSeeds(initialMessages),
|
|
498
|
+
[initialMessages]
|
|
499
|
+
);
|
|
500
|
+
const [conversation, setConversation] = useState3(initialConversation);
|
|
501
|
+
const [isStreaming, setIsStreaming] = useState3(false);
|
|
502
|
+
const activeAssistantIdRef = useRef3(null);
|
|
503
|
+
const insertionIndexRef = useRef3(initialConversation.messages.length);
|
|
504
|
+
const partIndexRef = useRef3({});
|
|
505
|
+
const replaceHistory = useCallback3((messages) => {
|
|
506
|
+
const next = mapSeeds(messages);
|
|
507
|
+
setConversation(next);
|
|
508
|
+
setIsStreaming(false);
|
|
509
|
+
activeAssistantIdRef.current = null;
|
|
510
|
+
insertionIndexRef.current = next.messages.length;
|
|
511
|
+
partIndexRef.current = {};
|
|
512
|
+
}, []);
|
|
513
|
+
const appendUserMessage = useCallback3(
|
|
514
|
+
({
|
|
515
|
+
id = uid(),
|
|
516
|
+
role = "user",
|
|
517
|
+
content,
|
|
518
|
+
createdAt,
|
|
519
|
+
attachments
|
|
520
|
+
}) => {
|
|
521
|
+
setConversation((prev) => ({
|
|
522
|
+
messages: [
|
|
523
|
+
...prev.messages,
|
|
524
|
+
{
|
|
525
|
+
id,
|
|
526
|
+
role,
|
|
527
|
+
_insertionIndex: insertionIndexRef.current++,
|
|
528
|
+
time: {
|
|
529
|
+
created: toMillis(createdAt) ?? Date.now()
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
],
|
|
533
|
+
partMap: {
|
|
534
|
+
...prev.partMap,
|
|
535
|
+
[id]: textPartsFromContent(content, attachments)
|
|
536
|
+
}
|
|
537
|
+
}));
|
|
538
|
+
return id;
|
|
539
|
+
},
|
|
540
|
+
[]
|
|
541
|
+
);
|
|
542
|
+
const beginAssistantMessage = useCallback3(
|
|
543
|
+
({
|
|
544
|
+
id = uid(),
|
|
545
|
+
role = "assistant",
|
|
546
|
+
createdAt
|
|
547
|
+
} = {}) => {
|
|
548
|
+
setConversation((prev) => ({
|
|
549
|
+
messages: [
|
|
550
|
+
...prev.messages,
|
|
551
|
+
{
|
|
552
|
+
id,
|
|
553
|
+
role,
|
|
554
|
+
_insertionIndex: insertionIndexRef.current++,
|
|
555
|
+
time: {
|
|
556
|
+
created: toMillis(createdAt) ?? Date.now()
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
],
|
|
560
|
+
partMap: {
|
|
561
|
+
...prev.partMap,
|
|
562
|
+
[id]: prev.partMap[id] ?? []
|
|
563
|
+
}
|
|
564
|
+
}));
|
|
565
|
+
activeAssistantIdRef.current = id;
|
|
566
|
+
partIndexRef.current[id] = partIndexRef.current[id] ?? {};
|
|
567
|
+
setIsStreaming(true);
|
|
568
|
+
return id;
|
|
569
|
+
},
|
|
570
|
+
[]
|
|
571
|
+
);
|
|
572
|
+
const completeAssistantMessage = useCallback3(
|
|
573
|
+
({ messageId, finalText } = {}) => {
|
|
574
|
+
const targetId = messageId ?? activeAssistantIdRef.current;
|
|
575
|
+
if (!targetId) {
|
|
576
|
+
setIsStreaming(false);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (finalText) {
|
|
580
|
+
setConversation((prev) => {
|
|
581
|
+
const existingParts = prev.partMap[targetId] ?? [];
|
|
582
|
+
const nextParts = [...existingParts];
|
|
583
|
+
const textIndex = nextParts.findIndex((part) => part.type === "text");
|
|
584
|
+
if (textIndex === -1) {
|
|
585
|
+
nextParts.push({ type: "text", text: finalText });
|
|
586
|
+
} else {
|
|
587
|
+
nextParts[textIndex] = { type: "text", text: finalText };
|
|
588
|
+
}
|
|
589
|
+
return {
|
|
590
|
+
...prev,
|
|
591
|
+
partMap: {
|
|
592
|
+
...prev.partMap,
|
|
593
|
+
[targetId]: nextParts
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
delete partIndexRef.current[targetId];
|
|
599
|
+
if (activeAssistantIdRef.current === targetId) {
|
|
600
|
+
activeAssistantIdRef.current = null;
|
|
601
|
+
}
|
|
602
|
+
setIsStreaming(false);
|
|
603
|
+
},
|
|
604
|
+
[]
|
|
605
|
+
);
|
|
606
|
+
const failAssistantMessage = useCallback3(
|
|
607
|
+
(error, options) => {
|
|
608
|
+
const targetId = options?.messageId ?? activeAssistantIdRef.current;
|
|
609
|
+
if (!targetId) {
|
|
610
|
+
setIsStreaming(false);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
setConversation((prev) => ({
|
|
614
|
+
...prev,
|
|
615
|
+
partMap: {
|
|
616
|
+
...prev.partMap,
|
|
617
|
+
[targetId]: [{ type: "text", text: `Error: ${error}` }]
|
|
618
|
+
}
|
|
619
|
+
}));
|
|
620
|
+
delete partIndexRef.current[targetId];
|
|
621
|
+
if (activeAssistantIdRef.current === targetId) {
|
|
622
|
+
activeAssistantIdRef.current = null;
|
|
623
|
+
}
|
|
624
|
+
setIsStreaming(false);
|
|
625
|
+
},
|
|
626
|
+
[]
|
|
627
|
+
);
|
|
628
|
+
const applySdkEvent = useCallback3(
|
|
629
|
+
(event, options) => {
|
|
630
|
+
const eventData = asRecord(event.data) ?? {};
|
|
631
|
+
if (event.type === "message.updated") {
|
|
632
|
+
const id = asString(eventData.id) ?? asString(eventData.messageId) ?? options?.messageId;
|
|
633
|
+
const role = asString(eventData.role) ?? "assistant";
|
|
634
|
+
if (!id) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
setConversation((prev) => {
|
|
638
|
+
if (prev.messages.some((message) => message.id === id)) {
|
|
639
|
+
return prev;
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
...prev,
|
|
643
|
+
messages: [
|
|
644
|
+
...prev.messages,
|
|
645
|
+
{
|
|
646
|
+
id,
|
|
647
|
+
role,
|
|
648
|
+
_insertionIndex: insertionIndexRef.current++,
|
|
649
|
+
time: { created: Date.now() }
|
|
650
|
+
}
|
|
651
|
+
],
|
|
652
|
+
partMap: {
|
|
653
|
+
...prev.partMap,
|
|
654
|
+
[id]: prev.partMap[id] ?? []
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
});
|
|
658
|
+
if (role === "assistant" || role === "system") {
|
|
659
|
+
activeAssistantIdRef.current = id;
|
|
660
|
+
partIndexRef.current[id] = partIndexRef.current[id] ?? {};
|
|
661
|
+
setIsStreaming(true);
|
|
662
|
+
}
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (event.type === "message.part.updated") {
|
|
666
|
+
const rawPart = asRecord(eventData.part) ?? eventData;
|
|
667
|
+
const targetId = options?.messageId ?? activeAssistantIdRef.current;
|
|
668
|
+
const delta = asString(eventData.delta);
|
|
669
|
+
if (!targetId || !rawPart) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
const normalizedPart = normalizePart(rawPart);
|
|
673
|
+
if (!normalizedPart) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
const key = getPartKey(rawPart);
|
|
677
|
+
setConversation((prev) => {
|
|
678
|
+
const existingParts = prev.partMap[targetId] ?? [];
|
|
679
|
+
const nextParts = [...existingParts];
|
|
680
|
+
const indexMap = partIndexRef.current[targetId] ?? (partIndexRef.current[targetId] = {});
|
|
681
|
+
const existingIndex = indexMap[key];
|
|
682
|
+
if (existingIndex == null) {
|
|
683
|
+
indexMap[key] = nextParts.length;
|
|
684
|
+
nextParts.push(mergePart(void 0, normalizedPart, delta));
|
|
685
|
+
} else {
|
|
686
|
+
nextParts[existingIndex] = mergePart(
|
|
687
|
+
nextParts[existingIndex],
|
|
688
|
+
normalizedPart,
|
|
689
|
+
delta
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
return {
|
|
693
|
+
...prev,
|
|
694
|
+
partMap: {
|
|
695
|
+
...prev.partMap,
|
|
696
|
+
[targetId]: nextParts
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
});
|
|
700
|
+
activeAssistantIdRef.current = targetId;
|
|
701
|
+
setIsStreaming(true);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (event.type === "result") {
|
|
705
|
+
completeAssistantMessage({
|
|
706
|
+
messageId: options?.messageId,
|
|
707
|
+
finalText: asString(eventData.finalText)
|
|
708
|
+
});
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
if (event.type === "done") {
|
|
712
|
+
completeAssistantMessage({ messageId: options?.messageId });
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (event.type === "error") {
|
|
716
|
+
failAssistantMessage(
|
|
717
|
+
asString(eventData.message) ?? "Agent error",
|
|
718
|
+
{ messageId: options?.messageId }
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
[completeAssistantMessage, failAssistantMessage]
|
|
723
|
+
);
|
|
724
|
+
const reset = useCallback3(() => {
|
|
725
|
+
setConversation({ messages: [], partMap: {} });
|
|
726
|
+
setIsStreaming(false);
|
|
727
|
+
activeAssistantIdRef.current = null;
|
|
728
|
+
insertionIndexRef.current = 0;
|
|
729
|
+
partIndexRef.current = {};
|
|
730
|
+
}, []);
|
|
731
|
+
return {
|
|
732
|
+
messages: conversation.messages,
|
|
733
|
+
partMap: conversation.partMap,
|
|
734
|
+
isStreaming,
|
|
735
|
+
activeAssistantMessageId: activeAssistantIdRef.current,
|
|
736
|
+
replaceHistory,
|
|
737
|
+
appendUserMessage,
|
|
738
|
+
beginAssistantMessage,
|
|
739
|
+
applySdkEvent,
|
|
740
|
+
completeAssistantMessage,
|
|
741
|
+
failAssistantMessage,
|
|
742
|
+
setStreaming: setIsStreaming,
|
|
743
|
+
reset
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
414
747
|
// src/hooks/use-session-stream.ts
|
|
415
|
-
import { useCallback as useCallback4, useEffect as
|
|
748
|
+
import { useCallback as useCallback4, useEffect as useEffect2, useRef as useRef4, useState as useState4 } from "react";
|
|
416
749
|
var _insertionCounter = 0;
|
|
417
750
|
function mapApiMessage(msg) {
|
|
418
751
|
const created = msg.info.timestamp ? new Date(msg.info.timestamp).getTime() : Date.now();
|
|
@@ -467,8 +800,8 @@ function useSessionStream({
|
|
|
467
800
|
const [isStreaming, setIsStreaming] = useState4(false);
|
|
468
801
|
const [error, setError] = useState4(null);
|
|
469
802
|
const [connected, setConnected] = useState4(false);
|
|
470
|
-
const abortRef =
|
|
471
|
-
const streamingMsgIdRef =
|
|
803
|
+
const abortRef = useRef4(null);
|
|
804
|
+
const streamingMsgIdRef = useRef4(null);
|
|
472
805
|
const refetch = useCallback4(async () => {
|
|
473
806
|
if (!token || !sessionId || !apiUrl) return;
|
|
474
807
|
try {
|
|
@@ -655,7 +988,7 @@ function useSessionStream({
|
|
|
655
988
|
setError(msg);
|
|
656
989
|
}
|
|
657
990
|
}, [apiUrl, token, sessionId]);
|
|
658
|
-
|
|
991
|
+
useEffect2(() => {
|
|
659
992
|
if (!enabled || !token || !sessionId) return;
|
|
660
993
|
refetch();
|
|
661
994
|
connectSSE();
|
|
@@ -668,12 +1001,12 @@ function useSessionStream({
|
|
|
668
1001
|
}
|
|
669
1002
|
|
|
670
1003
|
// src/hooks/use-dropdown-menu.ts
|
|
671
|
-
import { useEffect as
|
|
1004
|
+
import { useEffect as useEffect3, useRef as useRef5, useState as useState5 } from "react";
|
|
672
1005
|
function useDropdownMenu(options) {
|
|
673
1006
|
const closeOnEsc = options?.closeOnEsc ?? true;
|
|
674
1007
|
const [open, setOpen] = useState5(false);
|
|
675
|
-
const ref =
|
|
676
|
-
|
|
1008
|
+
const ref = useRef5(null);
|
|
1009
|
+
useEffect3(() => {
|
|
677
1010
|
function handleClick(e) {
|
|
678
1011
|
if (ref.current && !ref.current.contains(e.target)) {
|
|
679
1012
|
setOpen(false);
|
|
@@ -684,7 +1017,7 @@ function useDropdownMenu(options) {
|
|
|
684
1017
|
}
|
|
685
1018
|
return () => document.removeEventListener("mousedown", handleClick);
|
|
686
1019
|
}, [open]);
|
|
687
|
-
|
|
1020
|
+
useEffect3(() => {
|
|
688
1021
|
if (!open || !closeOnEsc) return;
|
|
689
1022
|
function handleKey(e) {
|
|
690
1023
|
if (e.key === "Escape") setOpen(false);
|
|
@@ -702,7 +1035,7 @@ function useDropdownMenu(options) {
|
|
|
702
1035
|
}
|
|
703
1036
|
|
|
704
1037
|
// src/hooks/use-sidecar-auth.ts
|
|
705
|
-
import { useState as useState6, useCallback as useCallback5, useEffect as
|
|
1038
|
+
import { useState as useState6, useCallback as useCallback5, useEffect as useEffect4, useRef as useRef6 } from "react";
|
|
706
1039
|
function storageKey(resourceId, apiUrl) {
|
|
707
1040
|
return `sidecar_session_${resourceId}__${apiUrl}`;
|
|
708
1041
|
}
|
|
@@ -738,7 +1071,7 @@ function useSidecarAuth({ resourceId, apiUrl, signMessage }) {
|
|
|
738
1071
|
const [expiresAt, setExpiresAt] = useState6(cached?.expiresAt ?? 0);
|
|
739
1072
|
const [isAuthenticating, setIsAuthenticating] = useState6(false);
|
|
740
1073
|
const [error, setError] = useState6(null);
|
|
741
|
-
const refreshTimerRef =
|
|
1074
|
+
const refreshTimerRef = useRef6(void 0);
|
|
742
1075
|
const clearCachedToken = useCallback5(() => {
|
|
743
1076
|
setToken(null);
|
|
744
1077
|
setExpiresAt(0);
|
|
@@ -779,7 +1112,7 @@ function useSidecarAuth({ resourceId, apiUrl, signMessage }) {
|
|
|
779
1112
|
setIsAuthenticating(false);
|
|
780
1113
|
}
|
|
781
1114
|
}, [resourceId, apiUrl, signMessage, clearCachedToken]);
|
|
782
|
-
|
|
1115
|
+
useEffect4(() => {
|
|
783
1116
|
if (refreshTimerRef.current) {
|
|
784
1117
|
clearTimeout(refreshTimerRef.current);
|
|
785
1118
|
}
|
|
@@ -812,10 +1145,8 @@ function useSidecarAuth({ resourceId, apiUrl, signMessage }) {
|
|
|
812
1145
|
|
|
813
1146
|
export {
|
|
814
1147
|
useToolCallStream,
|
|
815
|
-
useAuth,
|
|
816
|
-
createAuthFetcher,
|
|
817
|
-
useApiKey,
|
|
818
1148
|
useSSEStream,
|
|
1149
|
+
useSdkSession,
|
|
819
1150
|
useSessionStream,
|
|
820
1151
|
useDropdownMenu,
|
|
821
1152
|
useSidecarAuth
|