@gendive/chatllm 0.22.0 → 0.23.1
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/index.d.mts +2 -815
- package/dist/index.d.ts +2 -815
- package/dist/index.js +2 -2158
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -2043
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +309 -2
- package/dist/react/index.d.ts +309 -2
- package/dist/react/index.js +1284 -952
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +1085 -763
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import React17 from "react";
|
|
3
3
|
|
|
4
4
|
// src/react/hooks/useChatUI.ts
|
|
5
|
-
import { useState as useState6, useRef as
|
|
5
|
+
import { useState as useState6, useRef as useRef7, useCallback as useCallback8, useEffect as useEffect4, useMemo as useMemo4 } from "react";
|
|
6
6
|
|
|
7
7
|
// src/types.ts
|
|
8
8
|
var DEFAULT_PERSONALIZATION = {
|
|
@@ -2059,6 +2059,462 @@ var parseContextRefs = (content) => {
|
|
|
2059
2059
|
return { refs, cleanContent };
|
|
2060
2060
|
};
|
|
2061
2061
|
|
|
2062
|
+
// src/react/utils/errors.ts
|
|
2063
|
+
var ChatError = class extends Error {
|
|
2064
|
+
code;
|
|
2065
|
+
retryable;
|
|
2066
|
+
statusCode;
|
|
2067
|
+
originalError;
|
|
2068
|
+
constructor(code, message, options) {
|
|
2069
|
+
super(message);
|
|
2070
|
+
this.name = "ChatError";
|
|
2071
|
+
this.code = code;
|
|
2072
|
+
this.statusCode = options?.statusCode;
|
|
2073
|
+
this.originalError = options?.originalError;
|
|
2074
|
+
this.retryable = options?.retryable ?? isRetryableCode(code);
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
var classifyStatusCode = (status) => {
|
|
2078
|
+
if (status === 401 || status === 403) return "AUTH";
|
|
2079
|
+
if (status === 429) return "RATE_LIMIT";
|
|
2080
|
+
if (status >= 500) return "API";
|
|
2081
|
+
if (status >= 400) return "API";
|
|
2082
|
+
return "UNKNOWN";
|
|
2083
|
+
};
|
|
2084
|
+
var isRetryableCode = (code) => code === "NETWORK" || code === "RATE_LIMIT" || code === "API";
|
|
2085
|
+
var classifyFetchError = (error, response) => {
|
|
2086
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
2087
|
+
return new ChatError("ABORT", "\uC694\uCCAD\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", {
|
|
2088
|
+
originalError: error,
|
|
2089
|
+
retryable: false
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
2093
|
+
return new ChatError("ABORT", "\uC694\uCCAD\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", {
|
|
2094
|
+
originalError: error,
|
|
2095
|
+
retryable: false
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
if (error instanceof ChatError) return error;
|
|
2099
|
+
if (response && !response.ok) {
|
|
2100
|
+
const code = classifyStatusCode(response.status);
|
|
2101
|
+
const messages = {
|
|
2102
|
+
AUTH: "\uC778\uC99D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. API \uD0A4\uB97C \uD655\uC778\uD574\uC8FC\uC138\uC694.",
|
|
2103
|
+
RATE_LIMIT: "\uC694\uCCAD \uD55C\uB3C4\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.",
|
|
2104
|
+
API: `\uC11C\uBC84 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. (${response.status})`,
|
|
2105
|
+
NETWORK: "\uB124\uD2B8\uC6CC\uD06C \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.",
|
|
2106
|
+
TIMEOUT: "\uC694\uCCAD \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
2107
|
+
ABORT: "\uC694\uCCAD\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.",
|
|
2108
|
+
UNKNOWN: "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4."
|
|
2109
|
+
};
|
|
2110
|
+
return new ChatError(code, messages[code], {
|
|
2111
|
+
statusCode: response.status,
|
|
2112
|
+
originalError: error
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
if (error instanceof TypeError) {
|
|
2116
|
+
return new ChatError("NETWORK", "\uB124\uD2B8\uC6CC\uD06C \uC5F0\uACB0\uC744 \uD655\uC778\uD574\uC8FC\uC138\uC694.", {
|
|
2117
|
+
originalError: error
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
2120
|
+
const message = error instanceof Error ? error.message : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
|
|
2121
|
+
return new ChatError("UNKNOWN", message, { originalError: error });
|
|
2122
|
+
};
|
|
2123
|
+
var createTimeoutError = (timeoutMs) => new ChatError("TIMEOUT", `\uC2A4\uD2B8\uB9AC\uBC0D \uC751\uB2F5\uC774 ${timeoutMs / 1e3}\uCD08 \uB3D9\uC548 \uC5C6\uC2B5\uB2C8\uB2E4.`, {
|
|
2124
|
+
retryable: false
|
|
2125
|
+
});
|
|
2126
|
+
|
|
2127
|
+
// src/react/hooks/useStreamingFetch.ts
|
|
2128
|
+
import { useCallback as useCallback6, useRef as useRef5 } from "react";
|
|
2129
|
+
var DEFAULT_CHUNK_TIMEOUT = 3e4;
|
|
2130
|
+
var parseSSELine = (line) => {
|
|
2131
|
+
if (!line.trim()) return null;
|
|
2132
|
+
let data = line;
|
|
2133
|
+
if (line.startsWith("data: ")) {
|
|
2134
|
+
data = line.slice(6);
|
|
2135
|
+
if (data === "[DONE]") return null;
|
|
2136
|
+
}
|
|
2137
|
+
try {
|
|
2138
|
+
const parsed = JSON.parse(data);
|
|
2139
|
+
let usage = null;
|
|
2140
|
+
if (parsed.usage) {
|
|
2141
|
+
usage = {
|
|
2142
|
+
promptTokens: parsed.usage.prompt_tokens ?? parsed.usage.promptTokens ?? 0,
|
|
2143
|
+
completionTokens: parsed.usage.completion_tokens ?? parsed.usage.completionTokens ?? 0,
|
|
2144
|
+
totalTokens: parsed.usage.total_tokens ?? parsed.usage.totalTokens ?? 0
|
|
2145
|
+
};
|
|
2146
|
+
} else if (parsed.prompt_eval_count != null || parsed.eval_count != null) {
|
|
2147
|
+
usage = {
|
|
2148
|
+
promptTokens: parsed.prompt_eval_count ?? 0,
|
|
2149
|
+
completionTokens: parsed.eval_count ?? 0,
|
|
2150
|
+
totalTokens: (parsed.prompt_eval_count ?? 0) + (parsed.eval_count ?? 0)
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
2153
|
+
const delta = parsed.choices?.[0]?.delta ?? null;
|
|
2154
|
+
const finishReason = parsed.choices?.[0]?.finish_reason ?? null;
|
|
2155
|
+
const content = delta?.content ?? parsed.message?.content ?? parsed.content ?? parsed.text ?? "";
|
|
2156
|
+
const thinking = parsed.message?.thinking ?? "";
|
|
2157
|
+
return { content, thinking, finishReason, delta, usage, raw: parsed };
|
|
2158
|
+
} catch {
|
|
2159
|
+
return null;
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2162
|
+
var parseSSEResponse = async (response) => {
|
|
2163
|
+
const reader = response.body?.getReader();
|
|
2164
|
+
if (!reader) return "";
|
|
2165
|
+
const decoder = new TextDecoder();
|
|
2166
|
+
let buffer = "";
|
|
2167
|
+
let result = "";
|
|
2168
|
+
try {
|
|
2169
|
+
while (true) {
|
|
2170
|
+
const { done, value } = await reader.read();
|
|
2171
|
+
if (done) break;
|
|
2172
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2173
|
+
const lines = buffer.split("\n");
|
|
2174
|
+
buffer = lines.pop() || "";
|
|
2175
|
+
for (const line of lines) {
|
|
2176
|
+
const chunk = parseSSELine(line);
|
|
2177
|
+
if (chunk?.content) result += chunk.content;
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
if (buffer.trim()) {
|
|
2181
|
+
const chunk = parseSSELine(buffer);
|
|
2182
|
+
if (chunk?.content) result += chunk.content;
|
|
2183
|
+
}
|
|
2184
|
+
} finally {
|
|
2185
|
+
reader.releaseLock();
|
|
2186
|
+
}
|
|
2187
|
+
return result;
|
|
2188
|
+
};
|
|
2189
|
+
var useStreamingFetch = (options = {}) => {
|
|
2190
|
+
const { chunkTimeout = DEFAULT_CHUNK_TIMEOUT } = options;
|
|
2191
|
+
const abortControllersRef = useRef5(/* @__PURE__ */ new Map());
|
|
2192
|
+
const createAbortController = useCallback6((sessionId) => {
|
|
2193
|
+
const controller = new AbortController();
|
|
2194
|
+
abortControllersRef.current.set(sessionId, controller);
|
|
2195
|
+
return controller;
|
|
2196
|
+
}, []);
|
|
2197
|
+
const abort = useCallback6((sessionId) => {
|
|
2198
|
+
abortControllersRef.current.get(sessionId)?.abort();
|
|
2199
|
+
}, []);
|
|
2200
|
+
const cleanup = useCallback6((sessionId) => {
|
|
2201
|
+
abortControllersRef.current.delete(sessionId);
|
|
2202
|
+
}, []);
|
|
2203
|
+
const getSignal = useCallback6((sessionId) => {
|
|
2204
|
+
return abortControllersRef.current.get(sessionId)?.signal;
|
|
2205
|
+
}, []);
|
|
2206
|
+
const readWithTimeout = useCallback6(async (reader) => {
|
|
2207
|
+
let timerId;
|
|
2208
|
+
try {
|
|
2209
|
+
return await Promise.race([
|
|
2210
|
+
reader.read(),
|
|
2211
|
+
new Promise((_, reject) => {
|
|
2212
|
+
timerId = setTimeout(() => reject(createTimeoutError(chunkTimeout)), chunkTimeout);
|
|
2213
|
+
})
|
|
2214
|
+
]);
|
|
2215
|
+
} finally {
|
|
2216
|
+
clearTimeout(timerId);
|
|
2217
|
+
}
|
|
2218
|
+
}, [chunkTimeout]);
|
|
2219
|
+
const streamResponse = useCallback6(async (response, onChunk, streamOptions) => {
|
|
2220
|
+
if (!response.ok) {
|
|
2221
|
+
throw classifyFetchError(new Error("API error"), response);
|
|
2222
|
+
}
|
|
2223
|
+
const reader = response.body?.getReader();
|
|
2224
|
+
if (!reader) throw new Error("No reader");
|
|
2225
|
+
const decoder = new TextDecoder();
|
|
2226
|
+
let buffer = "";
|
|
2227
|
+
let lastUsage = null;
|
|
2228
|
+
try {
|
|
2229
|
+
while (true) {
|
|
2230
|
+
const { done, value } = streamOptions?.noTimeout ? await reader.read() : await readWithTimeout(reader);
|
|
2231
|
+
if (done) break;
|
|
2232
|
+
buffer += decoder.decode(value, { stream: true });
|
|
2233
|
+
const lines = buffer.split("\n");
|
|
2234
|
+
buffer = lines.pop() || "";
|
|
2235
|
+
let shouldBreak = false;
|
|
2236
|
+
for (const line of lines) {
|
|
2237
|
+
const chunk = parseSSELine(line);
|
|
2238
|
+
if (!chunk) continue;
|
|
2239
|
+
if (chunk.usage) lastUsage = chunk.usage;
|
|
2240
|
+
const result = onChunk(chunk);
|
|
2241
|
+
if (result === "break") {
|
|
2242
|
+
shouldBreak = true;
|
|
2243
|
+
break;
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
if (shouldBreak) break;
|
|
2247
|
+
}
|
|
2248
|
+
if (buffer.trim()) {
|
|
2249
|
+
const chunk = parseSSELine(buffer);
|
|
2250
|
+
if (chunk) {
|
|
2251
|
+
if (chunk.usage) lastUsage = chunk.usage;
|
|
2252
|
+
onChunk(chunk);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
} finally {
|
|
2256
|
+
reader.releaseLock();
|
|
2257
|
+
}
|
|
2258
|
+
return { usage: lastUsage };
|
|
2259
|
+
}, [readWithTimeout]);
|
|
2260
|
+
const fetchAndStream = useCallback6(async (fetchOptions, onChunk) => {
|
|
2261
|
+
const response = await fetch(fetchOptions.url, {
|
|
2262
|
+
method: "POST",
|
|
2263
|
+
headers: { "Content-Type": "application/json" },
|
|
2264
|
+
body: JSON.stringify(fetchOptions.body),
|
|
2265
|
+
signal: fetchOptions.signal
|
|
2266
|
+
});
|
|
2267
|
+
fetchOptions.onHeaders?.(response);
|
|
2268
|
+
return streamResponse(response, onChunk);
|
|
2269
|
+
}, [streamResponse]);
|
|
2270
|
+
return {
|
|
2271
|
+
abortControllers: abortControllersRef,
|
|
2272
|
+
createAbortController,
|
|
2273
|
+
abort,
|
|
2274
|
+
cleanup,
|
|
2275
|
+
getSignal,
|
|
2276
|
+
readWithTimeout,
|
|
2277
|
+
streamResponse,
|
|
2278
|
+
fetchAndStream,
|
|
2279
|
+
parseSSELine
|
|
2280
|
+
};
|
|
2281
|
+
};
|
|
2282
|
+
|
|
2283
|
+
// src/react/hooks/useChecklist.ts
|
|
2284
|
+
import { useRef as useRef6, useCallback as useCallback7, useMemo as useMemo3 } from "react";
|
|
2285
|
+
var CHECKLIST_STEP_DELAY_MS = 100;
|
|
2286
|
+
var toActiveItems = (items) => items.map((it) => ({
|
|
2287
|
+
id: it.id,
|
|
2288
|
+
title: it.title,
|
|
2289
|
+
skill: it.skill,
|
|
2290
|
+
fileIndex: it.fileIndex,
|
|
2291
|
+
fileType: it.fileType,
|
|
2292
|
+
refImage: it.refImage,
|
|
2293
|
+
refStep: it.refStep
|
|
2294
|
+
}));
|
|
2295
|
+
var useChecklist = ({
|
|
2296
|
+
sessionsRef,
|
|
2297
|
+
sessions,
|
|
2298
|
+
setSessions,
|
|
2299
|
+
sendMessage,
|
|
2300
|
+
abortControllers,
|
|
2301
|
+
removeLoadingSession,
|
|
2302
|
+
pendingAttachmentDataRef,
|
|
2303
|
+
trackChecklistSkip,
|
|
2304
|
+
resolveChecklistRefImage,
|
|
2305
|
+
buildChecklistStepPrompt
|
|
2306
|
+
}) => {
|
|
2307
|
+
const skipNextChecklistParsingRef = useRef6(false);
|
|
2308
|
+
const activeChecklistRef = useRef6(null);
|
|
2309
|
+
const pendingChecklistRef = useRef6(null);
|
|
2310
|
+
const sendMessageRef = useRef6(sendMessage);
|
|
2311
|
+
sendMessageRef.current = sendMessage;
|
|
2312
|
+
const findSessionAndMessage = useCallback7(
|
|
2313
|
+
(messageId) => {
|
|
2314
|
+
const session = sessionsRef.current?.find(
|
|
2315
|
+
(s) => s.messages.some((m) => m.id === messageId)
|
|
2316
|
+
);
|
|
2317
|
+
if (!session) return null;
|
|
2318
|
+
const message = session.messages.find((m) => m.id === messageId);
|
|
2319
|
+
if (!message?.checklistBlock) return null;
|
|
2320
|
+
return { session, message };
|
|
2321
|
+
},
|
|
2322
|
+
[sessionsRef]
|
|
2323
|
+
);
|
|
2324
|
+
const executeStep = useCallback7(
|
|
2325
|
+
(items, stepIndex, messageId, sessionId, isFirst) => {
|
|
2326
|
+
skipNextChecklistParsingRef.current = true;
|
|
2327
|
+
setTimeout(() => {
|
|
2328
|
+
const item = items[stepIndex];
|
|
2329
|
+
const refUrl = resolveChecklistRefImage(item, messageId, sessionId);
|
|
2330
|
+
if (refUrl) {
|
|
2331
|
+
pendingAttachmentDataRef.current = [
|
|
2332
|
+
{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }
|
|
2333
|
+
];
|
|
2334
|
+
}
|
|
2335
|
+
sendMessageRef.current(
|
|
2336
|
+
buildChecklistStepPrompt(stepIndex, items.length, item, isFirst, refUrl),
|
|
2337
|
+
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: stepIndex, title: item.title } }
|
|
2338
|
+
);
|
|
2339
|
+
}, CHECKLIST_STEP_DELAY_MS);
|
|
2340
|
+
},
|
|
2341
|
+
[resolveChecklistRefImage, buildChecklistStepPrompt, pendingAttachmentDataRef]
|
|
2342
|
+
);
|
|
2343
|
+
const updateChecklistItems = useCallback7(
|
|
2344
|
+
(sessionId, messageId, updater) => {
|
|
2345
|
+
setSessions(
|
|
2346
|
+
(prev) => prev.map((s) => {
|
|
2347
|
+
if (s.id !== sessionId) return s;
|
|
2348
|
+
return {
|
|
2349
|
+
...s,
|
|
2350
|
+
messages: s.messages.map((m) => {
|
|
2351
|
+
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
2352
|
+
const result = updater(m.checklistBlock.items, m.checklistBlock);
|
|
2353
|
+
return {
|
|
2354
|
+
...m,
|
|
2355
|
+
checklistBlock: {
|
|
2356
|
+
...m.checklistBlock,
|
|
2357
|
+
items: result.items,
|
|
2358
|
+
...result.currentStep !== void 0 ? { currentStep: result.currentStep } : {},
|
|
2359
|
+
...result.completed !== void 0 ? { completed: result.completed } : {}
|
|
2360
|
+
}
|
|
2361
|
+
};
|
|
2362
|
+
})
|
|
2363
|
+
};
|
|
2364
|
+
})
|
|
2365
|
+
);
|
|
2366
|
+
},
|
|
2367
|
+
[setSessions]
|
|
2368
|
+
);
|
|
2369
|
+
const handleChecklistStart = useCallback7(
|
|
2370
|
+
(messageId) => {
|
|
2371
|
+
const found = findSessionAndMessage(messageId);
|
|
2372
|
+
if (!found) return;
|
|
2373
|
+
const { session, message } = found;
|
|
2374
|
+
pendingChecklistRef.current = null;
|
|
2375
|
+
activeChecklistRef.current = {
|
|
2376
|
+
messageId,
|
|
2377
|
+
sessionId: session.id,
|
|
2378
|
+
items: toActiveItems(message.checklistBlock.items),
|
|
2379
|
+
currentStep: 0,
|
|
2380
|
+
stepResults: []
|
|
2381
|
+
};
|
|
2382
|
+
updateChecklistItems(session.id, messageId, (items) => ({
|
|
2383
|
+
items: items.map((it, idx) => ({
|
|
2384
|
+
...it,
|
|
2385
|
+
status: idx === 0 ? "in_progress" : it.status
|
|
2386
|
+
})),
|
|
2387
|
+
currentStep: 0
|
|
2388
|
+
}));
|
|
2389
|
+
executeStep(message.checklistBlock.items, 0, messageId, session.id, true);
|
|
2390
|
+
},
|
|
2391
|
+
[findSessionAndMessage, updateChecklistItems, executeStep]
|
|
2392
|
+
);
|
|
2393
|
+
const handleChecklistAbort = useCallback7(() => {
|
|
2394
|
+
if (!activeChecklistRef.current) return;
|
|
2395
|
+
const checklist = activeChecklistRef.current;
|
|
2396
|
+
const stepIdx = checklist.currentStep;
|
|
2397
|
+
abortControllers.current?.get(checklist.sessionId)?.abort();
|
|
2398
|
+
updateChecklistItems(checklist.sessionId, checklist.messageId, (items) => ({
|
|
2399
|
+
items: items.map((it, idx) => ({
|
|
2400
|
+
...it,
|
|
2401
|
+
status: idx === stepIdx ? "error" : it.status
|
|
2402
|
+
}))
|
|
2403
|
+
}));
|
|
2404
|
+
activeChecklistRef.current = null;
|
|
2405
|
+
removeLoadingSession(checklist.sessionId);
|
|
2406
|
+
}, [abortControllers, updateChecklistItems, removeLoadingSession]);
|
|
2407
|
+
const handleChecklistRetry = useCallback7(
|
|
2408
|
+
(messageId, stepIndex) => {
|
|
2409
|
+
const found = findSessionAndMessage(messageId);
|
|
2410
|
+
if (!found) return;
|
|
2411
|
+
const { session, message } = found;
|
|
2412
|
+
activeChecklistRef.current = {
|
|
2413
|
+
messageId,
|
|
2414
|
+
sessionId: session.id,
|
|
2415
|
+
items: toActiveItems(message.checklistBlock.items),
|
|
2416
|
+
currentStep: stepIndex,
|
|
2417
|
+
stepResults: message.checklistBlock.items.slice(0, stepIndex).map((it) => it.result || "")
|
|
2418
|
+
};
|
|
2419
|
+
updateChecklistItems(session.id, messageId, (items) => ({
|
|
2420
|
+
items: items.map((it, idx) => ({
|
|
2421
|
+
...it,
|
|
2422
|
+
status: idx === stepIndex ? "in_progress" : idx > stepIndex ? "pending" : it.status
|
|
2423
|
+
})),
|
|
2424
|
+
currentStep: stepIndex
|
|
2425
|
+
}));
|
|
2426
|
+
executeStep(message.checklistBlock.items, stepIndex, messageId, session.id, stepIndex === 0);
|
|
2427
|
+
},
|
|
2428
|
+
[findSessionAndMessage, updateChecklistItems, executeStep]
|
|
2429
|
+
);
|
|
2430
|
+
const handleChecklistSkip = useCallback7(
|
|
2431
|
+
(messageId, stepIndex) => {
|
|
2432
|
+
const found = findSessionAndMessage(messageId);
|
|
2433
|
+
if (!found) return;
|
|
2434
|
+
const { session, message } = found;
|
|
2435
|
+
trackChecklistSkip();
|
|
2436
|
+
updateChecklistItems(session.id, messageId, (items) => ({
|
|
2437
|
+
items: items.map((it, idx) => ({
|
|
2438
|
+
...it,
|
|
2439
|
+
status: idx === stepIndex ? "done" : it.status,
|
|
2440
|
+
result: idx === stepIndex ? "(\uAC74\uB108\uB700)" : it.result
|
|
2441
|
+
}))
|
|
2442
|
+
}));
|
|
2443
|
+
const nextPending = message.checklistBlock.items.findIndex(
|
|
2444
|
+
(it, idx) => idx > stepIndex && (it.status === "pending" || it.status === "error")
|
|
2445
|
+
);
|
|
2446
|
+
if (nextPending >= 0) {
|
|
2447
|
+
activeChecklistRef.current = {
|
|
2448
|
+
messageId,
|
|
2449
|
+
sessionId: session.id,
|
|
2450
|
+
items: toActiveItems(message.checklistBlock.items),
|
|
2451
|
+
currentStep: nextPending,
|
|
2452
|
+
stepResults: message.checklistBlock.items.slice(0, nextPending).map((it) => it.result || "(\uAC74\uB108\uB700)")
|
|
2453
|
+
};
|
|
2454
|
+
updateChecklistItems(session.id, messageId, (items) => ({
|
|
2455
|
+
items: items.map((it, idx) => ({
|
|
2456
|
+
...it,
|
|
2457
|
+
status: idx === nextPending ? "in_progress" : it.status
|
|
2458
|
+
})),
|
|
2459
|
+
currentStep: nextPending
|
|
2460
|
+
}));
|
|
2461
|
+
executeStep(message.checklistBlock.items, nextPending, messageId, session.id, false);
|
|
2462
|
+
} else {
|
|
2463
|
+
const allResults = message.checklistBlock.items.map((it, i) => {
|
|
2464
|
+
const result = i === stepIndex ? "(\uAC74\uB108\uB700)" : it.result || "(\uAC74\uB108\uB700)";
|
|
2465
|
+
return `### ${i + 1}. ${it.title}
|
|
2466
|
+
${result}`;
|
|
2467
|
+
}).join("\n\n");
|
|
2468
|
+
updateChecklistItems(session.id, messageId, () => ({
|
|
2469
|
+
items: message.checklistBlock.items.map((it, idx) => ({
|
|
2470
|
+
...it,
|
|
2471
|
+
status: idx === stepIndex ? "done" : it.status,
|
|
2472
|
+
result: idx === stepIndex ? "(\uAC74\uB108\uB700)" : it.result
|
|
2473
|
+
})),
|
|
2474
|
+
completed: true
|
|
2475
|
+
}));
|
|
2476
|
+
skipNextChecklistParsingRef.current = true;
|
|
2477
|
+
setTimeout(() => {
|
|
2478
|
+
sendMessageRef.current(
|
|
2479
|
+
`\uBAA8\uB4E0 \uCCB4\uD06C\uB9AC\uC2A4\uD2B8 \uB2E8\uACC4\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC544\uB798\uB294 \uAC01 \uB2E8\uACC4\uBCC4 \uACB0\uACFC\uC785\uB2C8\uB2E4:
|
|
2480
|
+
|
|
2481
|
+
${allResults}
|
|
2482
|
+
|
|
2483
|
+
\uC704 \uACB0\uACFC\uB97C \uC885\uD569\uD558\uC5EC \uCD5C\uC885 \uACB0\uACFC\uBB3C\uC744 \uC644\uC131\uD574\uC8FC\uC138\uC694. checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`,
|
|
2484
|
+
{ hiddenUserMessage: true, isChecklistExecution: true }
|
|
2485
|
+
);
|
|
2486
|
+
}, CHECKLIST_STEP_DELAY_MS);
|
|
2487
|
+
}
|
|
2488
|
+
},
|
|
2489
|
+
[findSessionAndMessage, updateChecklistItems, executeStep, trackChecklistSkip]
|
|
2490
|
+
);
|
|
2491
|
+
const activeChecklistMessage = useMemo3(() => {
|
|
2492
|
+
const active = activeChecklistRef.current;
|
|
2493
|
+
if (!active) {
|
|
2494
|
+
for (const session2 of sessions) {
|
|
2495
|
+
const msg = session2.messages.find(
|
|
2496
|
+
(m) => m.checklistBlock && !m.checklistBlock.completed
|
|
2497
|
+
);
|
|
2498
|
+
if (msg) return msg;
|
|
2499
|
+
}
|
|
2500
|
+
return null;
|
|
2501
|
+
}
|
|
2502
|
+
const session = sessions.find((s) => s.id === active.sessionId);
|
|
2503
|
+
if (!session) return null;
|
|
2504
|
+
return session.messages.find((m) => m.id === active.messageId) || null;
|
|
2505
|
+
}, [sessions]);
|
|
2506
|
+
return {
|
|
2507
|
+
activeChecklistRef,
|
|
2508
|
+
pendingChecklistRef,
|
|
2509
|
+
skipNextChecklistParsingRef,
|
|
2510
|
+
handleChecklistStart,
|
|
2511
|
+
handleChecklistAbort,
|
|
2512
|
+
handleChecklistRetry,
|
|
2513
|
+
handleChecklistSkip,
|
|
2514
|
+
activeChecklistMessage
|
|
2515
|
+
};
|
|
2516
|
+
};
|
|
2517
|
+
|
|
2062
2518
|
// src/react/utils/sessionCache.ts
|
|
2063
2519
|
var buildCacheKey = (storageKey, sessionId) => `${storageKey}_cache_${sessionId}`;
|
|
2064
2520
|
var writeSessionCache = (storageKey, session) => {
|
|
@@ -2091,38 +2547,14 @@ var removeSessionCache = (storageKey, sessionId) => {
|
|
|
2091
2547
|
};
|
|
2092
2548
|
|
|
2093
2549
|
// src/react/hooks/useChatUI.ts
|
|
2094
|
-
var parseSSEResponse = async (response) => {
|
|
2095
|
-
const reader = response.body?.getReader();
|
|
2096
|
-
if (!reader) return "";
|
|
2097
|
-
const decoder = new TextDecoder();
|
|
2098
|
-
let buffer = "";
|
|
2099
|
-
let result = "";
|
|
2100
|
-
while (true) {
|
|
2101
|
-
const { done, value } = await reader.read();
|
|
2102
|
-
if (done) break;
|
|
2103
|
-
buffer += decoder.decode(value, { stream: true });
|
|
2104
|
-
const lines = buffer.split("\n");
|
|
2105
|
-
buffer = lines.pop() || "";
|
|
2106
|
-
for (const line of lines) {
|
|
2107
|
-
if (line.startsWith("data: ")) {
|
|
2108
|
-
const data = line.slice(6);
|
|
2109
|
-
if (data === "[DONE]") continue;
|
|
2110
|
-
try {
|
|
2111
|
-
const parsed = JSON.parse(data);
|
|
2112
|
-
const chunk = parsed.content ?? parsed.text ?? "";
|
|
2113
|
-
if (chunk) result += chunk;
|
|
2114
|
-
} catch {
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
return result;
|
|
2120
|
-
};
|
|
2121
2550
|
var DEFAULT_STORAGE_KEY2 = "chatllm_sessions";
|
|
2122
2551
|
var DEFAULT_COMPRESSION_THRESHOLD = 20;
|
|
2123
2552
|
var DEFAULT_KEEP_RECENT = 6;
|
|
2124
2553
|
var DEFAULT_RECOMPRESSION_THRESHOLD = 10;
|
|
2125
2554
|
var DEFAULT_TOKEN_LIMIT = 8e3;
|
|
2555
|
+
var DEFAULT_STREAM_CHUNK_TIMEOUT = 3e4;
|
|
2556
|
+
var DEFAULT_MAX_TOOL_CALL_DEPTH = 5;
|
|
2557
|
+
var DEFAULT_MAX_TOOL_RESULT_SIZE = 1e4;
|
|
2126
2558
|
var DEFAULT_SESSION_CONTEXT_MAX_CHARS = 4e3;
|
|
2127
2559
|
var DEFAULT_SESSION_CONTEXT_MAX_ITEMS = 10;
|
|
2128
2560
|
var SESSION_CONTEXT_ITEM_MAX_CHARS = 500;
|
|
@@ -2184,15 +2616,22 @@ var convertAttachmentsToBase64 = async (attachments) => Promise.all(
|
|
|
2184
2616
|
size: att.size
|
|
2185
2617
|
}))
|
|
2186
2618
|
);
|
|
2187
|
-
var
|
|
2188
|
-
attachments.map(async (att) =>
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2619
|
+
var convertAttachmentsWithUploaderCached = async (attachments, uploader, cache) => Promise.all(
|
|
2620
|
+
attachments.map(async (att) => {
|
|
2621
|
+
let url = cache.get(att.id);
|
|
2622
|
+
if (!url) {
|
|
2623
|
+
url = await uploader(att.file);
|
|
2624
|
+
cache.set(att.id, url);
|
|
2625
|
+
}
|
|
2626
|
+
return {
|
|
2627
|
+
name: att.name,
|
|
2628
|
+
mimeType: att.mimeType,
|
|
2629
|
+
base64: "",
|
|
2630
|
+
url,
|
|
2631
|
+
size: att.size,
|
|
2632
|
+
source: "uploader"
|
|
2633
|
+
};
|
|
2634
|
+
})
|
|
2196
2635
|
);
|
|
2197
2636
|
var findPreviousResultImage = (messages) => {
|
|
2198
2637
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -2242,6 +2681,12 @@ var useChatUI = (options) => {
|
|
|
2242
2681
|
onSendMessage,
|
|
2243
2682
|
onSessionChange,
|
|
2244
2683
|
onError,
|
|
2684
|
+
streamChunkTimeout = DEFAULT_STREAM_CHUNK_TIMEOUT,
|
|
2685
|
+
retry: retryConfig,
|
|
2686
|
+
onAbort,
|
|
2687
|
+
onTokenUsage,
|
|
2688
|
+
maxToolCallDepth = DEFAULT_MAX_TOOL_CALL_DEPTH,
|
|
2689
|
+
maxToolResultSize = DEFAULT_MAX_TOOL_RESULT_SIZE,
|
|
2245
2690
|
onTitleChange,
|
|
2246
2691
|
generateTitle: generateTitleCallback,
|
|
2247
2692
|
// Memory options
|
|
@@ -2308,8 +2753,8 @@ var useChatUI = (options) => {
|
|
|
2308
2753
|
const [input, setInput] = useState6("");
|
|
2309
2754
|
const [loadingSessionIds, setLoadingSessionIds] = useState6(/* @__PURE__ */ new Set());
|
|
2310
2755
|
const isLoading = currentSessionId !== null && loadingSessionIds.has(currentSessionId);
|
|
2311
|
-
const addLoadingSession =
|
|
2312
|
-
const removeLoadingSession =
|
|
2756
|
+
const addLoadingSession = useCallback8((id) => setLoadingSessionIds((prev) => new Set(prev).add(id)), []);
|
|
2757
|
+
const removeLoadingSession = useCallback8((id) => setLoadingSessionIds((prev) => {
|
|
2313
2758
|
const next = new Set(prev);
|
|
2314
2759
|
next.delete(id);
|
|
2315
2760
|
return next;
|
|
@@ -2336,41 +2781,45 @@ var useChatUI = (options) => {
|
|
|
2336
2781
|
const [deepResearchProgress, setDeepResearchProgress] = useState6(
|
|
2337
2782
|
null
|
|
2338
2783
|
);
|
|
2339
|
-
const sessionsRef =
|
|
2784
|
+
const sessionsRef = useRef7(sessions);
|
|
2340
2785
|
useEffect4(() => {
|
|
2341
2786
|
sessionsRef.current = sessions;
|
|
2342
2787
|
}, [sessions]);
|
|
2343
|
-
const modelsRef =
|
|
2788
|
+
const modelsRef = useRef7(models);
|
|
2344
2789
|
useEffect4(() => {
|
|
2345
2790
|
modelsRef.current = models;
|
|
2346
2791
|
}, [models]);
|
|
2347
|
-
const onSendMessageRef =
|
|
2348
|
-
const onResponseHeadersRef =
|
|
2349
|
-
const onSessionChangeRef =
|
|
2350
|
-
const onErrorRef =
|
|
2351
|
-
const
|
|
2352
|
-
const
|
|
2353
|
-
const
|
|
2354
|
-
const
|
|
2355
|
-
const
|
|
2356
|
-
const
|
|
2357
|
-
const
|
|
2358
|
-
const
|
|
2359
|
-
const
|
|
2360
|
-
const
|
|
2361
|
-
const
|
|
2362
|
-
const
|
|
2363
|
-
const
|
|
2364
|
-
const
|
|
2365
|
-
const
|
|
2366
|
-
const
|
|
2367
|
-
const
|
|
2368
|
-
const
|
|
2792
|
+
const onSendMessageRef = useRef7(onSendMessage);
|
|
2793
|
+
const onResponseHeadersRef = useRef7(options.onResponseHeaders);
|
|
2794
|
+
const onSessionChangeRef = useRef7(onSessionChange);
|
|
2795
|
+
const onErrorRef = useRef7(onError);
|
|
2796
|
+
const onAbortRef = useRef7(onAbort);
|
|
2797
|
+
const onTokenUsageRef = useRef7(onTokenUsage);
|
|
2798
|
+
const onTitleChangeRef = useRef7(onTitleChange);
|
|
2799
|
+
const generateTitleRef = useRef7(generateTitleCallback);
|
|
2800
|
+
const onPersonalizationChangeRef = useRef7(options.onPersonalizationChange);
|
|
2801
|
+
const onPersonalizationSaveRef = useRef7(options.onPersonalizationSave);
|
|
2802
|
+
const onLoadSessionsRef = useRef7(onLoadSessions);
|
|
2803
|
+
const onCreateSessionRef = useRef7(onCreateSession);
|
|
2804
|
+
const onLoadSessionRef = useRef7(onLoadSession);
|
|
2805
|
+
const onDeleteSessionCallbackRef = useRef7(onDeleteSessionCallback);
|
|
2806
|
+
const onUpdateSessionTitleRef = useRef7(onUpdateSessionTitle);
|
|
2807
|
+
const onSaveMessagesRef = useRef7(onSaveMessages);
|
|
2808
|
+
const onToolCallRef = useRef7(onToolCall);
|
|
2809
|
+
const onSkillCompleteRef = useRef7(onSkillComplete);
|
|
2810
|
+
const onSessionContextChangeRef = useRef7(onSessionContextChange);
|
|
2811
|
+
const onLoadModelsRef = useRef7(onLoadModels);
|
|
2812
|
+
const onUploadImageRef = useRef7(onUploadImage);
|
|
2813
|
+
const onImageErrorRef = useRef7(onImageError);
|
|
2814
|
+
const fileUploaderRef = useRef7(fileUploader);
|
|
2815
|
+
const globalMemoryRef = useRef7(null);
|
|
2369
2816
|
useEffect4(() => {
|
|
2370
2817
|
onSendMessageRef.current = onSendMessage;
|
|
2371
2818
|
onResponseHeadersRef.current = options.onResponseHeaders;
|
|
2372
2819
|
onSessionChangeRef.current = onSessionChange;
|
|
2373
2820
|
onErrorRef.current = onError;
|
|
2821
|
+
onAbortRef.current = onAbort;
|
|
2822
|
+
onTokenUsageRef.current = onTokenUsage;
|
|
2374
2823
|
onTitleChangeRef.current = onTitleChange;
|
|
2375
2824
|
generateTitleRef.current = generateTitleCallback;
|
|
2376
2825
|
onPersonalizationChangeRef.current = options.onPersonalizationChange;
|
|
@@ -2389,15 +2838,19 @@ var useChatUI = (options) => {
|
|
|
2389
2838
|
fileUploaderRef.current = fileUploader;
|
|
2390
2839
|
onLoadModelsRef.current = onLoadModels;
|
|
2391
2840
|
});
|
|
2392
|
-
const abortControllersRef =
|
|
2393
|
-
const
|
|
2394
|
-
const
|
|
2395
|
-
const
|
|
2396
|
-
const
|
|
2397
|
-
const
|
|
2398
|
-
const
|
|
2399
|
-
|
|
2400
|
-
|
|
2841
|
+
const abortControllersRef = useRef7(/* @__PURE__ */ new Map());
|
|
2842
|
+
const { streamResponse } = useStreamingFetch({ chunkTimeout: streamChunkTimeout });
|
|
2843
|
+
const pendingInitialLoadRef = useRef7(null);
|
|
2844
|
+
const skipNextPollParsingRef = useRef7(false);
|
|
2845
|
+
const toolCallDepthRef = useRef7(0);
|
|
2846
|
+
const skipNextSkillParsingRef = useRef7(false);
|
|
2847
|
+
const sendMessageForChecklistRef = useRef7(
|
|
2848
|
+
async () => {
|
|
2849
|
+
console.warn("[ChatUI] sendMessageForChecklistRef called before sendMessage initialized");
|
|
2850
|
+
}
|
|
2851
|
+
);
|
|
2852
|
+
const pendingAttachmentDataRef = useRef7(null);
|
|
2853
|
+
const lastExtractionMsgCountRef = useRef7(0);
|
|
2401
2854
|
const resolveChecklistRefImage = (item, checklistMessageId, sessionId) => {
|
|
2402
2855
|
const session = sessionsRef.current.find((s) => s.id === sessionId);
|
|
2403
2856
|
if (!session) return null;
|
|
@@ -2464,7 +2917,7 @@ ${hints.join(" ")}` : "";
|
|
|
2464
2917
|
return `${stepLabel}
|
|
2465
2918
|
${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`;
|
|
2466
2919
|
};
|
|
2467
|
-
const memoryOptions =
|
|
2920
|
+
const memoryOptions = useMemo4(
|
|
2468
2921
|
() => ({
|
|
2469
2922
|
storageType: globalMemoryConfig?.storageType || "localStorage",
|
|
2470
2923
|
storageKey: globalMemoryConfig?.localStorage?.key || `${storageKey}_memory`,
|
|
@@ -2478,11 +2931,11 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2478
2931
|
const globalMemoryRaw = useGlobalMemory(memoryOptions);
|
|
2479
2932
|
const globalMemory = useGlobalMemoryEnabled ? globalMemoryRaw : null;
|
|
2480
2933
|
globalMemoryRef.current = globalMemory;
|
|
2481
|
-
const stableToolCall =
|
|
2934
|
+
const stableToolCall = useCallback8(
|
|
2482
2935
|
(name, params) => onToolCallRef.current(name, params),
|
|
2483
2936
|
[]
|
|
2484
2937
|
);
|
|
2485
|
-
const mergedSkills =
|
|
2938
|
+
const mergedSkills = useMemo4(() => {
|
|
2486
2939
|
if (!tools || !onToolCall) return skills || {};
|
|
2487
2940
|
const toolSkills = convertToolsToSkills(tools, stableToolCall);
|
|
2488
2941
|
return { ...skills || {}, ...toolSkills };
|
|
@@ -2520,7 +2973,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2520
2973
|
});
|
|
2521
2974
|
const [projectSettingsOpen, setProjectSettingsOpen] = useState6(false);
|
|
2522
2975
|
const projectMemoryKey = enableProjects && projectHook.currentProjectId ? `${storageKey}_project_memory_${projectHook.currentProjectId}` : `${storageKey}_project_memory_none`;
|
|
2523
|
-
const projectMemoryOptions =
|
|
2976
|
+
const projectMemoryOptions = useMemo4(
|
|
2524
2977
|
() => ({
|
|
2525
2978
|
storageType: globalMemoryConfig?.storageType || "localStorage",
|
|
2526
2979
|
storageKey: projectMemoryKey,
|
|
@@ -2532,7 +2985,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2532
2985
|
);
|
|
2533
2986
|
const projectMemoryRaw = useGlobalMemory(projectMemoryOptions);
|
|
2534
2987
|
const projectMemory = enableProjects ? projectMemoryRaw : null;
|
|
2535
|
-
const unwrapResponseHeaders =
|
|
2988
|
+
const unwrapResponseHeaders = useCallback8((result) => {
|
|
2536
2989
|
if (typeof result === "object" && result !== null && "response" in result && "headers" in result) {
|
|
2537
2990
|
const wrapped = result;
|
|
2538
2991
|
onResponseHeadersRef.current?.(wrapped.headers);
|
|
@@ -2540,7 +2993,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2540
2993
|
}
|
|
2541
2994
|
return result;
|
|
2542
2995
|
}, []);
|
|
2543
|
-
const emitFetchHeaders =
|
|
2996
|
+
const emitFetchHeaders = useCallback8((response) => {
|
|
2544
2997
|
if (onResponseHeadersRef.current && response.headers) {
|
|
2545
2998
|
const headerMap = {};
|
|
2546
2999
|
response.headers.forEach((v, k) => {
|
|
@@ -2549,7 +3002,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2549
3002
|
onResponseHeadersRef.current(headerMap);
|
|
2550
3003
|
}
|
|
2551
3004
|
}, []);
|
|
2552
|
-
const callInternalLLM =
|
|
3005
|
+
const callInternalLLM = useCallback8(async (prompt, model) => {
|
|
2553
3006
|
if (onSendMessageRef.current) {
|
|
2554
3007
|
const modelConfig = modelsRef.current.find((m) => m.id === model);
|
|
2555
3008
|
const provider = modelConfig?.provider || "ollama";
|
|
@@ -2587,7 +3040,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2587
3040
|
const currentSession = sessions.find((s) => s.id === currentSessionId) || null;
|
|
2588
3041
|
const messages = currentSession?.messages.filter((m) => !m.hidden) || [];
|
|
2589
3042
|
const compressionState = currentSession?.compressionState || null;
|
|
2590
|
-
const visibleSessions =
|
|
3043
|
+
const visibleSessions = useMemo4(() => {
|
|
2591
3044
|
if (!enableProjects || !projectHook.currentProjectId) return sessions;
|
|
2592
3045
|
return sessions.filter((s) => s.projectId === projectHook.currentProjectId);
|
|
2593
3046
|
}, [sessions, enableProjects, projectHook.currentProjectId]);
|
|
@@ -2682,7 +3135,7 @@ ${focusHint}${hintLine} checklist \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \u
|
|
|
2682
3135
|
useEffect4(() => {
|
|
2683
3136
|
onSessionChangeRef.current?.(currentSession);
|
|
2684
3137
|
}, [currentSession]);
|
|
2685
|
-
const buildSystemPrompt =
|
|
3138
|
+
const buildSystemPrompt = useCallback8((session) => {
|
|
2686
3139
|
const parts = [];
|
|
2687
3140
|
const { userProfile, responseStyle, language } = personalization;
|
|
2688
3141
|
const identityName = assistantName || "AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8";
|
|
@@ -3041,7 +3494,7 @@ AI (\uD655\uC815):
|
|
|
3041
3494
|
}
|
|
3042
3495
|
return parts.length > 0 ? parts.join("\n") : "";
|
|
3043
3496
|
}, [personalization, globalMemory, useGlobalMemoryEnabled, enablePoll, enableChecklist, buildSkillsPrompt, enableProjects, projectHook.currentProject, projectMemory, resolvedSkills, assistantName, onBuildSystemPrompt, compactSystemPrompt, selectedModel]);
|
|
3044
|
-
const promoteToSessionContext =
|
|
3497
|
+
const promoteToSessionContext = useCallback8((sessionId, skillName, content, metadata, label) => {
|
|
3045
3498
|
const item = createSessionContextItem(skillName, content, metadata, label);
|
|
3046
3499
|
setSessions(
|
|
3047
3500
|
(prev) => prev.map((s) => {
|
|
@@ -3052,7 +3505,7 @@ AI (\uD655\uC815):
|
|
|
3052
3505
|
})
|
|
3053
3506
|
);
|
|
3054
3507
|
}, []);
|
|
3055
|
-
const compressContext =
|
|
3508
|
+
const compressContext = useCallback8(async (messagesToCompress, model) => {
|
|
3056
3509
|
const conversationText = messagesToCompress.map((m) => `${m.role === "user" ? "\uC0AC\uC6A9\uC790" : "AI"}: ${m.content}`).join("\n\n");
|
|
3057
3510
|
const summaryPrompt = `\uB2E4\uC74C \uB300\uD654 \uB0B4\uC6A9\uC744 \uD575\uC2EC \uC815\uBCF4\uB9CC \uC720\uC9C0\uD558\uBA74\uC11C \uAC04\uACB0\uD558\uAC8C \uC694\uC57D\uD574\uC8FC\uC138\uC694.
|
|
3058
3511
|
\uC911\uC694\uD55C \uACB0\uC815\uC0AC\uD56D, \uC0AC\uC6A9\uC790 \uC694\uAD6C\uC0AC\uD56D, \uB9E5\uB77D \uC815\uBCF4\uB97C \uBCF4\uC874\uD558\uC138\uC694.
|
|
@@ -3067,7 +3520,7 @@ ${conversationText}
|
|
|
3067
3520
|
return "";
|
|
3068
3521
|
}
|
|
3069
3522
|
}, [callInternalLLM]);
|
|
3070
|
-
const incrementalCompressContext =
|
|
3523
|
+
const incrementalCompressContext = useCallback8(
|
|
3071
3524
|
async (existingSummary, newMessages, model) => {
|
|
3072
3525
|
const newConversation = newMessages.map((m) => `${m.role === "user" ? "\uC0AC\uC6A9\uC790" : "AI"}: ${m.content}`).join("\n\n");
|
|
3073
3526
|
const mergePrompt = `\uAE30\uC874 \uB300\uD654 \uC694\uC57D\uACFC \uC0C8\uB85C\uC6B4 \uB300\uD654 \uB0B4\uC6A9\uC744 \uD1B5\uD569\uD558\uC5EC \uD558\uB098\uC758 \uC694\uC57D\uC73C\uB85C \uB9CC\uB4E4\uC5B4\uC8FC\uC138\uC694.
|
|
@@ -3094,10 +3547,10 @@ ${newConversation}
|
|
|
3094
3547
|
},
|
|
3095
3548
|
[callInternalLLM]
|
|
3096
3549
|
);
|
|
3097
|
-
const estimateTokens =
|
|
3550
|
+
const estimateTokens = useCallback8((messages2) => {
|
|
3098
3551
|
return messages2.reduce((sum, m) => sum + Math.ceil(m.content.length / 4), 0);
|
|
3099
3552
|
}, []);
|
|
3100
|
-
const newSession =
|
|
3553
|
+
const newSession = useCallback8(async () => {
|
|
3101
3554
|
const projectId = enableProjects ? projectHook.currentProjectId || DEFAULT_PROJECT_ID : void 0;
|
|
3102
3555
|
if (useExternalStorage && onCreateSessionRef.current) {
|
|
3103
3556
|
setIsSessionLoading(true);
|
|
@@ -3135,7 +3588,7 @@ ${newConversation}
|
|
|
3135
3588
|
setSessions((prev) => [newSess, ...prev]);
|
|
3136
3589
|
setCurrentSessionId(newSess.id);
|
|
3137
3590
|
}, [selectedModel, useExternalStorage, enableProjects, projectHook.currentProjectId]);
|
|
3138
|
-
const selectSession =
|
|
3591
|
+
const selectSession = useCallback8(async (id) => {
|
|
3139
3592
|
if (useExternalStorage && onLoadSessionRef.current) {
|
|
3140
3593
|
setIsSessionLoading(true);
|
|
3141
3594
|
try {
|
|
@@ -3245,7 +3698,7 @@ ${newConversation}
|
|
|
3245
3698
|
pendingInitialLoadRef.current = null;
|
|
3246
3699
|
selectSession(id);
|
|
3247
3700
|
}, [currentSessionId, selectSession, useExternalStorage]);
|
|
3248
|
-
const deleteSession =
|
|
3701
|
+
const deleteSession = useCallback8(async (id) => {
|
|
3249
3702
|
if (useExternalStorage && onDeleteSessionCallbackRef.current) {
|
|
3250
3703
|
try {
|
|
3251
3704
|
await onDeleteSessionCallbackRef.current(id);
|
|
@@ -3273,7 +3726,7 @@ ${newConversation}
|
|
|
3273
3726
|
return filtered;
|
|
3274
3727
|
});
|
|
3275
3728
|
}, [currentSessionId, storageKey, useExternalStorage]);
|
|
3276
|
-
const renameSession =
|
|
3729
|
+
const renameSession = useCallback8(async (id, newTitle) => {
|
|
3277
3730
|
if (!newTitle.trim()) return;
|
|
3278
3731
|
if (useExternalStorage && onUpdateSessionTitleRef.current) {
|
|
3279
3732
|
try {
|
|
@@ -3296,7 +3749,7 @@ ${newConversation}
|
|
|
3296
3749
|
);
|
|
3297
3750
|
onTitleChangeRef.current?.(id, newTitle.trim());
|
|
3298
3751
|
}, [useExternalStorage]);
|
|
3299
|
-
const setModel =
|
|
3752
|
+
const setModel = useCallback8((model) => {
|
|
3300
3753
|
setSelectedModel(model);
|
|
3301
3754
|
if (currentSessionId) {
|
|
3302
3755
|
setSessions(
|
|
@@ -3304,10 +3757,10 @@ ${newConversation}
|
|
|
3304
3757
|
);
|
|
3305
3758
|
}
|
|
3306
3759
|
}, [currentSessionId]);
|
|
3307
|
-
const toggleSidebar =
|
|
3308
|
-
const openSettings =
|
|
3309
|
-
const closeSettings =
|
|
3310
|
-
const copyMessage =
|
|
3760
|
+
const toggleSidebar = useCallback8(() => setSidebarOpen((prev) => !prev), []);
|
|
3761
|
+
const openSettings = useCallback8(() => setSettingsOpen(true), []);
|
|
3762
|
+
const closeSettings = useCallback8(() => setSettingsOpen(false), []);
|
|
3763
|
+
const copyMessage = useCallback8((content, id) => {
|
|
3311
3764
|
if (typeof navigator !== "undefined") {
|
|
3312
3765
|
navigator.clipboard.writeText(content).then(() => {
|
|
3313
3766
|
setCopiedMessageId(id);
|
|
@@ -3315,30 +3768,30 @@ ${newConversation}
|
|
|
3315
3768
|
});
|
|
3316
3769
|
}
|
|
3317
3770
|
}, []);
|
|
3318
|
-
const startEdit =
|
|
3771
|
+
const startEdit = useCallback8((message) => {
|
|
3319
3772
|
if (message.role === "user") {
|
|
3320
3773
|
setEditingMessageId(message.id);
|
|
3321
3774
|
}
|
|
3322
3775
|
}, []);
|
|
3323
|
-
const cancelEdit =
|
|
3776
|
+
const cancelEdit = useCallback8(() => {
|
|
3324
3777
|
setEditingMessageId(null);
|
|
3325
3778
|
}, []);
|
|
3326
|
-
const stopGeneration =
|
|
3779
|
+
const stopGeneration = useCallback8(() => {
|
|
3327
3780
|
if (currentSessionId) {
|
|
3328
3781
|
abortControllersRef.current.get(currentSessionId)?.abort();
|
|
3329
3782
|
}
|
|
3330
3783
|
}, [currentSessionId]);
|
|
3331
|
-
const updatePersonalization =
|
|
3784
|
+
const updatePersonalization = useCallback8((config) => {
|
|
3332
3785
|
setPersonalization((prev) => {
|
|
3333
3786
|
const next = { ...prev, ...config };
|
|
3334
3787
|
onPersonalizationChangeRef.current?.(next);
|
|
3335
3788
|
return next;
|
|
3336
3789
|
});
|
|
3337
3790
|
}, []);
|
|
3338
|
-
const savePersonalization =
|
|
3791
|
+
const savePersonalization = useCallback8(() => {
|
|
3339
3792
|
onPersonalizationSaveRef.current?.(personalization);
|
|
3340
3793
|
}, [personalization]);
|
|
3341
|
-
const addAttachments =
|
|
3794
|
+
const addAttachments = useCallback8((files) => {
|
|
3342
3795
|
const newAttachments = files.map((file) => {
|
|
3343
3796
|
const isImage = file.type.startsWith("image/");
|
|
3344
3797
|
return {
|
|
@@ -3353,7 +3806,7 @@ ${newConversation}
|
|
|
3353
3806
|
});
|
|
3354
3807
|
setAttachments((prev) => [...prev, ...newAttachments]);
|
|
3355
3808
|
}, []);
|
|
3356
|
-
const removeAttachment =
|
|
3809
|
+
const removeAttachment = useCallback8((id) => {
|
|
3357
3810
|
setAttachments((prev) => {
|
|
3358
3811
|
const target = prev.find((a) => a.id === id);
|
|
3359
3812
|
if (target?.previewUrl) {
|
|
@@ -3362,17 +3815,17 @@ ${newConversation}
|
|
|
3362
3815
|
return prev.filter((a) => a.id !== id);
|
|
3363
3816
|
});
|
|
3364
3817
|
}, []);
|
|
3365
|
-
const toggleDeepResearchMode =
|
|
3818
|
+
const toggleDeepResearchMode = useCallback8(() => {
|
|
3366
3819
|
setIsDeepResearchMode((prev) => !prev);
|
|
3367
3820
|
}, []);
|
|
3368
|
-
const trackBehavior =
|
|
3821
|
+
const trackBehavior = useCallback8(async (key, updater) => {
|
|
3369
3822
|
if (!globalMemory) return;
|
|
3370
3823
|
const fullKey = `behavior.${key}`;
|
|
3371
3824
|
const prev = globalMemory.get(fullKey) ?? {};
|
|
3372
3825
|
const next = updater(prev);
|
|
3373
3826
|
await globalMemory.set(fullKey, next, { category: "behavior" });
|
|
3374
3827
|
}, [globalMemory]);
|
|
3375
|
-
const trackMessageLength =
|
|
3828
|
+
const trackMessageLength = useCallback8((length) => {
|
|
3376
3829
|
trackBehavior("messageLength", (prev) => {
|
|
3377
3830
|
const samples = (prev.messageLengthSamples || []).slice(-19);
|
|
3378
3831
|
samples.push(length);
|
|
@@ -3380,39 +3833,69 @@ ${newConversation}
|
|
|
3380
3833
|
return { ...prev, messageLengthSamples: samples, avgMessageLength: avg };
|
|
3381
3834
|
});
|
|
3382
3835
|
}, [trackBehavior]);
|
|
3383
|
-
const trackSkillUsage =
|
|
3836
|
+
const trackSkillUsage = useCallback8((skillName) => {
|
|
3384
3837
|
trackBehavior("skillUsage", (prev) => ({
|
|
3385
3838
|
...prev,
|
|
3386
3839
|
[skillName]: (prev[skillName] || 0) + 1
|
|
3387
3840
|
}));
|
|
3388
3841
|
}, [trackBehavior]);
|
|
3389
|
-
const trackChecklistSkip =
|
|
3842
|
+
const trackChecklistSkip = useCallback8(() => {
|
|
3390
3843
|
trackBehavior("checklistSkipRate", (prev) => ({
|
|
3391
3844
|
total: (prev.total || 0) + 1,
|
|
3392
3845
|
skipped: (prev.skipped || 0) + 1
|
|
3393
3846
|
}));
|
|
3394
3847
|
}, [trackBehavior]);
|
|
3395
|
-
const trackChecklistComplete =
|
|
3848
|
+
const trackChecklistComplete = useCallback8(() => {
|
|
3396
3849
|
trackBehavior("checklistSkipRate", (prev) => ({
|
|
3397
3850
|
total: (prev.total || 0) + 1,
|
|
3398
3851
|
skipped: prev.skipped || 0
|
|
3399
3852
|
}));
|
|
3400
3853
|
}, [trackBehavior]);
|
|
3401
|
-
const
|
|
3854
|
+
const {
|
|
3855
|
+
activeChecklistRef,
|
|
3856
|
+
pendingChecklistRef,
|
|
3857
|
+
skipNextChecklistParsingRef,
|
|
3858
|
+
handleChecklistStart,
|
|
3859
|
+
handleChecklistAbort,
|
|
3860
|
+
handleChecklistRetry,
|
|
3861
|
+
handleChecklistSkip
|
|
3862
|
+
} = useChecklist({
|
|
3863
|
+
sessionsRef,
|
|
3864
|
+
sessions,
|
|
3865
|
+
setSessions,
|
|
3866
|
+
sendMessage: (...args) => sendMessageForChecklistRef.current(...args),
|
|
3867
|
+
abortControllers: abortControllersRef,
|
|
3868
|
+
removeLoadingSession,
|
|
3869
|
+
pendingAttachmentDataRef,
|
|
3870
|
+
trackChecklistSkip,
|
|
3871
|
+
resolveChecklistRefImage,
|
|
3872
|
+
buildChecklistStepPrompt
|
|
3873
|
+
});
|
|
3874
|
+
const trackRegenerate = useCallback8(() => {
|
|
3402
3875
|
trackBehavior("regenerateRate", (prev) => ({
|
|
3403
3876
|
...prev,
|
|
3404
3877
|
regenerated: (prev.regenerated || 0) + 1
|
|
3405
3878
|
}));
|
|
3406
3879
|
}, [trackBehavior]);
|
|
3407
|
-
const trackMessageSent =
|
|
3880
|
+
const trackMessageSent = useCallback8(() => {
|
|
3408
3881
|
trackBehavior("regenerateRate", (prev) => ({
|
|
3409
3882
|
total: (prev.total || 0) + 1,
|
|
3410
3883
|
regenerated: prev.regenerated || 0
|
|
3411
3884
|
}));
|
|
3412
3885
|
}, [trackBehavior]);
|
|
3413
|
-
const sendMessage =
|
|
3886
|
+
const sendMessage = useCallback8(async (content, options2) => {
|
|
3414
3887
|
const messageContent = content || input;
|
|
3415
3888
|
if (!messageContent.trim() && attachments.length === 0 || isLoading) return;
|
|
3889
|
+
if (options2?.hiddenUserMessage) {
|
|
3890
|
+
toolCallDepthRef.current += 1;
|
|
3891
|
+
if (toolCallDepthRef.current > maxToolCallDepth) {
|
|
3892
|
+
toolCallDepthRef.current = 0;
|
|
3893
|
+
onErrorRef.current?.(new Error(`\uB3C4\uAD6C \uD638\uCD9C \uAE4A\uC774 \uC81C\uD55C \uCD08\uACFC (\uCD5C\uB300 ${maxToolCallDepth}\uD68C)`));
|
|
3894
|
+
return;
|
|
3895
|
+
}
|
|
3896
|
+
} else {
|
|
3897
|
+
toolCallDepthRef.current = 0;
|
|
3898
|
+
}
|
|
3416
3899
|
let sessionId = currentSessionId;
|
|
3417
3900
|
if (!sessionId) {
|
|
3418
3901
|
if (useExternalStorage && onCreateSessionRef.current) {
|
|
@@ -3466,6 +3949,7 @@ ${finalContent}`;
|
|
|
3466
3949
|
const actionPrompt = selectedAction?.systemPrompt;
|
|
3467
3950
|
const isHidden = options2?.hiddenUserMessage ?? false;
|
|
3468
3951
|
const currentAttachments = attachments;
|
|
3952
|
+
const uploadedUrlMap = /* @__PURE__ */ new Map();
|
|
3469
3953
|
let userContentParts;
|
|
3470
3954
|
if (currentAttachments.length > 0) {
|
|
3471
3955
|
userContentParts = [];
|
|
@@ -3474,8 +3958,19 @@ ${finalContent}`;
|
|
|
3474
3958
|
}
|
|
3475
3959
|
for (const att of currentAttachments) {
|
|
3476
3960
|
if (att.type === "image" && att.file) {
|
|
3477
|
-
|
|
3478
|
-
|
|
3961
|
+
let imageUrl;
|
|
3962
|
+
try {
|
|
3963
|
+
if (fileUploaderRef.current) {
|
|
3964
|
+
imageUrl = await fileUploaderRef.current(att.file);
|
|
3965
|
+
} else {
|
|
3966
|
+
const dataUri = await fileToDataUri(att.file);
|
|
3967
|
+
imageUrl = onUploadImageRef.current ? await onUploadImageRef.current(dataUri, att.name) : dataUri;
|
|
3968
|
+
}
|
|
3969
|
+
} catch (err) {
|
|
3970
|
+
console.error("[chatllm] image upload failed, falling back to data URI:", err);
|
|
3971
|
+
imageUrl = await fileToDataUri(att.file);
|
|
3972
|
+
}
|
|
3973
|
+
uploadedUrlMap.set(att.id, imageUrl);
|
|
3479
3974
|
userContentParts.push({ type: "image", url: imageUrl, alt: att.name, fileName: att.name });
|
|
3480
3975
|
} else {
|
|
3481
3976
|
userContentParts.push({ type: "file", name: att.name, url: att.previewUrl || "", mimeType: att.mimeType, size: att.size });
|
|
@@ -3574,7 +4069,7 @@ ${finalContent}`;
|
|
|
3574
4069
|
})
|
|
3575
4070
|
);
|
|
3576
4071
|
try {
|
|
3577
|
-
const filesToPass = fileUploaderRef.current ? await
|
|
4072
|
+
const filesToPass = fileUploaderRef.current ? await convertAttachmentsWithUploaderCached(matchedFiles, fileUploaderRef.current, uploadedUrlMap) : skillConfig.autoConvertBase64 ? await convertAttachmentsToBase64(matchedFiles) : matchedFiles;
|
|
3578
4073
|
const result = await skillConfig.execute({ files: filesToPass, userMessage: finalContent });
|
|
3579
4074
|
const attachResultType = result.metadata?.type || result.metadata?.resultType || "text";
|
|
3580
4075
|
const toolResultPart = {
|
|
@@ -3633,7 +4128,7 @@ ${finalContent}`;
|
|
|
3633
4128
|
if (hasImageAttachments && hasUserText) {
|
|
3634
4129
|
const imageAttachments = currentAttachments.filter((a) => a.type === "image");
|
|
3635
4130
|
try {
|
|
3636
|
-
pendingAttachmentDataRef.current = fileUploaderRef.current ? await
|
|
4131
|
+
pendingAttachmentDataRef.current = fileUploaderRef.current ? await convertAttachmentsWithUploaderCached(imageAttachments, fileUploaderRef.current, uploadedUrlMap) : await convertAttachmentsToBase64(imageAttachments);
|
|
3637
4132
|
} catch (err) {
|
|
3638
4133
|
console.error("[chatllm] pendingAttachment conversion failed:", err);
|
|
3639
4134
|
pendingAttachmentDataRef.current = null;
|
|
@@ -3686,10 +4181,11 @@ ${finalContent}`;
|
|
|
3686
4181
|
return;
|
|
3687
4182
|
}
|
|
3688
4183
|
abortControllersRef.current.set(capturedSessionId, new AbortController());
|
|
4184
|
+
let accumulatedContent = "";
|
|
4185
|
+
let lastUsage = null;
|
|
3689
4186
|
try {
|
|
3690
4187
|
const shouldSkipSkillParsing = skipNextSkillParsingRef.current;
|
|
3691
4188
|
skipNextSkillParsingRef.current = false;
|
|
3692
|
-
let accumulatedContent = "";
|
|
3693
4189
|
let checklistStepImageUrl = null;
|
|
3694
4190
|
let messagesToSend = [...existingMessages, userMessage];
|
|
3695
4191
|
const recompressionThreshold = DEFAULT_RECOMPRESSION_THRESHOLD;
|
|
@@ -3895,119 +4391,71 @@ ${attachmentContext}
|
|
|
3895
4391
|
emitFetchHeaders(response);
|
|
3896
4392
|
}
|
|
3897
4393
|
if (response) {
|
|
3898
|
-
if (!response.ok) throw new Error("API error");
|
|
3899
|
-
const reader = response.body?.getReader();
|
|
3900
|
-
if (!reader) throw new Error("No reader");
|
|
3901
|
-
const decoder = new TextDecoder();
|
|
3902
|
-
let buffer = "";
|
|
3903
4394
|
let skillTagDetected = false;
|
|
3904
4395
|
let checklistTagDetected = false;
|
|
3905
4396
|
let toolCallAcc = null;
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
4397
|
+
const streamResult = await streamResponse(response, (chunk) => {
|
|
4398
|
+
if (useNativeTools && chunk.delta?.tool_calls) {
|
|
4399
|
+
toolCallAcc = accumulateToolCallDelta(toolCallAcc, chunk.delta);
|
|
4400
|
+
}
|
|
4401
|
+
if (useNativeTools && chunk.finishReason === "tool_calls" && toolCallAcc) {
|
|
4402
|
+
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
4403
|
+
skillTagDetected = true;
|
|
4404
|
+
return "break";
|
|
4405
|
+
}
|
|
4406
|
+
const { content: content2, thinking } = chunk;
|
|
4407
|
+
if (content2 || thinking) {
|
|
4408
|
+
if (content2) accumulatedContent += content2;
|
|
4409
|
+
if (!shouldSkipSkillParsing && accumulatedContent.includes("</skill_use>")) {
|
|
4410
|
+
const endIdx = accumulatedContent.indexOf("</skill_use>");
|
|
4411
|
+
accumulatedContent = accumulatedContent.substring(0, endIdx + "</skill_use>".length);
|
|
4412
|
+
skillTagDetected = true;
|
|
3918
4413
|
}
|
|
3919
|
-
|
|
3920
|
-
const
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
if (useNativeTools && delta?.tool_calls) {
|
|
3924
|
-
toolCallAcc = accumulateToolCallDelta(toolCallAcc, delta);
|
|
3925
|
-
}
|
|
3926
|
-
if (useNativeTools && finishReason === "tool_calls" && toolCallAcc) {
|
|
3927
|
-
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
3928
|
-
skillTagDetected = true;
|
|
3929
|
-
break;
|
|
3930
|
-
}
|
|
3931
|
-
const content2 = delta?.content || parsed.message?.content || parsed.content || parsed.text || "";
|
|
3932
|
-
const thinking = parsed.message?.thinking || "";
|
|
3933
|
-
if (content2 || thinking) {
|
|
3934
|
-
if (content2) accumulatedContent += content2;
|
|
3935
|
-
if (!shouldSkipSkillParsing && accumulatedContent.includes("</skill_use>")) {
|
|
3936
|
-
const endIdx = accumulatedContent.indexOf("</skill_use>");
|
|
3937
|
-
accumulatedContent = accumulatedContent.substring(0, endIdx + "</skill_use>".length);
|
|
3938
|
-
skillTagDetected = true;
|
|
3939
|
-
}
|
|
3940
|
-
if (!skipNextChecklistParsingRef.current && accumulatedContent.includes("</checklist>")) {
|
|
3941
|
-
const endIdx = accumulatedContent.indexOf("</checklist>");
|
|
3942
|
-
accumulatedContent = accumulatedContent.substring(0, endIdx + "</checklist>".length);
|
|
3943
|
-
checklistTagDetected = true;
|
|
3944
|
-
}
|
|
3945
|
-
const displayContent = skillTagDetected ? accumulatedContent : null;
|
|
3946
|
-
setSessions(
|
|
3947
|
-
(prev) => prev.map((s) => {
|
|
3948
|
-
if (s.id === capturedSessionId) {
|
|
3949
|
-
return {
|
|
3950
|
-
...s,
|
|
3951
|
-
messages: s.messages.map((m) => {
|
|
3952
|
-
if (m.id !== assistantMessageId) return m;
|
|
3953
|
-
if (displayContent) {
|
|
3954
|
-
return { ...m, content: displayContent };
|
|
3955
|
-
}
|
|
3956
|
-
let newContent = m.content;
|
|
3957
|
-
if (thinking) {
|
|
3958
|
-
if (!newContent.includes("<thinking>")) {
|
|
3959
|
-
newContent = "<thinking>" + thinking;
|
|
3960
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
3961
|
-
newContent += thinking;
|
|
3962
|
-
}
|
|
3963
|
-
}
|
|
3964
|
-
if (content2) {
|
|
3965
|
-
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
3966
|
-
newContent += "</thinking>\n\n";
|
|
3967
|
-
}
|
|
3968
|
-
newContent += content2;
|
|
3969
|
-
}
|
|
3970
|
-
return { ...m, content: newContent };
|
|
3971
|
-
})
|
|
3972
|
-
};
|
|
3973
|
-
}
|
|
3974
|
-
return s;
|
|
3975
|
-
})
|
|
3976
|
-
);
|
|
3977
|
-
if (skillTagDetected || checklistTagDetected) break;
|
|
3978
|
-
}
|
|
3979
|
-
} catch {
|
|
4414
|
+
if (!skipNextChecklistParsingRef.current && accumulatedContent.includes("</checklist>")) {
|
|
4415
|
+
const endIdx = accumulatedContent.indexOf("</checklist>");
|
|
4416
|
+
accumulatedContent = accumulatedContent.substring(0, endIdx + "</checklist>".length);
|
|
4417
|
+
checklistTagDetected = true;
|
|
3980
4418
|
}
|
|
4419
|
+
const displayContent = skillTagDetected ? accumulatedContent : null;
|
|
4420
|
+
setSessions(
|
|
4421
|
+
(prev) => prev.map((s) => {
|
|
4422
|
+
if (s.id === capturedSessionId) {
|
|
4423
|
+
return {
|
|
4424
|
+
...s,
|
|
4425
|
+
messages: s.messages.map((m) => {
|
|
4426
|
+
if (m.id !== assistantMessageId) return m;
|
|
4427
|
+
if (displayContent) {
|
|
4428
|
+
return { ...m, content: displayContent };
|
|
4429
|
+
}
|
|
4430
|
+
let newContent = m.content;
|
|
4431
|
+
if (thinking) {
|
|
4432
|
+
if (!newContent.includes("<thinking>")) {
|
|
4433
|
+
newContent = "<thinking>" + thinking;
|
|
4434
|
+
} else if (!newContent.includes("</thinking>")) {
|
|
4435
|
+
newContent += thinking;
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
if (content2) {
|
|
4439
|
+
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
4440
|
+
newContent += "</thinking>\n\n";
|
|
4441
|
+
}
|
|
4442
|
+
newContent += content2;
|
|
4443
|
+
}
|
|
4444
|
+
return { ...m, content: newContent };
|
|
4445
|
+
})
|
|
4446
|
+
};
|
|
4447
|
+
}
|
|
4448
|
+
return s;
|
|
4449
|
+
})
|
|
4450
|
+
);
|
|
4451
|
+
if (skillTagDetected || checklistTagDetected) return "break";
|
|
3981
4452
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
4453
|
+
});
|
|
4454
|
+
lastUsage = streamResult.usage;
|
|
3984
4455
|
if (useNativeTools && toolCallAcc && !skillTagDetected) {
|
|
3985
4456
|
accumulatedContent += toolCallToSkillUseXml(toolCallAcc);
|
|
3986
4457
|
skillTagDetected = true;
|
|
3987
4458
|
}
|
|
3988
|
-
if (buffer.trim()) {
|
|
3989
|
-
try {
|
|
3990
|
-
const parsed = JSON.parse(buffer);
|
|
3991
|
-
const content2 = parsed.message?.content || parsed.content || parsed.text;
|
|
3992
|
-
if (content2) {
|
|
3993
|
-
accumulatedContent += content2;
|
|
3994
|
-
setSessions(
|
|
3995
|
-
(prev) => prev.map((s) => {
|
|
3996
|
-
if (s.id === capturedSessionId) {
|
|
3997
|
-
return {
|
|
3998
|
-
...s,
|
|
3999
|
-
messages: s.messages.map(
|
|
4000
|
-
(m) => m.id === assistantMessageId ? { ...m, content: m.content + content2 } : m
|
|
4001
|
-
)
|
|
4002
|
-
};
|
|
4003
|
-
}
|
|
4004
|
-
return s;
|
|
4005
|
-
})
|
|
4006
|
-
);
|
|
4007
|
-
}
|
|
4008
|
-
} catch {
|
|
4009
|
-
}
|
|
4010
|
-
}
|
|
4011
4459
|
}
|
|
4012
4460
|
const saveMessagesOnEarlyReturn = (overrideContentParts) => {
|
|
4013
4461
|
if (!useExternalStorage || !capturedSessionId) return;
|
|
@@ -4368,9 +4816,10 @@ ${result.content}
|
|
|
4368
4816
|
}
|
|
4369
4817
|
skipNextSkillParsingRef.current = true;
|
|
4370
4818
|
saveMessagesOnEarlyReturn();
|
|
4819
|
+
const truncatedResult = result.content.length > maxToolResultSize ? result.content.substring(0, maxToolResultSize) + "\n\n...(truncated)" : result.content;
|
|
4371
4820
|
const resultPrompt = `\uC2A4\uD0AC "${detectedSkill.name}" \uC2E4\uD589 \uACB0\uACFC:
|
|
4372
4821
|
|
|
4373
|
-
${
|
|
4822
|
+
${truncatedResult}
|
|
4374
4823
|
|
|
4375
4824
|
\uC704 \uACB0\uACFC\uB97C \uBC14\uD0D5\uC73C\uB85C \uC0AC\uC6A9\uC790\uC758 \uC6D0\uB798 \uC9C8\uBB38\uC5D0 \uB2F5\uBCC0\uD574\uC8FC\uC138\uC694. skill_use \uD0DC\uADF8\uB294 \uC0AC\uC6A9\uD558\uC9C0 \uB9C8\uC138\uC694.`;
|
|
4376
4825
|
setTimeout(() => {
|
|
@@ -4696,6 +5145,32 @@ ${stepSummary}
|
|
|
4696
5145
|
};
|
|
4697
5146
|
})
|
|
4698
5147
|
);
|
|
5148
|
+
if (lastUsage && capturedSessionId) {
|
|
5149
|
+
const usage = lastUsage;
|
|
5150
|
+
setSessions(
|
|
5151
|
+
(prev) => prev.map((s) => {
|
|
5152
|
+
if (s.id !== capturedSessionId) return s;
|
|
5153
|
+
const prevStats = s.tokenStats ?? {
|
|
5154
|
+
totalPromptTokens: 0,
|
|
5155
|
+
totalCompletionTokens: 0,
|
|
5156
|
+
totalTokens: 0,
|
|
5157
|
+
messageCount: 0,
|
|
5158
|
+
lastUpdated: 0
|
|
5159
|
+
};
|
|
5160
|
+
return {
|
|
5161
|
+
...s,
|
|
5162
|
+
tokenStats: {
|
|
5163
|
+
totalPromptTokens: prevStats.totalPromptTokens + usage.promptTokens,
|
|
5164
|
+
totalCompletionTokens: prevStats.totalCompletionTokens + usage.completionTokens,
|
|
5165
|
+
totalTokens: prevStats.totalTokens + usage.totalTokens,
|
|
5166
|
+
messageCount: prevStats.messageCount + 1,
|
|
5167
|
+
lastUpdated: Date.now()
|
|
5168
|
+
}
|
|
5169
|
+
};
|
|
5170
|
+
})
|
|
5171
|
+
);
|
|
5172
|
+
onTokenUsageRef.current?.(capturedSessionId, usage);
|
|
5173
|
+
}
|
|
4699
5174
|
if (useExternalStorage && capturedSessionId) {
|
|
4700
5175
|
const assistantContentForSave = accumulatedContent;
|
|
4701
5176
|
if (assistantContentForSave && onSaveMessagesRef.current) {
|
|
@@ -4724,296 +5199,77 @@ ${stepSummary}
|
|
|
4724
5199
|
if (sinceLastExtraction >= 4) {
|
|
4725
5200
|
lastExtractionMsgCountRef.current = currentMsgCount;
|
|
4726
5201
|
const recentForExtraction = [...existingMessages.slice(-6), userMessage].map(
|
|
4727
|
-
(m) => ({ role: m.role, content: m.content })
|
|
4728
|
-
);
|
|
4729
|
-
infoExtraction.extractInfo(recentForExtraction).catch(() => {
|
|
4730
|
-
});
|
|
4731
|
-
}
|
|
4732
|
-
}
|
|
4733
|
-
if (observerConfig) {
|
|
4734
|
-
const newMessages = [userMessage, { id: assistantMessageId, role: "assistant", content: accumulatedContent, timestamp: Date.now() }];
|
|
4735
|
-
observer.processMessages(capturedSessionId, newMessages);
|
|
4736
|
-
}
|
|
4737
|
-
} catch (error) {
|
|
4738
|
-
if (error instanceof Error && error.name === "AbortError") {
|
|
4739
|
-
return;
|
|
4740
|
-
}
|
|
4741
|
-
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
4742
|
-
onErrorRef.current?.(err);
|
|
4743
|
-
setSessions(
|
|
4744
|
-
(prev) => prev.map((s) => {
|
|
4745
|
-
if (s.id === capturedSessionId) {
|
|
4746
|
-
return {
|
|
4747
|
-
...s,
|
|
4748
|
-
messages: s.messages.map(
|
|
4749
|
-
(m) => m.id === assistantMessageId ? { ...m, content: "\uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694." } : m
|
|
4750
|
-
)
|
|
4751
|
-
};
|
|
4752
|
-
}
|
|
4753
|
-
return s;
|
|
4754
|
-
})
|
|
4755
|
-
);
|
|
4756
|
-
} finally {
|
|
4757
|
-
removeLoadingSession(capturedSessionId);
|
|
4758
|
-
abortControllersRef.current.delete(capturedSessionId);
|
|
4759
|
-
}
|
|
4760
|
-
}, [
|
|
4761
|
-
input,
|
|
4762
|
-
loadingSessionIds,
|
|
4763
|
-
currentSessionId,
|
|
4764
|
-
sessions,
|
|
4765
|
-
selectedModel,
|
|
4766
|
-
quotedText,
|
|
4767
|
-
selectedAction,
|
|
4768
|
-
apiEndpoint,
|
|
4769
|
-
apiKey,
|
|
4770
|
-
models,
|
|
4771
|
-
contextCompressionThreshold,
|
|
4772
|
-
keepRecentMessages,
|
|
4773
|
-
buildSystemPrompt,
|
|
4774
|
-
compressContext,
|
|
4775
|
-
useExternalStorage,
|
|
4776
|
-
handleSkillCall,
|
|
4777
|
-
resolvedSkills,
|
|
4778
|
-
/** @Todo vibecode - attachments, continueAfterToolResult를 deps에 추가하여 stale closure 방지 */
|
|
4779
|
-
attachments,
|
|
4780
|
-
continueAfterToolResult
|
|
4781
|
-
]);
|
|
4782
|
-
const handleChecklistStart = useCallback6(
|
|
4783
|
-
(messageId) => {
|
|
4784
|
-
const session = sessionsRef.current.find(
|
|
4785
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
4786
|
-
);
|
|
4787
|
-
if (!session) return;
|
|
4788
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
4789
|
-
if (!message?.checklistBlock) return;
|
|
4790
|
-
pendingChecklistRef.current = null;
|
|
4791
|
-
activeChecklistRef.current = {
|
|
4792
|
-
messageId,
|
|
4793
|
-
sessionId: session.id,
|
|
4794
|
-
items: message.checklistBlock.items.map((it) => ({ id: it.id, title: it.title, skill: it.skill, fileIndex: it.fileIndex, fileType: it.fileType, refImage: it.refImage, refStep: it.refStep })),
|
|
4795
|
-
currentStep: 0,
|
|
4796
|
-
stepResults: []
|
|
4797
|
-
};
|
|
4798
|
-
setSessions(
|
|
4799
|
-
(prev) => prev.map((s) => {
|
|
4800
|
-
if (s.id !== session.id) return s;
|
|
4801
|
-
return {
|
|
4802
|
-
...s,
|
|
4803
|
-
messages: s.messages.map((m) => {
|
|
4804
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
4805
|
-
const updatedItems = m.checklistBlock.items.map((it, idx) => ({
|
|
4806
|
-
...it,
|
|
4807
|
-
status: idx === 0 ? "in_progress" : it.status
|
|
4808
|
-
}));
|
|
4809
|
-
return {
|
|
4810
|
-
...m,
|
|
4811
|
-
checklistBlock: { ...m.checklistBlock, items: updatedItems, currentStep: 0 }
|
|
4812
|
-
};
|
|
4813
|
-
})
|
|
4814
|
-
};
|
|
4815
|
-
})
|
|
4816
|
-
);
|
|
4817
|
-
skipNextChecklistParsingRef.current = true;
|
|
4818
|
-
setTimeout(() => {
|
|
4819
|
-
const items = message.checklistBlock.items;
|
|
4820
|
-
const refUrl = resolveChecklistRefImage(items[0], messageId, session.id);
|
|
4821
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
4822
|
-
sendMessage(
|
|
4823
|
-
buildChecklistStepPrompt(0, items.length, items[0], true, refUrl),
|
|
4824
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: 0, title: items[0].title } }
|
|
4825
|
-
);
|
|
4826
|
-
}, 100);
|
|
4827
|
-
},
|
|
4828
|
-
[sendMessage]
|
|
4829
|
-
);
|
|
4830
|
-
const handleChecklistAbort = useCallback6(() => {
|
|
4831
|
-
if (!activeChecklistRef.current) return;
|
|
4832
|
-
const checklist = activeChecklistRef.current;
|
|
4833
|
-
const stepIdx = checklist.currentStep;
|
|
4834
|
-
abortControllersRef.current.get(checklist.sessionId)?.abort();
|
|
4835
|
-
setSessions(
|
|
4836
|
-
(prev) => prev.map((s) => {
|
|
4837
|
-
if (s.id !== checklist.sessionId) return s;
|
|
4838
|
-
return {
|
|
4839
|
-
...s,
|
|
4840
|
-
messages: s.messages.map((m) => {
|
|
4841
|
-
if (m.id !== checklist.messageId || !m.checklistBlock) return m;
|
|
4842
|
-
return {
|
|
4843
|
-
...m,
|
|
4844
|
-
checklistBlock: {
|
|
4845
|
-
...m.checklistBlock,
|
|
4846
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
4847
|
-
...it,
|
|
4848
|
-
status: idx === stepIdx ? "error" : it.status
|
|
4849
|
-
}))
|
|
4850
|
-
}
|
|
4851
|
-
};
|
|
4852
|
-
})
|
|
4853
|
-
};
|
|
4854
|
-
})
|
|
4855
|
-
);
|
|
4856
|
-
activeChecklistRef.current = null;
|
|
4857
|
-
removeLoadingSession(checklist.sessionId);
|
|
4858
|
-
}, [removeLoadingSession]);
|
|
4859
|
-
const handleChecklistRetry = useCallback6(
|
|
4860
|
-
(messageId, stepIndex) => {
|
|
4861
|
-
const session = sessionsRef.current.find(
|
|
4862
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
4863
|
-
);
|
|
4864
|
-
if (!session) return;
|
|
4865
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
4866
|
-
if (!message?.checklistBlock) return;
|
|
4867
|
-
activeChecklistRef.current = {
|
|
4868
|
-
messageId,
|
|
4869
|
-
sessionId: session.id,
|
|
4870
|
-
items: message.checklistBlock.items.map((it) => ({ id: it.id, title: it.title, skill: it.skill, fileIndex: it.fileIndex, fileType: it.fileType, refImage: it.refImage, refStep: it.refStep })),
|
|
4871
|
-
currentStep: stepIndex,
|
|
4872
|
-
stepResults: message.checklistBlock.items.slice(0, stepIndex).map((it) => it.result || "")
|
|
4873
|
-
};
|
|
4874
|
-
setSessions(
|
|
4875
|
-
(prev) => prev.map((s) => {
|
|
4876
|
-
if (s.id !== session.id) return s;
|
|
4877
|
-
return {
|
|
4878
|
-
...s,
|
|
4879
|
-
messages: s.messages.map((m) => {
|
|
4880
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
4881
|
-
return {
|
|
4882
|
-
...m,
|
|
4883
|
-
checklistBlock: {
|
|
4884
|
-
...m.checklistBlock,
|
|
4885
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
4886
|
-
...it,
|
|
4887
|
-
status: idx === stepIndex ? "in_progress" : idx > stepIndex ? "pending" : it.status
|
|
4888
|
-
})),
|
|
4889
|
-
currentStep: stepIndex
|
|
4890
|
-
}
|
|
4891
|
-
};
|
|
4892
|
-
})
|
|
4893
|
-
};
|
|
4894
|
-
})
|
|
4895
|
-
);
|
|
4896
|
-
skipNextChecklistParsingRef.current = true;
|
|
4897
|
-
setTimeout(() => {
|
|
4898
|
-
const items = message.checklistBlock.items;
|
|
4899
|
-
const refUrl = resolveChecklistRefImage(items[stepIndex], messageId, session.id);
|
|
4900
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
4901
|
-
sendMessage(
|
|
4902
|
-
buildChecklistStepPrompt(stepIndex, items.length, items[stepIndex], stepIndex === 0, refUrl),
|
|
4903
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: stepIndex, title: items[stepIndex].title } }
|
|
4904
|
-
);
|
|
4905
|
-
}, 100);
|
|
4906
|
-
},
|
|
4907
|
-
[sendMessage]
|
|
4908
|
-
);
|
|
4909
|
-
const handleChecklistSkip = useCallback6(
|
|
4910
|
-
(messageId, stepIndex) => {
|
|
4911
|
-
const session = sessionsRef.current.find(
|
|
4912
|
-
(s) => s.messages.some((m) => m.id === messageId)
|
|
4913
|
-
);
|
|
4914
|
-
if (!session) return;
|
|
4915
|
-
const message = session.messages.find((m) => m.id === messageId);
|
|
4916
|
-
if (!message?.checklistBlock) return;
|
|
4917
|
-
trackChecklistSkip();
|
|
4918
|
-
setSessions(
|
|
4919
|
-
(prev) => prev.map((s) => {
|
|
4920
|
-
if (s.id !== session.id) return s;
|
|
4921
|
-
return {
|
|
4922
|
-
...s,
|
|
4923
|
-
messages: s.messages.map((m) => {
|
|
4924
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
4925
|
-
return {
|
|
4926
|
-
...m,
|
|
4927
|
-
checklistBlock: {
|
|
4928
|
-
...m.checklistBlock,
|
|
4929
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
4930
|
-
...it,
|
|
4931
|
-
status: idx === stepIndex ? "done" : it.status,
|
|
4932
|
-
result: idx === stepIndex ? "(\uAC74\uB108\uB700)" : it.result
|
|
4933
|
-
}))
|
|
4934
|
-
}
|
|
4935
|
-
};
|
|
4936
|
-
})
|
|
4937
|
-
};
|
|
4938
|
-
})
|
|
4939
|
-
);
|
|
4940
|
-
const nextPending = message.checklistBlock.items.findIndex(
|
|
4941
|
-
(it, idx) => idx > stepIndex && (it.status === "pending" || it.status === "error")
|
|
4942
|
-
);
|
|
4943
|
-
if (nextPending >= 0) {
|
|
4944
|
-
activeChecklistRef.current = {
|
|
4945
|
-
messageId,
|
|
4946
|
-
sessionId: session.id,
|
|
4947
|
-
items: message.checklistBlock.items.map((it) => ({ id: it.id, title: it.title, skill: it.skill, fileIndex: it.fileIndex, fileType: it.fileType, refImage: it.refImage, refStep: it.refStep })),
|
|
4948
|
-
currentStep: nextPending,
|
|
4949
|
-
stepResults: message.checklistBlock.items.slice(0, nextPending).map((it) => it.result || "(\uAC74\uB108\uB700)")
|
|
4950
|
-
};
|
|
4951
|
-
setSessions(
|
|
4952
|
-
(prev) => prev.map((s) => {
|
|
4953
|
-
if (s.id !== session.id) return s;
|
|
4954
|
-
return {
|
|
4955
|
-
...s,
|
|
4956
|
-
messages: s.messages.map((m) => {
|
|
4957
|
-
if (m.id !== messageId || !m.checklistBlock) return m;
|
|
4958
|
-
return {
|
|
4959
|
-
...m,
|
|
4960
|
-
checklistBlock: {
|
|
4961
|
-
...m.checklistBlock,
|
|
4962
|
-
items: m.checklistBlock.items.map((it, idx) => ({
|
|
4963
|
-
...it,
|
|
4964
|
-
status: idx === nextPending ? "in_progress" : it.status
|
|
4965
|
-
})),
|
|
4966
|
-
currentStep: nextPending
|
|
4967
|
-
}
|
|
4968
|
-
};
|
|
4969
|
-
})
|
|
4970
|
-
};
|
|
4971
|
-
})
|
|
4972
|
-
);
|
|
4973
|
-
skipNextChecklistParsingRef.current = true;
|
|
4974
|
-
setTimeout(() => {
|
|
4975
|
-
const items = message.checklistBlock.items;
|
|
4976
|
-
const refUrl = resolveChecklistRefImage(items[nextPending], messageId, session.id);
|
|
4977
|
-
if (refUrl) pendingAttachmentDataRef.current = [{ name: "ref_image", mimeType: "image/png", base64: "", size: 0, url: refUrl }];
|
|
4978
|
-
sendMessage(
|
|
4979
|
-
buildChecklistStepPrompt(nextPending, items.length, items[nextPending], false, refUrl),
|
|
4980
|
-
{ hiddenUserMessage: true, isChecklistExecution: true, checklistStep: { index: nextPending, title: items[nextPending].title } }
|
|
5202
|
+
(m) => ({ role: m.role, content: m.content })
|
|
4981
5203
|
);
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
5204
|
+
infoExtraction.extractInfo(recentForExtraction).catch(() => {
|
|
5205
|
+
});
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
if (observerConfig) {
|
|
5209
|
+
const newMessages = [userMessage, { id: assistantMessageId, role: "assistant", content: accumulatedContent, timestamp: Date.now() }];
|
|
5210
|
+
observer.processMessages(capturedSessionId, newMessages);
|
|
5211
|
+
}
|
|
5212
|
+
} catch (error) {
|
|
5213
|
+
const classified = classifyFetchError(error);
|
|
5214
|
+
if (classified.code === "ABORT") {
|
|
5215
|
+
if (accumulatedContent) {
|
|
5216
|
+
setSessions(
|
|
5217
|
+
(prev) => prev.map((s) => {
|
|
5218
|
+
if (s.id !== capturedSessionId) return s;
|
|
5219
|
+
return {
|
|
5220
|
+
...s,
|
|
5221
|
+
messages: s.messages.map(
|
|
5222
|
+
(m) => m.id === assistantMessageId ? { ...m, content: accumulatedContent } : m
|
|
5223
|
+
)
|
|
5224
|
+
};
|
|
5225
|
+
})
|
|
5226
|
+
);
|
|
5227
|
+
onAbortRef.current?.(capturedSessionId, accumulatedContent);
|
|
5228
|
+
}
|
|
5229
|
+
return;
|
|
5230
|
+
}
|
|
5231
|
+
onErrorRef.current?.(classified);
|
|
5232
|
+
setSessions(
|
|
5233
|
+
(prev) => prev.map((s) => {
|
|
5234
|
+
if (s.id === capturedSessionId) {
|
|
4992
5235
|
return {
|
|
4993
5236
|
...s,
|
|
4994
|
-
messages: s.messages.map(
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
})
|
|
5237
|
+
messages: s.messages.map(
|
|
5238
|
+
(m) => m.id === assistantMessageId ? { ...m, content: accumulatedContent || "\uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694." } : m
|
|
5239
|
+
)
|
|
4998
5240
|
};
|
|
4999
|
-
}
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5241
|
+
}
|
|
5242
|
+
return s;
|
|
5243
|
+
})
|
|
5244
|
+
);
|
|
5245
|
+
} finally {
|
|
5246
|
+
removeLoadingSession(capturedSessionId);
|
|
5247
|
+
abortControllersRef.current.delete(capturedSessionId);
|
|
5248
|
+
}
|
|
5249
|
+
}, [
|
|
5250
|
+
input,
|
|
5251
|
+
loadingSessionIds,
|
|
5252
|
+
currentSessionId,
|
|
5253
|
+
sessions,
|
|
5254
|
+
selectedModel,
|
|
5255
|
+
quotedText,
|
|
5256
|
+
selectedAction,
|
|
5257
|
+
apiEndpoint,
|
|
5258
|
+
apiKey,
|
|
5259
|
+
models,
|
|
5260
|
+
contextCompressionThreshold,
|
|
5261
|
+
keepRecentMessages,
|
|
5262
|
+
buildSystemPrompt,
|
|
5263
|
+
compressContext,
|
|
5264
|
+
useExternalStorage,
|
|
5265
|
+
handleSkillCall,
|
|
5266
|
+
resolvedSkills,
|
|
5267
|
+
/** @Todo vibecode - attachments, continueAfterToolResult를 deps에 추가하여 stale closure 방지 */
|
|
5268
|
+
attachments,
|
|
5269
|
+
continueAfterToolResult
|
|
5270
|
+
]);
|
|
5271
|
+
sendMessageForChecklistRef.current = sendMessage;
|
|
5272
|
+
const handlePollSubmit = useCallback8(
|
|
5017
5273
|
(messageId, responses) => {
|
|
5018
5274
|
const currentSess = sessions.find((s) => s.id === currentSessionId);
|
|
5019
5275
|
const targetMessage = currentSess?.messages.find((m) => m.id === messageId);
|
|
@@ -5083,7 +5339,7 @@ ${formattedParts.join("\n")}
|
|
|
5083
5339
|
},
|
|
5084
5340
|
[sessions, currentSessionId, selectedModel, sendMessage]
|
|
5085
5341
|
);
|
|
5086
|
-
const saveEdit =
|
|
5342
|
+
const saveEdit = useCallback8(async (content) => {
|
|
5087
5343
|
if (!editingMessageId || !currentSession || !currentSessionId) return;
|
|
5088
5344
|
const messageIndex = currentSession.messages.findIndex((m) => m.id === editingMessageId);
|
|
5089
5345
|
if (messageIndex === -1) return;
|
|
@@ -5100,7 +5356,7 @@ ${formattedParts.join("\n")}
|
|
|
5100
5356
|
setEditingMessageId(null);
|
|
5101
5357
|
await sendMessage(content);
|
|
5102
5358
|
}, [editingMessageId, currentSession, currentSessionId, sendMessage]);
|
|
5103
|
-
const regenerate =
|
|
5359
|
+
const regenerate = useCallback8(async (messageId) => {
|
|
5104
5360
|
console.log("[ChatUI] Regenerate called:", { messageId, currentSessionId, isLoading });
|
|
5105
5361
|
if (!currentSession || !currentSessionId || isLoading) {
|
|
5106
5362
|
console.log("[ChatUI] Regenerate early return - missing session or loading");
|
|
@@ -5176,100 +5432,39 @@ ${formattedParts.join("\n")}
|
|
|
5176
5432
|
signal: abortControllersRef.current.get(capturedSessionId).signal
|
|
5177
5433
|
});
|
|
5178
5434
|
console.log("[ChatUI] Regenerate response status:", response.status);
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
if (data === "[DONE]") continue;
|
|
5196
|
-
}
|
|
5197
|
-
try {
|
|
5198
|
-
const parsed = JSON.parse(data);
|
|
5199
|
-
const content = parsed.message?.content || parsed.content || parsed.text || "";
|
|
5200
|
-
const thinking = parsed.message?.thinking || "";
|
|
5201
|
-
if (content || thinking) {
|
|
5202
|
-
setSessions(
|
|
5203
|
-
(prev) => prev.map((s) => {
|
|
5204
|
-
if (s.id === capturedSessionId) {
|
|
5205
|
-
return {
|
|
5206
|
-
...s,
|
|
5207
|
-
messages: s.messages.map((m) => {
|
|
5208
|
-
if (m.id !== assistantMessageId) return m;
|
|
5209
|
-
let newContent = m.content;
|
|
5210
|
-
if (thinking) {
|
|
5211
|
-
if (!newContent.includes("<thinking>")) {
|
|
5212
|
-
newContent = "<thinking>" + thinking;
|
|
5213
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
5214
|
-
newContent += thinking;
|
|
5215
|
-
}
|
|
5216
|
-
}
|
|
5217
|
-
if (content) {
|
|
5218
|
-
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
5219
|
-
newContent += "</thinking>\n\n";
|
|
5220
|
-
}
|
|
5221
|
-
newContent += content;
|
|
5222
|
-
}
|
|
5223
|
-
return { ...m, content: newContent };
|
|
5224
|
-
})
|
|
5225
|
-
};
|
|
5226
|
-
}
|
|
5227
|
-
return s;
|
|
5228
|
-
})
|
|
5229
|
-
);
|
|
5230
|
-
}
|
|
5231
|
-
} catch {
|
|
5232
|
-
}
|
|
5233
|
-
}
|
|
5234
|
-
}
|
|
5235
|
-
if (buffer.trim()) {
|
|
5236
|
-
try {
|
|
5237
|
-
const parsed = JSON.parse(buffer);
|
|
5238
|
-
const content = parsed.message?.content || parsed.content || parsed.text || "";
|
|
5239
|
-
const thinking = parsed.message?.thinking || "";
|
|
5240
|
-
if (content || thinking) {
|
|
5241
|
-
setSessions(
|
|
5242
|
-
(prev) => prev.map((s) => {
|
|
5243
|
-
if (s.id === capturedSessionId) {
|
|
5244
|
-
return {
|
|
5245
|
-
...s,
|
|
5246
|
-
messages: s.messages.map((m) => {
|
|
5247
|
-
if (m.id !== assistantMessageId) return m;
|
|
5248
|
-
let newContent = m.content;
|
|
5249
|
-
if (thinking) {
|
|
5250
|
-
if (!newContent.includes("<thinking>")) {
|
|
5251
|
-
newContent = "<thinking>" + thinking;
|
|
5252
|
-
} else if (!newContent.includes("</thinking>")) {
|
|
5253
|
-
newContent += thinking;
|
|
5254
|
-
}
|
|
5435
|
+
await streamResponse(response, (chunk) => {
|
|
5436
|
+
const { content, thinking } = chunk;
|
|
5437
|
+
if (content || thinking) {
|
|
5438
|
+
setSessions(
|
|
5439
|
+
(prev) => prev.map((s) => {
|
|
5440
|
+
if (s.id === capturedSessionId) {
|
|
5441
|
+
return {
|
|
5442
|
+
...s,
|
|
5443
|
+
messages: s.messages.map((m) => {
|
|
5444
|
+
if (m.id !== assistantMessageId) return m;
|
|
5445
|
+
let newContent = m.content;
|
|
5446
|
+
if (thinking) {
|
|
5447
|
+
if (!newContent.includes("<thinking>")) {
|
|
5448
|
+
newContent = "<thinking>" + thinking;
|
|
5449
|
+
} else if (!newContent.includes("</thinking>")) {
|
|
5450
|
+
newContent += thinking;
|
|
5255
5451
|
}
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
newContent += content;
|
|
5452
|
+
}
|
|
5453
|
+
if (content) {
|
|
5454
|
+
if (newContent.includes("<thinking>") && !newContent.includes("</thinking>")) {
|
|
5455
|
+
newContent += "</thinking>\n\n";
|
|
5261
5456
|
}
|
|
5262
|
-
|
|
5263
|
-
}
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
}
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5457
|
+
newContent += content;
|
|
5458
|
+
}
|
|
5459
|
+
return { ...m, content: newContent };
|
|
5460
|
+
})
|
|
5461
|
+
};
|
|
5462
|
+
}
|
|
5463
|
+
return s;
|
|
5464
|
+
})
|
|
5465
|
+
);
|
|
5271
5466
|
}
|
|
5272
|
-
}
|
|
5467
|
+
}, { noTimeout: true });
|
|
5273
5468
|
} catch (error) {
|
|
5274
5469
|
if (error instanceof Error && error.name === "AbortError") {
|
|
5275
5470
|
return;
|
|
@@ -5280,7 +5475,7 @@ ${formattedParts.join("\n")}
|
|
|
5280
5475
|
removeLoadingSession(capturedSessionId);
|
|
5281
5476
|
}
|
|
5282
5477
|
}, [currentSession, currentSessionId, loadingSessionIds, selectedModel, models, apiEndpoint, apiKey, buildSystemPrompt]);
|
|
5283
|
-
const askOtherModel =
|
|
5478
|
+
const askOtherModel = useCallback8(async (messageId, targetModel) => {
|
|
5284
5479
|
if (!currentSession || !currentSessionId || isLoading) return;
|
|
5285
5480
|
const assistantIndex = currentSession.messages.findIndex((m) => m.id === messageId);
|
|
5286
5481
|
if (assistantIndex === -1) return;
|
|
@@ -5335,30 +5530,7 @@ ${currentSession.contextSummary}` },
|
|
|
5335
5530
|
responseContent = result.content;
|
|
5336
5531
|
responseSources = result.sources;
|
|
5337
5532
|
} else {
|
|
5338
|
-
|
|
5339
|
-
const decoder = new TextDecoder();
|
|
5340
|
-
let buffer = "";
|
|
5341
|
-
while (true) {
|
|
5342
|
-
const { done, value } = await reader.read();
|
|
5343
|
-
if (done) break;
|
|
5344
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5345
|
-
const lines = buffer.split("\n");
|
|
5346
|
-
buffer = lines.pop() || "";
|
|
5347
|
-
for (const line of lines) {
|
|
5348
|
-
if (line.startsWith("data: ")) {
|
|
5349
|
-
const data = line.slice(6);
|
|
5350
|
-
if (data === "[DONE]") continue;
|
|
5351
|
-
try {
|
|
5352
|
-
const parsed = JSON.parse(data);
|
|
5353
|
-
{
|
|
5354
|
-
const chunk = parsed.content ?? parsed.text ?? "";
|
|
5355
|
-
if (chunk) responseContent += chunk;
|
|
5356
|
-
}
|
|
5357
|
-
} catch {
|
|
5358
|
-
}
|
|
5359
|
-
}
|
|
5360
|
-
}
|
|
5361
|
-
}
|
|
5533
|
+
responseContent = await parseSSEResponse(new Response(result));
|
|
5362
5534
|
}
|
|
5363
5535
|
} else {
|
|
5364
5536
|
const isOllama = provider === "ollama" || apiEndpoint.includes("ollama") || apiEndpoint.includes("11434");
|
|
@@ -5376,32 +5548,9 @@ ${currentSession.contextSummary}` },
|
|
|
5376
5548
|
signal: abortControllersRef.current.get(currentSessionId).signal
|
|
5377
5549
|
});
|
|
5378
5550
|
emitFetchHeaders(response);
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
const decoder = new TextDecoder();
|
|
5383
|
-
let buffer = "";
|
|
5384
|
-
while (true) {
|
|
5385
|
-
const { done, value } = await reader.read();
|
|
5386
|
-
if (done) break;
|
|
5387
|
-
buffer += decoder.decode(value, { stream: true });
|
|
5388
|
-
const lines = buffer.split("\n");
|
|
5389
|
-
buffer = lines.pop() || "";
|
|
5390
|
-
for (const line of lines) {
|
|
5391
|
-
if (line.startsWith("data: ")) {
|
|
5392
|
-
const data = line.slice(6);
|
|
5393
|
-
if (data === "[DONE]") continue;
|
|
5394
|
-
try {
|
|
5395
|
-
const parsed = JSON.parse(data);
|
|
5396
|
-
{
|
|
5397
|
-
const chunk = parsed.content ?? parsed.text ?? "";
|
|
5398
|
-
if (chunk) responseContent += chunk;
|
|
5399
|
-
}
|
|
5400
|
-
} catch {
|
|
5401
|
-
}
|
|
5402
|
-
}
|
|
5403
|
-
}
|
|
5404
|
-
}
|
|
5551
|
+
await streamResponse(response, (chunk) => {
|
|
5552
|
+
if (chunk.content) responseContent += chunk.content;
|
|
5553
|
+
}, { noTimeout: true });
|
|
5405
5554
|
}
|
|
5406
5555
|
const alternative = {
|
|
5407
5556
|
id: generateId5("alt"),
|
|
@@ -5461,13 +5610,13 @@ ${currentSession.contextSummary}` },
|
|
|
5461
5610
|
onSendMessage,
|
|
5462
5611
|
onError
|
|
5463
5612
|
]);
|
|
5464
|
-
const setActiveAlternative =
|
|
5613
|
+
const setActiveAlternative = useCallback8((assistantMessageId, index) => {
|
|
5465
5614
|
setActiveAlternatives((prev) => ({
|
|
5466
5615
|
...prev,
|
|
5467
5616
|
[assistantMessageId]: index
|
|
5468
5617
|
}));
|
|
5469
5618
|
}, []);
|
|
5470
|
-
const getActiveAlternative =
|
|
5619
|
+
const getActiveAlternative = useCallback8((assistantMessageId) => {
|
|
5471
5620
|
return activeAlternatives[assistantMessageId] ?? 0;
|
|
5472
5621
|
}, [activeAlternatives]);
|
|
5473
5622
|
return {
|
|
@@ -5525,6 +5674,10 @@ ${currentSession.contextSummary}` },
|
|
|
5525
5674
|
}));
|
|
5526
5675
|
await infoExtraction.extractInfo(recentMessages);
|
|
5527
5676
|
},
|
|
5677
|
+
getTokenStats: useCallback8((sessionId) => {
|
|
5678
|
+
const session = sessions.find((s) => s.id === sessionId);
|
|
5679
|
+
return session?.tokenStats ?? null;
|
|
5680
|
+
}, [sessions]),
|
|
5528
5681
|
// External Storage Loading States
|
|
5529
5682
|
/**
|
|
5530
5683
|
* @description 세션 목록 로딩 상태
|
|
@@ -5685,7 +5838,7 @@ var ImageErrorContext = createContext(null);
|
|
|
5685
5838
|
var useImageError = () => useContext(ImageErrorContext);
|
|
5686
5839
|
|
|
5687
5840
|
// src/react/components/ChatSidebar.tsx
|
|
5688
|
-
import { useState as useState9, useRef as
|
|
5841
|
+
import { useState as useState9, useRef as useRef9, useEffect as useEffect6 } from "react";
|
|
5689
5842
|
|
|
5690
5843
|
// src/react/components/Icon.tsx
|
|
5691
5844
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -5785,7 +5938,7 @@ var IconSvg = ({
|
|
|
5785
5938
|
};
|
|
5786
5939
|
|
|
5787
5940
|
// src/react/components/MarkdownRenderer.tsx
|
|
5788
|
-
import React2, { useMemo as
|
|
5941
|
+
import React2, { useMemo as useMemo5 } from "react";
|
|
5789
5942
|
|
|
5790
5943
|
// src/react/components/LinkChip.tsx
|
|
5791
5944
|
import { useState as useState7 } from "react";
|
|
@@ -6842,11 +6995,11 @@ var MarkdownRenderer = ({
|
|
|
6842
6995
|
sources,
|
|
6843
6996
|
inline = false
|
|
6844
6997
|
}) => {
|
|
6845
|
-
const inlineRendered =
|
|
6998
|
+
const inlineRendered = useMemo5(() => {
|
|
6846
6999
|
if (!inline) return null;
|
|
6847
7000
|
return parseInlineElements(content, "inline-md", sources);
|
|
6848
7001
|
}, [inline, content, sources]);
|
|
6849
|
-
const rendered =
|
|
7002
|
+
const rendered = useMemo5(() => {
|
|
6850
7003
|
if (inline) return null;
|
|
6851
7004
|
const elements = [];
|
|
6852
7005
|
let processedContent = content;
|
|
@@ -7243,7 +7396,7 @@ var MarkdownRenderer = ({
|
|
|
7243
7396
|
};
|
|
7244
7397
|
|
|
7245
7398
|
// src/react/components/ProjectSelector.tsx
|
|
7246
|
-
import { useState as useState8, useRef as
|
|
7399
|
+
import { useState as useState8, useRef as useRef8, useEffect as useEffect5 } from "react";
|
|
7247
7400
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
7248
7401
|
var ProjectSelector = ({
|
|
7249
7402
|
projects,
|
|
@@ -7253,7 +7406,7 @@ var ProjectSelector = ({
|
|
|
7253
7406
|
onProjectSettings
|
|
7254
7407
|
}) => {
|
|
7255
7408
|
const [isOpen, setIsOpen] = useState8(false);
|
|
7256
|
-
const dropdownRef =
|
|
7409
|
+
const dropdownRef = useRef8(null);
|
|
7257
7410
|
const currentProject = projects.find((p) => p.id === currentProjectId);
|
|
7258
7411
|
useEffect5(() => {
|
|
7259
7412
|
const handleClickOutside = (e) => {
|
|
@@ -7491,7 +7644,7 @@ var ChatSidebar = ({
|
|
|
7491
7644
|
const sidebarWidth = typeof widthProp === "number" ? `${widthProp}px` : widthProp || "288px";
|
|
7492
7645
|
const [editingId, setEditingId] = useState9(null);
|
|
7493
7646
|
const [editingTitle, setEditingTitle] = useState9("");
|
|
7494
|
-
const inputRef =
|
|
7647
|
+
const inputRef = useRef9(null);
|
|
7495
7648
|
useEffect6(() => {
|
|
7496
7649
|
if (editingId && inputRef.current) {
|
|
7497
7650
|
inputRef.current.focus();
|
|
@@ -8133,7 +8286,7 @@ var ChatHeader = ({
|
|
|
8133
8286
|
};
|
|
8134
8287
|
|
|
8135
8288
|
// src/react/components/ChatInput.tsx
|
|
8136
|
-
import React6, { useRef as
|
|
8289
|
+
import React6, { useRef as useRef10, useEffect as useEffect7, useState as useState11 } from "react";
|
|
8137
8290
|
import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
8138
8291
|
var ChatInput = ({
|
|
8139
8292
|
value,
|
|
@@ -8162,12 +8315,12 @@ var ChatInput = ({
|
|
|
8162
8315
|
inline = false
|
|
8163
8316
|
}) => {
|
|
8164
8317
|
const [mainMenuOpen, setMainMenuOpen] = React6.useState(false);
|
|
8165
|
-
const textareaRef =
|
|
8166
|
-
const fileInputRef =
|
|
8318
|
+
const textareaRef = useRef10(null);
|
|
8319
|
+
const fileInputRef = useRef10(null);
|
|
8167
8320
|
const [actionMenuOpen, setActionMenuOpen] = useState11(false);
|
|
8168
8321
|
const [isDragOver, setIsDragOver] = useState11(false);
|
|
8169
|
-
const mainMenuRef =
|
|
8170
|
-
const actionMenuRef =
|
|
8322
|
+
const mainMenuRef = useRef10(null);
|
|
8323
|
+
const actionMenuRef = useRef10(null);
|
|
8171
8324
|
useEffect7(() => {
|
|
8172
8325
|
if (textareaRef.current) {
|
|
8173
8326
|
textareaRef.current.style.height = "auto";
|
|
@@ -8896,7 +9049,7 @@ var iconButtonStyle = {
|
|
|
8896
9049
|
};
|
|
8897
9050
|
|
|
8898
9051
|
// src/react/components/MessageList.tsx
|
|
8899
|
-
import React14, { useRef as
|
|
9052
|
+
import React14, { useRef as useRef12, useEffect as useEffect10, useCallback as useCallback13, useState as useState18 } from "react";
|
|
8900
9053
|
|
|
8901
9054
|
// src/react/components/MessageBubble.tsx
|
|
8902
9055
|
import { useState as useState17 } from "react";
|
|
@@ -9185,7 +9338,7 @@ var DeepResearchProgressUI = ({ progress }) => {
|
|
|
9185
9338
|
};
|
|
9186
9339
|
|
|
9187
9340
|
// src/react/components/PollCard.tsx
|
|
9188
|
-
import { useState as useState12, useCallback as
|
|
9341
|
+
import { useState as useState12, useCallback as useCallback9, useEffect as useEffect8 } from "react";
|
|
9189
9342
|
import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
9190
9343
|
var renderInlineMarkdown = (text) => {
|
|
9191
9344
|
const parts = [];
|
|
@@ -9229,7 +9382,7 @@ var PollCard = ({
|
|
|
9229
9382
|
const [otherTexts, setOtherTexts] = useState12({});
|
|
9230
9383
|
const [otherSelected, setOtherSelected] = useState12({});
|
|
9231
9384
|
const currentQuestion = questions[activeTab];
|
|
9232
|
-
const handleOptionToggle =
|
|
9385
|
+
const handleOptionToggle = useCallback9(
|
|
9233
9386
|
(questionId, optionId, multiSelect) => {
|
|
9234
9387
|
setSelections((prev) => {
|
|
9235
9388
|
const current = prev[questionId] || /* @__PURE__ */ new Set();
|
|
@@ -9251,7 +9404,7 @@ var PollCard = ({
|
|
|
9251
9404
|
},
|
|
9252
9405
|
[]
|
|
9253
9406
|
);
|
|
9254
|
-
const handleOtherToggle =
|
|
9407
|
+
const handleOtherToggle = useCallback9((questionId, multiSelect) => {
|
|
9255
9408
|
if (multiSelect) {
|
|
9256
9409
|
setOtherSelected((prev) => ({ ...prev, [questionId]: !prev[questionId] }));
|
|
9257
9410
|
} else {
|
|
@@ -9259,7 +9412,7 @@ var PollCard = ({
|
|
|
9259
9412
|
setOtherSelected((prev) => ({ ...prev, [questionId]: true }));
|
|
9260
9413
|
}
|
|
9261
9414
|
}, []);
|
|
9262
|
-
const handleSubmit =
|
|
9415
|
+
const handleSubmit = useCallback9(() => {
|
|
9263
9416
|
const responses = questions.map((q) => {
|
|
9264
9417
|
const selected = selections[q.id] || /* @__PURE__ */ new Set();
|
|
9265
9418
|
const other = otherSelected[q.id] && otherTexts[q.id]?.trim();
|
|
@@ -9272,7 +9425,7 @@ var PollCard = ({
|
|
|
9272
9425
|
});
|
|
9273
9426
|
onSubmit(responses);
|
|
9274
9427
|
}, [questions, selections, otherSelected, otherTexts, onSubmit]);
|
|
9275
|
-
const handleSkip =
|
|
9428
|
+
const handleSkip = useCallback9(() => {
|
|
9276
9429
|
const responses = questions.map((q) => ({
|
|
9277
9430
|
questionId: q.id,
|
|
9278
9431
|
selectedOptions: [],
|
|
@@ -9320,7 +9473,7 @@ var PollCard = ({
|
|
|
9320
9473
|
}
|
|
9321
9474
|
return () => timers.forEach(clearTimeout);
|
|
9322
9475
|
}, [activeTab, currentQuestion.options.length]);
|
|
9323
|
-
const handleNext =
|
|
9476
|
+
const handleNext = useCallback9(() => {
|
|
9324
9477
|
if (!isLastTab) {
|
|
9325
9478
|
setActiveTab((prev) => prev + 1);
|
|
9326
9479
|
} else {
|
|
@@ -9738,7 +9891,7 @@ var SkillProgressUI = ({
|
|
|
9738
9891
|
};
|
|
9739
9892
|
|
|
9740
9893
|
// src/react/components/ImageContentCard.tsx
|
|
9741
|
-
import { useState as useState13, useCallback as
|
|
9894
|
+
import { useState as useState13, useCallback as useCallback10 } from "react";
|
|
9742
9895
|
import { Fragment as Fragment6, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
9743
9896
|
var ImageContentCard = ({ part }) => {
|
|
9744
9897
|
const [isExpanded, setIsExpanded] = useState13(false);
|
|
@@ -9746,7 +9899,7 @@ var ImageContentCard = ({ part }) => {
|
|
|
9746
9899
|
const [hasError, setHasError] = useState13(false);
|
|
9747
9900
|
const [imgSrc, setImgSrc] = useState13(part.url);
|
|
9748
9901
|
const onImageError = useImageError();
|
|
9749
|
-
const handleImageError =
|
|
9902
|
+
const handleImageError = useCallback10(async () => {
|
|
9750
9903
|
if (onImageError) {
|
|
9751
9904
|
const newUrl = await onImageError(imgSrc, part.fileName);
|
|
9752
9905
|
if (newUrl) {
|
|
@@ -10123,7 +10276,7 @@ var ToolStatusCard = ({
|
|
|
10123
10276
|
};
|
|
10124
10277
|
|
|
10125
10278
|
// src/react/components/ArtifactCard.tsx
|
|
10126
|
-
import { useRef as
|
|
10279
|
+
import { useRef as useRef11, useEffect as useEffect9, useState as useState15, useCallback as useCallback11 } from "react";
|
|
10127
10280
|
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
10128
10281
|
var getThemeStyles = () => {
|
|
10129
10282
|
const root = document.documentElement;
|
|
@@ -10164,9 +10317,9 @@ var getAutoResizeScript = (id) => `
|
|
|
10164
10317
|
});
|
|
10165
10318
|
</script>`;
|
|
10166
10319
|
var HtmlArtifact = ({ code }) => {
|
|
10167
|
-
const iframeRef =
|
|
10320
|
+
const iframeRef = useRef11(null);
|
|
10168
10321
|
const [height, setHeight] = useState15(200);
|
|
10169
|
-
const artifactId =
|
|
10322
|
+
const artifactId = useRef11(`artifact-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`);
|
|
10170
10323
|
useEffect9(() => {
|
|
10171
10324
|
const id = artifactId.current;
|
|
10172
10325
|
const handleMessage = (e) => {
|
|
@@ -10213,10 +10366,10 @@ var SvgArtifact = ({ code }) => {
|
|
|
10213
10366
|
);
|
|
10214
10367
|
};
|
|
10215
10368
|
var MermaidArtifact = ({ code }) => {
|
|
10216
|
-
const containerRef =
|
|
10369
|
+
const containerRef = useRef11(null);
|
|
10217
10370
|
const [svgHtml, setSvgHtml] = useState15(null);
|
|
10218
10371
|
const [error, setError] = useState15(null);
|
|
10219
|
-
const renderMermaid =
|
|
10372
|
+
const renderMermaid = useCallback11(async () => {
|
|
10220
10373
|
try {
|
|
10221
10374
|
let mermaid;
|
|
10222
10375
|
try {
|
|
@@ -10475,7 +10628,7 @@ var ArtifactActions = ({ code, language, containerRef }) => {
|
|
|
10475
10628
|
] });
|
|
10476
10629
|
};
|
|
10477
10630
|
var ArtifactCard = ({ part, index = 0 }) => {
|
|
10478
|
-
const contentRef =
|
|
10631
|
+
const contentRef = useRef11(null);
|
|
10479
10632
|
return /* @__PURE__ */ jsxs13(
|
|
10480
10633
|
"div",
|
|
10481
10634
|
{
|
|
@@ -10647,7 +10800,7 @@ var ContentPartRenderer = ({
|
|
|
10647
10800
|
};
|
|
10648
10801
|
|
|
10649
10802
|
// src/react/components/ChecklistCard.tsx
|
|
10650
|
-
import { useState as useState16, useMemo as
|
|
10803
|
+
import { useState as useState16, useMemo as useMemo6 } from "react";
|
|
10651
10804
|
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
10652
10805
|
var ChecklistCard = ({
|
|
10653
10806
|
items,
|
|
@@ -10658,7 +10811,7 @@ var ChecklistCard = ({
|
|
|
10658
10811
|
onStart
|
|
10659
10812
|
}) => {
|
|
10660
10813
|
const [expandedItems, setExpandedItems] = useState16(/* @__PURE__ */ new Set());
|
|
10661
|
-
const { doneCount, isRunning, hasError, isWaiting } =
|
|
10814
|
+
const { doneCount, isRunning, hasError, isWaiting } = useMemo6(() => {
|
|
10662
10815
|
const done = items.filter((it) => it.status === "done").length;
|
|
10663
10816
|
const running = items.some((it) => it.status === "in_progress");
|
|
10664
10817
|
const waiting = items.every((it) => it.status === "pending");
|
|
@@ -11908,12 +12061,12 @@ var MessageList = ({
|
|
|
11908
12061
|
onExport,
|
|
11909
12062
|
onToggleChecklistPanel
|
|
11910
12063
|
}) => {
|
|
11911
|
-
const messagesEndRef =
|
|
11912
|
-
const containerRef =
|
|
12064
|
+
const messagesEndRef = useRef12(null);
|
|
12065
|
+
const containerRef = useRef12(null);
|
|
11913
12066
|
const [selectedText, setSelectedText] = useState18("");
|
|
11914
12067
|
const [selectionPosition, setSelectionPosition] = useState18(null);
|
|
11915
12068
|
const [showScrollButton, setShowScrollButton] = useState18(false);
|
|
11916
|
-
const userScrollLockRef =
|
|
12069
|
+
const userScrollLockRef = useRef12(false);
|
|
11917
12070
|
const SCROLL_THRESHOLD = 100;
|
|
11918
12071
|
useEffect10(() => {
|
|
11919
12072
|
const container = containerRef.current;
|
|
@@ -11952,7 +12105,7 @@ var MessageList = ({
|
|
|
11952
12105
|
container.removeEventListener("touchmove", handleTouchMove);
|
|
11953
12106
|
};
|
|
11954
12107
|
}, []);
|
|
11955
|
-
const handleScroll =
|
|
12108
|
+
const handleScroll = useCallback13(() => {
|
|
11956
12109
|
if (!containerRef.current) return;
|
|
11957
12110
|
const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
|
|
11958
12111
|
const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
|
|
@@ -11968,7 +12121,7 @@ var MessageList = ({
|
|
|
11968
12121
|
behavior: "smooth"
|
|
11969
12122
|
});
|
|
11970
12123
|
}, [messages]);
|
|
11971
|
-
const scrollToBottom =
|
|
12124
|
+
const scrollToBottom = useCallback13(() => {
|
|
11972
12125
|
userScrollLockRef.current = false;
|
|
11973
12126
|
setShowScrollButton(false);
|
|
11974
12127
|
containerRef.current?.scrollTo({
|
|
@@ -11976,7 +12129,7 @@ var MessageList = ({
|
|
|
11976
12129
|
behavior: "smooth"
|
|
11977
12130
|
});
|
|
11978
12131
|
}, []);
|
|
11979
|
-
const handleMouseUp =
|
|
12132
|
+
const handleMouseUp = useCallback13(() => {
|
|
11980
12133
|
const selection = typeof window !== "undefined" ? window.getSelection() : null;
|
|
11981
12134
|
const text = selection?.toString().trim();
|
|
11982
12135
|
if (text && text.length > 0) {
|
|
@@ -15247,13 +15400,13 @@ var ChatUI = (props) => {
|
|
|
15247
15400
|
};
|
|
15248
15401
|
|
|
15249
15402
|
// src/react/ChatFloatingWidget.tsx
|
|
15250
|
-
import { useState as useState28, useRef as
|
|
15403
|
+
import { useState as useState28, useRef as useRef19, useEffect as useEffect19, useMemo as useMemo8, useCallback as useCallback18 } from "react";
|
|
15251
15404
|
|
|
15252
15405
|
// src/react/hooks/useFloatingWidget.ts
|
|
15253
|
-
import { useState as useState22, useCallback as
|
|
15406
|
+
import { useState as useState22, useCallback as useCallback15, useEffect as useEffect14, useRef as useRef14 } from "react";
|
|
15254
15407
|
|
|
15255
15408
|
// src/react/hooks/useDragResize.ts
|
|
15256
|
-
import { useState as useState21, useRef as
|
|
15409
|
+
import { useState as useState21, useRef as useRef13, useCallback as useCallback14, useEffect as useEffect13, useMemo as useMemo7 } from "react";
|
|
15257
15410
|
var DRAG_THRESHOLD = 5;
|
|
15258
15411
|
var FAB_SIZE = 56;
|
|
15259
15412
|
var EDGE_MARGIN = 24;
|
|
@@ -15348,7 +15501,7 @@ var useDragResize = (options) => {
|
|
|
15348
15501
|
}));
|
|
15349
15502
|
const vw = viewport.width;
|
|
15350
15503
|
const vh = viewport.height;
|
|
15351
|
-
const initializedRef =
|
|
15504
|
+
const initializedRef = useRef13(false);
|
|
15352
15505
|
useEffect13(() => {
|
|
15353
15506
|
if (initializedRef.current || typeof window === "undefined") return;
|
|
15354
15507
|
initializedRef.current = true;
|
|
@@ -15362,26 +15515,26 @@ var useDragResize = (options) => {
|
|
|
15362
15515
|
setFabPos({ x: loaded.fabX, y: loaded.fabY });
|
|
15363
15516
|
setPanelSize({ width: loaded.panelWidth, height: loaded.panelHeight });
|
|
15364
15517
|
}, [initialPosition, storageKey, initialWidth, initialHeight]);
|
|
15365
|
-
const dragStartRef =
|
|
15366
|
-
const isDraggingRef =
|
|
15367
|
-
const wasDragRef =
|
|
15368
|
-
const userDraggedRef =
|
|
15369
|
-
const dragDeltaRef =
|
|
15370
|
-
const fabElRef =
|
|
15371
|
-
const dragCleanupRef =
|
|
15372
|
-
const snapTimerRef =
|
|
15373
|
-
const prevDragRef =
|
|
15374
|
-
const shakeScoreRef =
|
|
15375
|
-
const prevVelocityRef =
|
|
15376
|
-
const isDizzyRef =
|
|
15377
|
-
const dizzyTimerRef =
|
|
15378
|
-
const lastVelocityRef =
|
|
15379
|
-
const dragRafRef =
|
|
15380
|
-
const animCleanupRef =
|
|
15381
|
-
const snapEnabledRef =
|
|
15382
|
-
const storageKeyRef =
|
|
15383
|
-
const panelSizeRef =
|
|
15384
|
-
const fabPosRef =
|
|
15518
|
+
const dragStartRef = useRef13(null);
|
|
15519
|
+
const isDraggingRef = useRef13(false);
|
|
15520
|
+
const wasDragRef = useRef13(false);
|
|
15521
|
+
const userDraggedRef = useRef13(false);
|
|
15522
|
+
const dragDeltaRef = useRef13({ dx: 0, dy: 0 });
|
|
15523
|
+
const fabElRef = useRef13(null);
|
|
15524
|
+
const dragCleanupRef = useRef13(null);
|
|
15525
|
+
const snapTimerRef = useRef13(null);
|
|
15526
|
+
const prevDragRef = useRef13(null);
|
|
15527
|
+
const shakeScoreRef = useRef13(0);
|
|
15528
|
+
const prevVelocityRef = useRef13(null);
|
|
15529
|
+
const isDizzyRef = useRef13(false);
|
|
15530
|
+
const dizzyTimerRef = useRef13(null);
|
|
15531
|
+
const lastVelocityRef = useRef13({ vx: 0, vy: 0, speed: 0 });
|
|
15532
|
+
const dragRafRef = useRef13(null);
|
|
15533
|
+
const animCleanupRef = useRef13(null);
|
|
15534
|
+
const snapEnabledRef = useRef13(snapEnabled);
|
|
15535
|
+
const storageKeyRef = useRef13(storageKey);
|
|
15536
|
+
const panelSizeRef = useRef13(panelSize);
|
|
15537
|
+
const fabPosRef = useRef13(fabPos);
|
|
15385
15538
|
useEffect13(() => {
|
|
15386
15539
|
snapEnabledRef.current = snapEnabled;
|
|
15387
15540
|
}, [snapEnabled]);
|
|
@@ -15394,7 +15547,7 @@ var useDragResize = (options) => {
|
|
|
15394
15547
|
useEffect13(() => {
|
|
15395
15548
|
fabPosRef.current = fabPos;
|
|
15396
15549
|
}, [fabPos]);
|
|
15397
|
-
const resizeStartRef =
|
|
15550
|
+
const resizeStartRef = useRef13(null);
|
|
15398
15551
|
useEffect13(() => {
|
|
15399
15552
|
return () => {
|
|
15400
15553
|
dragCleanupRef.current?.();
|
|
@@ -15403,7 +15556,7 @@ var useDragResize = (options) => {
|
|
|
15403
15556
|
if (dragRafRef.current) cancelAnimationFrame(dragRafRef.current);
|
|
15404
15557
|
};
|
|
15405
15558
|
}, []);
|
|
15406
|
-
const handleFabPointerDown =
|
|
15559
|
+
const handleFabPointerDown = useCallback14((e) => {
|
|
15407
15560
|
if (disabled || isMobile) return;
|
|
15408
15561
|
dragCleanupRef.current?.();
|
|
15409
15562
|
const el = e.currentTarget;
|
|
@@ -15585,7 +15738,7 @@ var useDragResize = (options) => {
|
|
|
15585
15738
|
}, [disabled, isMobile]);
|
|
15586
15739
|
const fabCenterX = fabPos.x + FAB_SIZE / 2;
|
|
15587
15740
|
const panelDirection = fabCenterX > vw / 2 ? "left" : "right";
|
|
15588
|
-
const panelPositionStyle =
|
|
15741
|
+
const panelPositionStyle = useMemo7(() => {
|
|
15589
15742
|
if (isMobile || disabled) return {};
|
|
15590
15743
|
const fabIsTop = fabPos.y < vh / 2;
|
|
15591
15744
|
let panelBottom;
|
|
@@ -15617,7 +15770,7 @@ var useDragResize = (options) => {
|
|
|
15617
15770
|
borderRadius: "var(--floating-panel-radius, 16px)"
|
|
15618
15771
|
};
|
|
15619
15772
|
}, [isMobile, disabled, fabPos.x, fabPos.y, panelSize.width, panelSize.height, vw, vh, panelDirection, minHeight]);
|
|
15620
|
-
const handleResizePointerDown =
|
|
15773
|
+
const handleResizePointerDown = useCallback14((edge, e) => {
|
|
15621
15774
|
if (disabled || isMobile) return;
|
|
15622
15775
|
e.preventDefault();
|
|
15623
15776
|
e.stopPropagation();
|
|
@@ -15633,7 +15786,7 @@ var useDragResize = (options) => {
|
|
|
15633
15786
|
};
|
|
15634
15787
|
setIsResizing(true);
|
|
15635
15788
|
}, [disabled, isMobile]);
|
|
15636
|
-
const handleResizePointerMove =
|
|
15789
|
+
const handleResizePointerMove = useCallback14((e) => {
|
|
15637
15790
|
if (!resizeStartRef.current) return;
|
|
15638
15791
|
const { startX, startY, width, height, edge, fabY } = resizeStartRef.current;
|
|
15639
15792
|
const dx = e.clientX - startX;
|
|
@@ -15657,7 +15810,7 @@ var useDragResize = (options) => {
|
|
|
15657
15810
|
newHeight = Math.max(minHeight, Math.min(maxH, newHeight));
|
|
15658
15811
|
setPanelSize({ width: newWidth, height: newHeight });
|
|
15659
15812
|
}, [minWidth, maxWidth, minHeight]);
|
|
15660
|
-
const handleResizePointerUp =
|
|
15813
|
+
const handleResizePointerUp = useCallback14((e) => {
|
|
15661
15814
|
if (!resizeStartRef.current) return;
|
|
15662
15815
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
15663
15816
|
resizeStartRef.current = null;
|
|
@@ -15730,7 +15883,7 @@ var useFloatingWidget = (options) => {
|
|
|
15730
15883
|
} = options || {};
|
|
15731
15884
|
const [isOpen, setIsOpen] = useState22(defaultOpen);
|
|
15732
15885
|
const [activeTab, setActiveTab] = useState22(defaultTab);
|
|
15733
|
-
const panelRef =
|
|
15886
|
+
const panelRef = useRef14(null);
|
|
15734
15887
|
const dragResize = useDragResize({
|
|
15735
15888
|
initialPosition: position,
|
|
15736
15889
|
initialWidth: width,
|
|
@@ -15742,9 +15895,9 @@ var useFloatingWidget = (options) => {
|
|
|
15742
15895
|
maxWidth,
|
|
15743
15896
|
minHeight
|
|
15744
15897
|
});
|
|
15745
|
-
const onOpenRef =
|
|
15746
|
-
const onCloseRef =
|
|
15747
|
-
const onTabChangeRef =
|
|
15898
|
+
const onOpenRef = useRef14(onOpen);
|
|
15899
|
+
const onCloseRef = useRef14(onClose);
|
|
15900
|
+
const onTabChangeRef = useRef14(onTabChange);
|
|
15748
15901
|
useEffect14(() => {
|
|
15749
15902
|
onOpenRef.current = onOpen;
|
|
15750
15903
|
}, [onOpen]);
|
|
@@ -15754,15 +15907,15 @@ var useFloatingWidget = (options) => {
|
|
|
15754
15907
|
useEffect14(() => {
|
|
15755
15908
|
onTabChangeRef.current = onTabChange;
|
|
15756
15909
|
}, [onTabChange]);
|
|
15757
|
-
const open =
|
|
15910
|
+
const open = useCallback15(() => {
|
|
15758
15911
|
setIsOpen(true);
|
|
15759
15912
|
onOpenRef.current?.();
|
|
15760
15913
|
}, []);
|
|
15761
|
-
const close =
|
|
15914
|
+
const close = useCallback15(() => {
|
|
15762
15915
|
setIsOpen(false);
|
|
15763
15916
|
onCloseRef.current?.();
|
|
15764
15917
|
}, []);
|
|
15765
|
-
const toggle =
|
|
15918
|
+
const toggle = useCallback15(() => {
|
|
15766
15919
|
setIsOpen((prev) => {
|
|
15767
15920
|
const next = !prev;
|
|
15768
15921
|
if (next) onOpenRef.current?.();
|
|
@@ -15770,11 +15923,11 @@ var useFloatingWidget = (options) => {
|
|
|
15770
15923
|
return next;
|
|
15771
15924
|
});
|
|
15772
15925
|
}, []);
|
|
15773
|
-
const setTab =
|
|
15926
|
+
const setTab = useCallback15((tabKey) => {
|
|
15774
15927
|
setActiveTab(tabKey);
|
|
15775
15928
|
onTabChangeRef.current?.(tabKey);
|
|
15776
15929
|
}, []);
|
|
15777
|
-
const handleFabInteraction =
|
|
15930
|
+
const handleFabInteraction = useCallback15(() => {
|
|
15778
15931
|
if (dragResize.isDragging) return;
|
|
15779
15932
|
toggle();
|
|
15780
15933
|
}, [dragResize.isDragging, toggle]);
|
|
@@ -15804,10 +15957,10 @@ var useFloatingWidget = (options) => {
|
|
|
15804
15957
|
};
|
|
15805
15958
|
|
|
15806
15959
|
// src/react/components/floating/FloatingFab.tsx
|
|
15807
|
-
import { useRef as
|
|
15960
|
+
import { useRef as useRef16, useState as useState24, useEffect as useEffect16 } from "react";
|
|
15808
15961
|
|
|
15809
15962
|
// src/react/components/floating/DevDiveCharacter.tsx
|
|
15810
|
-
import { useState as useState23, useEffect as useEffect15, useRef as
|
|
15963
|
+
import { useState as useState23, useEffect as useEffect15, useRef as useRef15, useCallback as useCallback16 } from "react";
|
|
15811
15964
|
import { Fragment as Fragment10, jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
15812
15965
|
var THEMES = {
|
|
15813
15966
|
dark: { body: "#2ecc71", stroke: "#27ae60", highlight: "#3ddc84", face: "#1a1a2e", eyeLight: "#fff" },
|
|
@@ -15838,11 +15991,11 @@ var DevDiveFabCharacter = ({
|
|
|
15838
15991
|
const [isWinking, setIsWinking] = useState23(false);
|
|
15839
15992
|
const [isCatMouth, setIsCatMouth] = useState23(false);
|
|
15840
15993
|
const [isEntering, setIsEntering] = useState23(true);
|
|
15841
|
-
const svgRef =
|
|
15842
|
-
const cleanupRef =
|
|
15843
|
-
const lastActivityRef =
|
|
15844
|
-
const isSleepyRef =
|
|
15845
|
-
const markActivity =
|
|
15994
|
+
const svgRef = useRef15(null);
|
|
15995
|
+
const cleanupRef = useRef15(null);
|
|
15996
|
+
const lastActivityRef = useRef15(Date.now());
|
|
15997
|
+
const isSleepyRef = useRef15(false);
|
|
15998
|
+
const markActivity = useCallback16(() => {
|
|
15846
15999
|
lastActivityRef.current = Date.now();
|
|
15847
16000
|
if (isSleepyRef.current) {
|
|
15848
16001
|
isSleepyRef.current = false;
|
|
@@ -15962,7 +16115,7 @@ var DevDiveFabCharacter = ({
|
|
|
15962
16115
|
}, 5e3);
|
|
15963
16116
|
return () => clearInterval(check);
|
|
15964
16117
|
}, [isOpen, isTalking, isDizzy]);
|
|
15965
|
-
const prevCompleteRef =
|
|
16118
|
+
const prevCompleteRef = useRef15(false);
|
|
15966
16119
|
useEffect15(() => {
|
|
15967
16120
|
const wasComplete = prevCompleteRef.current;
|
|
15968
16121
|
prevCompleteRef.current = isComplete;
|
|
@@ -16163,7 +16316,7 @@ var FloatingFab = ({
|
|
|
16163
16316
|
}) => {
|
|
16164
16317
|
const isRight = position.includes("right");
|
|
16165
16318
|
const isBottom = position.includes("bottom");
|
|
16166
|
-
const fabRef =
|
|
16319
|
+
const fabRef = useRef16(null);
|
|
16167
16320
|
const isCharacterMode = !icon;
|
|
16168
16321
|
const [bubbleOnRight, setBubbleOnRight] = useState24(!isRight);
|
|
16169
16322
|
const posLeft = positionStyle?.left;
|
|
@@ -16178,11 +16331,11 @@ var FloatingFab = ({
|
|
|
16178
16331
|
}, [isRight, posLeft, posTop]);
|
|
16179
16332
|
const [bubbleText, setBubbleText] = useState24(null);
|
|
16180
16333
|
const [bubbleExiting, setBubbleExiting] = useState24(false);
|
|
16181
|
-
const bubbleTextRef =
|
|
16334
|
+
const bubbleTextRef = useRef16(bubbleText);
|
|
16182
16335
|
bubbleTextRef.current = bubbleText;
|
|
16183
16336
|
const [displayText, setDisplayText] = useState24(null);
|
|
16184
16337
|
const [isTyping, setIsTyping] = useState24(false);
|
|
16185
|
-
const typingTimerRef =
|
|
16338
|
+
const typingTimerRef = useRef16(null);
|
|
16186
16339
|
useEffect16(() => {
|
|
16187
16340
|
if (notification) {
|
|
16188
16341
|
setBubbleText(notification);
|
|
@@ -16219,7 +16372,7 @@ var FloatingFab = ({
|
|
|
16219
16372
|
}, 300);
|
|
16220
16373
|
return () => clearTimeout(timer);
|
|
16221
16374
|
}, [notification]);
|
|
16222
|
-
const notifContentRef =
|
|
16375
|
+
const notifContentRef = useRef16(null);
|
|
16223
16376
|
const [needsMarquee, setNeedsMarquee] = useState24(false);
|
|
16224
16377
|
useEffect16(() => {
|
|
16225
16378
|
if (isTyping || !notification) {
|
|
@@ -16363,7 +16516,7 @@ var FloatingFab = ({
|
|
|
16363
16516
|
};
|
|
16364
16517
|
|
|
16365
16518
|
// src/react/components/floating/FloatingPanel.tsx
|
|
16366
|
-
import { useState as useState25, useEffect as useEffect17, useRef as
|
|
16519
|
+
import { useState as useState25, useEffect as useEffect17, useRef as useRef17 } from "react";
|
|
16367
16520
|
import { jsxs as jsxs25 } from "react/jsx-runtime";
|
|
16368
16521
|
var FloatingPanel = ({
|
|
16369
16522
|
isOpen,
|
|
@@ -16392,7 +16545,7 @@ var FloatingPanel = ({
|
|
|
16392
16545
|
}, []);
|
|
16393
16546
|
const [shouldRender, setShouldRender] = useState25(isOpen);
|
|
16394
16547
|
const [isVisible, setIsVisible] = useState25(isOpen);
|
|
16395
|
-
const rafRef =
|
|
16548
|
+
const rafRef = useRef17(0);
|
|
16396
16549
|
useEffect17(() => {
|
|
16397
16550
|
if (isOpen) {
|
|
16398
16551
|
setShouldRender(true);
|
|
@@ -16569,10 +16722,10 @@ var FloatingTabBar = ({
|
|
|
16569
16722
|
};
|
|
16570
16723
|
|
|
16571
16724
|
// src/react/components/floating/CompactChatView.tsx
|
|
16572
|
-
import { useState as useState27, useCallback as
|
|
16725
|
+
import { useState as useState27, useCallback as useCallback17 } from "react";
|
|
16573
16726
|
|
|
16574
16727
|
// src/react/components/floating/CompactSessionMenu.tsx
|
|
16575
|
-
import { useState as useState26, useRef as
|
|
16728
|
+
import { useState as useState26, useRef as useRef18, useEffect as useEffect18 } from "react";
|
|
16576
16729
|
import { jsx as jsx27, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
16577
16730
|
var CompactSessionMenu = ({
|
|
16578
16731
|
sessions,
|
|
@@ -16584,9 +16737,9 @@ var CompactSessionMenu = ({
|
|
|
16584
16737
|
onClose,
|
|
16585
16738
|
isLoading = false
|
|
16586
16739
|
}) => {
|
|
16587
|
-
const menuRef =
|
|
16588
|
-
const inputRef =
|
|
16589
|
-
const onCloseRef =
|
|
16740
|
+
const menuRef = useRef18(null);
|
|
16741
|
+
const inputRef = useRef18(null);
|
|
16742
|
+
const onCloseRef = useRef18(onClose);
|
|
16590
16743
|
onCloseRef.current = onClose;
|
|
16591
16744
|
const [editingId, setEditingId] = useState26(null);
|
|
16592
16745
|
const [editingTitle, setEditingTitle] = useState26("");
|
|
@@ -16915,15 +17068,15 @@ var CompactChatView = ({
|
|
|
16915
17068
|
setInput(choice.text);
|
|
16916
17069
|
};
|
|
16917
17070
|
const [showSessionMenu, setShowSessionMenu] = useState27(false);
|
|
16918
|
-
const handleSessionSelect =
|
|
17071
|
+
const handleSessionSelect = useCallback17((id) => {
|
|
16919
17072
|
selectSession(id);
|
|
16920
17073
|
setShowSessionMenu(false);
|
|
16921
17074
|
}, [selectSession]);
|
|
16922
|
-
const handleNewSession =
|
|
17075
|
+
const handleNewSession = useCallback17(() => {
|
|
16923
17076
|
newSession();
|
|
16924
17077
|
setShowSessionMenu(false);
|
|
16925
17078
|
}, [newSession]);
|
|
16926
|
-
const handleCloseMenu =
|
|
17079
|
+
const handleCloseMenu = useCallback17(() => {
|
|
16927
17080
|
setShowSessionMenu(false);
|
|
16928
17081
|
}, []);
|
|
16929
17082
|
const greeting = personalization?.userProfile?.nickname ? `${personalization.userProfile.nickname}\uB2D8, \uBB34\uC5C7\uC774\uB4E0 \uBB3C\uC5B4\uBCF4\uC138\uC694` : "\uBB34\uC5C7\uC774\uB4E0 \uBB3C\uC5B4\uBCF4\uC138\uC694";
|
|
@@ -17243,11 +17396,11 @@ var ChatFloatingWidget = ({
|
|
|
17243
17396
|
maxWidth,
|
|
17244
17397
|
minHeight
|
|
17245
17398
|
});
|
|
17246
|
-
const notifObj =
|
|
17399
|
+
const notifObj = useMemo8(() => {
|
|
17247
17400
|
if (!notification) return null;
|
|
17248
17401
|
return typeof notification === "string" ? { text: notification } : notification;
|
|
17249
17402
|
}, [notification]);
|
|
17250
|
-
const handleFabClick =
|
|
17403
|
+
const handleFabClick = useCallback18(() => {
|
|
17251
17404
|
if (notifObj?.onClick) {
|
|
17252
17405
|
notifObj.onClick();
|
|
17253
17406
|
if (!isOpen) handleFabInteraction();
|
|
@@ -17261,7 +17414,7 @@ var ChatFloatingWidget = ({
|
|
|
17261
17414
|
}
|
|
17262
17415
|
handleFabInteraction();
|
|
17263
17416
|
}, [notifObj, isOpen, handleFabInteraction, setTab, tabs]);
|
|
17264
|
-
const allTabs =
|
|
17417
|
+
const allTabs = useMemo8(() => {
|
|
17265
17418
|
const chatTab = {
|
|
17266
17419
|
key: "chat",
|
|
17267
17420
|
label: "\uCC44\uD305",
|
|
@@ -17276,7 +17429,7 @@ var ChatFloatingWidget = ({
|
|
|
17276
17429
|
return [chatTab, ...customTabs];
|
|
17277
17430
|
}, [tabs]);
|
|
17278
17431
|
const [unreadBadge, setUnreadBadge] = useState28(0);
|
|
17279
|
-
const seenMessageIdsRef =
|
|
17432
|
+
const seenMessageIdsRef = useRef19(/* @__PURE__ */ new Set());
|
|
17280
17433
|
useEffect19(() => {
|
|
17281
17434
|
if (seenMessageIdsRef.current.size === 0 && chatState.messages.length > 0) {
|
|
17282
17435
|
for (const m of chatState.messages) {
|
|
@@ -17309,11 +17462,11 @@ var ChatFloatingWidget = ({
|
|
|
17309
17462
|
}
|
|
17310
17463
|
}
|
|
17311
17464
|
}, [isOpen, chatState.messages]);
|
|
17312
|
-
const totalBadge =
|
|
17465
|
+
const totalBadge = useMemo8(() => {
|
|
17313
17466
|
return tabs.reduce((sum, t) => sum + (t.badge || 0), 0) + unreadBadge;
|
|
17314
17467
|
}, [tabs, unreadBadge]);
|
|
17315
|
-
const isTalking =
|
|
17316
|
-
const prevLoadingRef =
|
|
17468
|
+
const isTalking = useMemo8(() => !!notification || chatState.isLoading, [notification, chatState.isLoading]);
|
|
17469
|
+
const prevLoadingRef = useRef19(chatState.isLoading);
|
|
17317
17470
|
const [isComplete, setIsComplete] = useState28(false);
|
|
17318
17471
|
useEffect19(() => {
|
|
17319
17472
|
const wasLoading = prevLoadingRef.current;
|
|
@@ -17431,7 +17584,7 @@ var ChatFloatingWidget = ({
|
|
|
17431
17584
|
};
|
|
17432
17585
|
|
|
17433
17586
|
// src/react/hooks/useDeepResearch.ts
|
|
17434
|
-
import { useState as useState29, useCallback as
|
|
17587
|
+
import { useState as useState29, useCallback as useCallback19, useRef as useRef20 } from "react";
|
|
17435
17588
|
var REPORT_GENERATION_PROMPT2 = `\uB2F9\uC2E0\uC740 \uB9AC\uC11C\uCE58 \uBCF4\uACE0\uC11C \uC791\uC131 \uC804\uBB38\uAC00\uC785\uB2C8\uB2E4.
|
|
17436
17589
|
|
|
17437
17590
|
<collected_sources>
|
|
@@ -17484,8 +17637,8 @@ var useDeepResearch = (options) => {
|
|
|
17484
17637
|
const { onWebSearch, onExtractContent, apiEndpoint, apiKey, model, provider } = options;
|
|
17485
17638
|
const [isResearching, setIsResearching] = useState29(false);
|
|
17486
17639
|
const [progress, setProgress] = useState29(null);
|
|
17487
|
-
const abortControllerRef =
|
|
17488
|
-
const callLLM2 =
|
|
17640
|
+
const abortControllerRef = useRef20(null);
|
|
17641
|
+
const callLLM2 = useCallback19(
|
|
17489
17642
|
async (prompt, stream = false) => {
|
|
17490
17643
|
const response = await fetch(apiEndpoint, {
|
|
17491
17644
|
method: "POST",
|
|
@@ -17512,7 +17665,7 @@ var useDeepResearch = (options) => {
|
|
|
17512
17665
|
},
|
|
17513
17666
|
[apiEndpoint, apiKey, model, provider]
|
|
17514
17667
|
);
|
|
17515
|
-
const analyzeQuery2 =
|
|
17668
|
+
const analyzeQuery2 = useCallback19(
|
|
17516
17669
|
async (query) => {
|
|
17517
17670
|
const prompt = QUERY_ANALYSIS_PROMPT2.replace("{question}", query);
|
|
17518
17671
|
const response = await callLLM2(prompt);
|
|
@@ -17531,7 +17684,7 @@ var useDeepResearch = (options) => {
|
|
|
17531
17684
|
},
|
|
17532
17685
|
[callLLM2]
|
|
17533
17686
|
);
|
|
17534
|
-
const runSubAgent2 =
|
|
17687
|
+
const runSubAgent2 = useCallback19(
|
|
17535
17688
|
async (topic, queries, agentId, updateProgress) => {
|
|
17536
17689
|
updateProgress({ status: "searching", searchCount: 0, resultsCount: 0 });
|
|
17537
17690
|
const allResults = [];
|
|
@@ -17570,7 +17723,7 @@ var useDeepResearch = (options) => {
|
|
|
17570
17723
|
},
|
|
17571
17724
|
[onWebSearch, onExtractContent]
|
|
17572
17725
|
);
|
|
17573
|
-
const generateReport2 =
|
|
17726
|
+
const generateReport2 = useCallback19(
|
|
17574
17727
|
async (query, results, onStreamContent) => {
|
|
17575
17728
|
const allSources = [];
|
|
17576
17729
|
const sourcesForPrompt = [];
|
|
@@ -17628,7 +17781,7 @@ var useDeepResearch = (options) => {
|
|
|
17628
17781
|
},
|
|
17629
17782
|
[callLLM2]
|
|
17630
17783
|
);
|
|
17631
|
-
const runDeepResearch =
|
|
17784
|
+
const runDeepResearch = useCallback19(
|
|
17632
17785
|
async (query, onStreamContent) => {
|
|
17633
17786
|
abortControllerRef.current = new AbortController();
|
|
17634
17787
|
setIsResearching(true);
|
|
@@ -17731,7 +17884,7 @@ var useDeepResearch = (options) => {
|
|
|
17731
17884
|
},
|
|
17732
17885
|
[analyzeQuery2, runSubAgent2, generateReport2]
|
|
17733
17886
|
);
|
|
17734
|
-
const stopResearch =
|
|
17887
|
+
const stopResearch = useCallback19(() => {
|
|
17735
17888
|
abortControllerRef.current?.abort();
|
|
17736
17889
|
setIsResearching(false);
|
|
17737
17890
|
setProgress(null);
|
|
@@ -17744,6 +17897,122 @@ var useDeepResearch = (options) => {
|
|
|
17744
17897
|
};
|
|
17745
17898
|
};
|
|
17746
17899
|
|
|
17900
|
+
// src/react/hooks/useContentParsers.ts
|
|
17901
|
+
import { useCallback as useCallback20 } from "react";
|
|
17902
|
+
var useContentParsers = ({
|
|
17903
|
+
sessionsRef,
|
|
17904
|
+
setSessions
|
|
17905
|
+
}) => {
|
|
17906
|
+
const applyPollParsing = useCallback20(
|
|
17907
|
+
(sessionId, messageId, content, skipParsing) => {
|
|
17908
|
+
const { pollBlock, cleanContent } = parsePollFromContent(content);
|
|
17909
|
+
setSessions(
|
|
17910
|
+
(prev) => prev.map((s) => {
|
|
17911
|
+
if (s.id !== sessionId) return s;
|
|
17912
|
+
return {
|
|
17913
|
+
...s,
|
|
17914
|
+
messages: s.messages.map((m) => {
|
|
17915
|
+
if (m.id !== messageId) return m;
|
|
17916
|
+
if (skipParsing) {
|
|
17917
|
+
return { ...m, content: cleanContent };
|
|
17918
|
+
}
|
|
17919
|
+
if (pollBlock) {
|
|
17920
|
+
return { ...m, content: cleanContent, pollBlock };
|
|
17921
|
+
}
|
|
17922
|
+
return m;
|
|
17923
|
+
})
|
|
17924
|
+
};
|
|
17925
|
+
})
|
|
17926
|
+
);
|
|
17927
|
+
return skipParsing ? null : pollBlock;
|
|
17928
|
+
},
|
|
17929
|
+
[setSessions]
|
|
17930
|
+
);
|
|
17931
|
+
const applyChecklistParsing = useCallback20(
|
|
17932
|
+
(sessionId, messageId, content, skipParsing) => {
|
|
17933
|
+
if (skipParsing) return null;
|
|
17934
|
+
const { checklistBlock, cleanContent } = parseChecklistFromContent(content);
|
|
17935
|
+
if (!checklistBlock) return null;
|
|
17936
|
+
setSessions(
|
|
17937
|
+
(prev) => prev.map((s) => {
|
|
17938
|
+
if (s.id !== sessionId) return s;
|
|
17939
|
+
return {
|
|
17940
|
+
...s,
|
|
17941
|
+
messages: s.messages.map((m) => {
|
|
17942
|
+
if (m.id !== messageId) return m;
|
|
17943
|
+
return { ...m, content: cleanContent, checklistBlock };
|
|
17944
|
+
})
|
|
17945
|
+
};
|
|
17946
|
+
})
|
|
17947
|
+
);
|
|
17948
|
+
return checklistBlock;
|
|
17949
|
+
},
|
|
17950
|
+
[setSessions]
|
|
17951
|
+
);
|
|
17952
|
+
const applyArtifactParsing = useCallback20(
|
|
17953
|
+
(sessionId, messageId, content) => {
|
|
17954
|
+
if (!hasArtifactTag(content)) return false;
|
|
17955
|
+
let found = false;
|
|
17956
|
+
setSessions(
|
|
17957
|
+
(prev) => prev.map((s) => {
|
|
17958
|
+
if (s.id !== sessionId) return s;
|
|
17959
|
+
return {
|
|
17960
|
+
...s,
|
|
17961
|
+
messages: s.messages.map((m) => {
|
|
17962
|
+
if (m.id !== messageId) return m;
|
|
17963
|
+
const { artifacts, cleanContent } = parseArtifactsFromContent(m.content);
|
|
17964
|
+
if (artifacts.length === 0) return m;
|
|
17965
|
+
found = true;
|
|
17966
|
+
const existingParts = m.contentParts || [];
|
|
17967
|
+
const textPart = cleanContent.trim() ? [{ type: "text", content: cleanContent }] : [];
|
|
17968
|
+
return {
|
|
17969
|
+
...m,
|
|
17970
|
+
content: cleanContent,
|
|
17971
|
+
contentParts: [
|
|
17972
|
+
...textPart,
|
|
17973
|
+
...existingParts.filter(
|
|
17974
|
+
(p) => p.type !== "text" && p.type !== "artifact"
|
|
17975
|
+
),
|
|
17976
|
+
...artifacts
|
|
17977
|
+
]
|
|
17978
|
+
};
|
|
17979
|
+
})
|
|
17980
|
+
};
|
|
17981
|
+
})
|
|
17982
|
+
);
|
|
17983
|
+
return found;
|
|
17984
|
+
},
|
|
17985
|
+
[setSessions]
|
|
17986
|
+
);
|
|
17987
|
+
const parseContextReferences = useCallback20(
|
|
17988
|
+
(content, sessionContext) => {
|
|
17989
|
+
const { refs, cleanContent } = parseContextRefs(content);
|
|
17990
|
+
if (refs.length === 0 || !sessionContext) {
|
|
17991
|
+
return { refs, cleanContent, refContents: null };
|
|
17992
|
+
}
|
|
17993
|
+
const assembled = refs.map((refId) => {
|
|
17994
|
+
const item = sessionContext.references.find(
|
|
17995
|
+
(r) => r.id === refId
|
|
17996
|
+
);
|
|
17997
|
+
return item ? `[${item.label || item.skillName}]
|
|
17998
|
+
${item.summary}` : null;
|
|
17999
|
+
}).filter(Boolean).join("\n\n");
|
|
18000
|
+
return {
|
|
18001
|
+
refs,
|
|
18002
|
+
cleanContent,
|
|
18003
|
+
refContents: assembled || null
|
|
18004
|
+
};
|
|
18005
|
+
},
|
|
18006
|
+
[]
|
|
18007
|
+
);
|
|
18008
|
+
return {
|
|
18009
|
+
applyPollParsing,
|
|
18010
|
+
applyChecklistParsing,
|
|
18011
|
+
applyArtifactParsing,
|
|
18012
|
+
parseContextReferences
|
|
18013
|
+
};
|
|
18014
|
+
};
|
|
18015
|
+
|
|
17747
18016
|
// src/react/utils/conversationSearchAdapter.ts
|
|
17748
18017
|
var formatSearchResults = (results) => {
|
|
17749
18018
|
if (results.length === 0) {
|
|
@@ -18271,8 +18540,52 @@ var MemoryPanel = ({
|
|
|
18271
18540
|
}
|
|
18272
18541
|
);
|
|
18273
18542
|
};
|
|
18543
|
+
|
|
18544
|
+
// src/react/utils/retry.ts
|
|
18545
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
18546
|
+
maxRetries: 3,
|
|
18547
|
+
initialDelayMs: 1e3,
|
|
18548
|
+
maxDelayMs: 3e4,
|
|
18549
|
+
backoffMultiplier: 2
|
|
18550
|
+
};
|
|
18551
|
+
var calculateDelay = (attempt, config) => {
|
|
18552
|
+
const exponential = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt);
|
|
18553
|
+
const capped = Math.min(exponential, config.maxDelayMs);
|
|
18554
|
+
const jitter = 0.5 + Math.random() * 0.5;
|
|
18555
|
+
return Math.floor(capped * jitter);
|
|
18556
|
+
};
|
|
18557
|
+
var sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
18558
|
+
if (signal?.aborted) {
|
|
18559
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
18560
|
+
return;
|
|
18561
|
+
}
|
|
18562
|
+
const timer = setTimeout(resolve, ms);
|
|
18563
|
+
signal?.addEventListener("abort", () => {
|
|
18564
|
+
clearTimeout(timer);
|
|
18565
|
+
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
18566
|
+
}, { once: true });
|
|
18567
|
+
});
|
|
18568
|
+
var withRetry = async (fn, config, signal) => {
|
|
18569
|
+
const merged = { ...DEFAULT_RETRY_CONFIG, ...config };
|
|
18570
|
+
let lastError;
|
|
18571
|
+
for (let attempt = 0; attempt <= merged.maxRetries; attempt++) {
|
|
18572
|
+
try {
|
|
18573
|
+
return await fn();
|
|
18574
|
+
} catch (error) {
|
|
18575
|
+
lastError = error;
|
|
18576
|
+
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
|
18577
|
+
if (error instanceof Error && error.name === "AbortError") throw error;
|
|
18578
|
+
if (signal?.aborted) throw error;
|
|
18579
|
+
if (attempt >= merged.maxRetries) throw error;
|
|
18580
|
+
if (error instanceof ChatError && !error.retryable) throw error;
|
|
18581
|
+
await sleep(calculateDelay(attempt, merged), signal);
|
|
18582
|
+
}
|
|
18583
|
+
}
|
|
18584
|
+
throw lastError;
|
|
18585
|
+
};
|
|
18274
18586
|
export {
|
|
18275
18587
|
ArtifactCard,
|
|
18588
|
+
ChatError,
|
|
18276
18589
|
ChatFloatingWidget,
|
|
18277
18590
|
ChatHeader,
|
|
18278
18591
|
ChatInput,
|
|
@@ -18285,6 +18598,7 @@ export {
|
|
|
18285
18598
|
ContentPartRenderer,
|
|
18286
18599
|
DEFAULT_PROJECT_ID,
|
|
18287
18600
|
DEFAULT_PROJECT_TITLE,
|
|
18601
|
+
DEFAULT_RETRY_CONFIG,
|
|
18288
18602
|
DeepResearchProgressUI,
|
|
18289
18603
|
DevDiveAvatar,
|
|
18290
18604
|
DevDiveFabCharacter,
|
|
@@ -18308,20 +18622,28 @@ export {
|
|
|
18308
18622
|
ResizeHandles,
|
|
18309
18623
|
SettingsModal,
|
|
18310
18624
|
SkillProgressUI,
|
|
18625
|
+
classifyFetchError,
|
|
18311
18626
|
convertSkillsToOpenAITools,
|
|
18312
18627
|
convertToolsToSkills,
|
|
18313
18628
|
createAdvancedResearchSkill,
|
|
18314
18629
|
createConversationSearchSkill,
|
|
18315
18630
|
createDeepResearchSkill,
|
|
18631
|
+
createTimeoutError,
|
|
18316
18632
|
migrateSessionsToProjects,
|
|
18633
|
+
parseSSELine,
|
|
18634
|
+
parseSSEResponse,
|
|
18317
18635
|
useChatUI,
|
|
18636
|
+
useChecklist,
|
|
18637
|
+
useContentParsers,
|
|
18318
18638
|
useDeepResearch,
|
|
18319
18639
|
useDragResize,
|
|
18320
18640
|
useFloatingWidget,
|
|
18321
18641
|
useImageError,
|
|
18322
18642
|
useObserver,
|
|
18323
18643
|
useProject,
|
|
18324
|
-
useSkills
|
|
18644
|
+
useSkills,
|
|
18645
|
+
useStreamingFetch,
|
|
18646
|
+
withRetry
|
|
18325
18647
|
};
|
|
18326
18648
|
/**
|
|
18327
18649
|
* @description localStorage 기반 메모리 저장소 어댑터
|