@yourgpt/copilot-sdk 2.1.5-alpha.4 → 2.1.5-alpha.6
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/{MessageTree-CSIHErPK.d.ts → MessageTree-Clhiv_k2.d.ts} +4 -3
- package/dist/{MessageTree-B0JGQOCi.d.cts → MessageTree-Dt9qfJ55.d.cts} +4 -3
- package/dist/{chunk-XUR3IOPX.cjs → chunk-2QLF7XM7.cjs} +1557 -3120
- package/dist/chunk-2QLF7XM7.cjs.map +1 -0
- package/dist/{chunk-NUXLAZOE.cjs → chunk-3ZDRX7J2.cjs} +2 -2
- package/dist/{chunk-NUXLAZOE.cjs.map → chunk-3ZDRX7J2.cjs.map} +1 -1
- package/dist/{chunk-RKGRQRZU.js → chunk-533K2Z7C.js} +4 -4
- package/dist/{chunk-RKGRQRZU.js.map → chunk-533K2Z7C.js.map} +1 -1
- package/dist/chunk-5EGBIQYS.cjs +292 -0
- package/dist/chunk-5EGBIQYS.cjs.map +1 -0
- package/dist/{chunk-3AONOZLY.js → chunk-AIVXGTWS.js} +2 -2
- package/dist/chunk-AIVXGTWS.js.map +1 -0
- package/dist/{chunk-LLM7AHMO.js → chunk-DDZLRCVX.js} +2 -2
- package/dist/{chunk-LLM7AHMO.js.map → chunk-DDZLRCVX.js.map} +1 -1
- package/dist/{chunk-FLZO2FO3.js → chunk-I2XOCFHG.js} +1558 -3071
- package/dist/chunk-I2XOCFHG.js.map +1 -0
- package/dist/{chunk-EEH3L64W.js → chunk-PSNLKMZH.js} +73 -11
- package/dist/chunk-PSNLKMZH.js.map +1 -0
- package/dist/chunk-PURFAD2P.js +2020 -0
- package/dist/chunk-PURFAD2P.js.map +1 -0
- package/dist/chunk-QTGEEBRW.cjs +2077 -0
- package/dist/chunk-QTGEEBRW.cjs.map +1 -0
- package/dist/{chunk-TPB7XED6.cjs → chunk-TPDMBDQX.cjs} +2 -2
- package/dist/chunk-TPDMBDQX.cjs.map +1 -0
- package/dist/chunk-TXQ37MAO.js +287 -0
- package/dist/chunk-TXQ37MAO.js.map +1 -0
- package/dist/{chunk-B4YDIMP3.cjs → chunk-VION33GW.cjs} +92 -30
- package/dist/chunk-VION33GW.cjs.map +1 -0
- package/dist/{chunk-MDS23G2S.cjs → chunk-Y2A6AMGO.cjs} +10 -10
- package/dist/{chunk-MDS23G2S.cjs.map → chunk-Y2A6AMGO.cjs.map} +1 -1
- package/dist/core/index.cjs +93 -93
- package/dist/core/index.d.cts +3 -3
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +5 -5
- package/dist/experimental/index.cjs +644 -0
- package/dist/experimental/index.cjs.map +1 -0
- package/dist/experimental/index.d.cts +924 -0
- package/dist/experimental/index.d.ts +924 -0
- package/dist/experimental/index.js +611 -0
- package/dist/experimental/index.js.map +1 -0
- package/dist/{index-D7169xuR.d.ts → index-D8zza1Q8.d.ts} +1 -1
- package/dist/{index-CzJB8Ddo.d.cts → index-DCVjTdIZ.d.cts} +1 -1
- package/dist/mcp/index.d.cts +3 -3
- package/dist/mcp/index.d.ts +3 -3
- package/dist/react/index.cjs +140 -123
- package/dist/react/index.d.cts +378 -10
- package/dist/react/index.d.ts +378 -10
- package/dist/react/index.js +7 -6
- package/dist/styles.css +45 -0
- package/dist/tools/anthropic/index.cjs +3 -3
- package/dist/tools/anthropic/index.d.cts +1 -1
- package/dist/tools/anthropic/index.d.ts +1 -1
- package/dist/tools/anthropic/index.js +2 -2
- package/dist/tools/brave/index.cjs +6 -6
- package/dist/tools/brave/index.d.cts +1 -1
- package/dist/tools/brave/index.d.ts +1 -1
- package/dist/tools/brave/index.js +3 -3
- package/dist/tools/exa/index.cjs +6 -6
- package/dist/tools/exa/index.d.cts +1 -1
- package/dist/tools/exa/index.d.ts +1 -1
- package/dist/tools/exa/index.js +3 -3
- package/dist/tools/google/index.cjs +6 -6
- package/dist/tools/google/index.d.cts +1 -1
- package/dist/tools/google/index.d.ts +1 -1
- package/dist/tools/google/index.js +4 -4
- package/dist/tools/openai/index.cjs +6 -6
- package/dist/tools/openai/index.d.cts +1 -1
- package/dist/tools/openai/index.d.ts +1 -1
- package/dist/tools/openai/index.js +3 -3
- package/dist/tools/searxng/index.cjs +6 -6
- package/dist/tools/searxng/index.d.cts +1 -1
- package/dist/tools/searxng/index.d.ts +1 -1
- package/dist/tools/searxng/index.js +3 -3
- package/dist/tools/serper/index.cjs +6 -6
- package/dist/tools/serper/index.d.cts +1 -1
- package/dist/tools/serper/index.d.ts +1 -1
- package/dist/tools/serper/index.js +3 -3
- package/dist/tools/tavily/index.cjs +6 -6
- package/dist/tools/tavily/index.d.cts +1 -1
- package/dist/tools/tavily/index.d.ts +1 -1
- package/dist/tools/tavily/index.js +3 -3
- package/dist/tools/web-search/index.cjs +7 -7
- package/dist/tools/web-search/index.d.cts +2 -2
- package/dist/tools/web-search/index.d.ts +2 -2
- package/dist/tools/web-search/index.js +4 -4
- package/dist/{tools-tmksfhUo.d.cts → tools-DcS6Aeao.d.cts} +7 -3
- package/dist/{tools-tmksfhUo.d.ts → tools-DcS6Aeao.d.ts} +7 -3
- package/dist/{types-BqwW3Baj.d.ts → types-BUYni9B8.d.ts} +1 -1
- package/dist/{types-BLw7mxtW.d.cts → types-Cvg4DUoc.d.cts} +1 -1
- package/dist/ui/index.cjs +1297 -596
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +136 -9
- package/dist/ui/index.d.ts +136 -9
- package/dist/ui/index.js +1128 -436
- package/dist/ui/index.js.map +1 -1
- package/package.json +6 -1
- package/dist/chunk-3AONOZLY.js.map +0 -1
- package/dist/chunk-B4YDIMP3.cjs.map +0 -1
- package/dist/chunk-EEH3L64W.js.map +0 -1
- package/dist/chunk-FLZO2FO3.js.map +0 -1
- package/dist/chunk-TPB7XED6.cjs.map +0 -1
- package/dist/chunk-XUR3IOPX.cjs.map +0 -1
|
@@ -0,0 +1,2020 @@
|
|
|
1
|
+
import { useCopilot, useSkillContext, AbstractChat, ReactChatState } from './chunk-I2XOCFHG.js';
|
|
2
|
+
import { ThreadManager, isConsoleCaptureActive, startConsoleCapture, isNetworkCaptureActive, startNetworkCapture, stopConsoleCapture, stopNetworkCapture, isScreenshotSupported, captureScreenshot, getConsoleLogs, getNetworkRequests, clearConsoleLogs, clearNetworkRequests, formatLogsForAI, formatRequestsForAI, detectIntent, streamSSE, zodObjectToInputSchema } from './chunk-PSNLKMZH.js';
|
|
3
|
+
import { createContext, useEffect, useState, useRef, useCallback, useMemo, useSyncExternalStore, useReducer, useContext } from 'react';
|
|
4
|
+
import * as z from 'zod';
|
|
5
|
+
|
|
6
|
+
function useAIActions(actions) {
|
|
7
|
+
const { registerAction, unregisterAction } = useCopilot();
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
for (const action of actions) {
|
|
10
|
+
registerAction(action);
|
|
11
|
+
}
|
|
12
|
+
return () => {
|
|
13
|
+
for (const action of actions) {
|
|
14
|
+
unregisterAction(action.name);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
}, [actions, registerAction, unregisterAction]);
|
|
18
|
+
}
|
|
19
|
+
function useAIAction(action) {
|
|
20
|
+
useAIActions([action]);
|
|
21
|
+
}
|
|
22
|
+
function useAITools(options = {}) {
|
|
23
|
+
const {
|
|
24
|
+
screenshot = false,
|
|
25
|
+
console: consoleCapture = false,
|
|
26
|
+
network = false,
|
|
27
|
+
requireConsent = true,
|
|
28
|
+
screenshotOptions,
|
|
29
|
+
consoleOptions,
|
|
30
|
+
networkOptions,
|
|
31
|
+
onConsentRequest,
|
|
32
|
+
autoStart = true
|
|
33
|
+
} = options;
|
|
34
|
+
const [isEnabled] = useState(screenshot || consoleCapture || network);
|
|
35
|
+
const [activeCaptures, setActiveCaptures] = useState({
|
|
36
|
+
console: false,
|
|
37
|
+
network: false
|
|
38
|
+
});
|
|
39
|
+
const [pendingConsent, setPendingConsent] = useState(null);
|
|
40
|
+
const consentResolverRef = useRef(null);
|
|
41
|
+
const rememberedConsentRef = useRef(/* @__PURE__ */ new Set());
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!autoStart || !isEnabled) return;
|
|
44
|
+
if (consoleCapture && !isConsoleCaptureActive()) {
|
|
45
|
+
startConsoleCapture(consoleOptions);
|
|
46
|
+
setActiveCaptures((prev) => ({ ...prev, console: true }));
|
|
47
|
+
}
|
|
48
|
+
if (network && !isNetworkCaptureActive()) {
|
|
49
|
+
startNetworkCapture(networkOptions);
|
|
50
|
+
setActiveCaptures((prev) => ({ ...prev, network: true }));
|
|
51
|
+
}
|
|
52
|
+
return () => {
|
|
53
|
+
stopConsoleCapture();
|
|
54
|
+
stopNetworkCapture();
|
|
55
|
+
};
|
|
56
|
+
}, [
|
|
57
|
+
autoStart,
|
|
58
|
+
isEnabled,
|
|
59
|
+
consoleCapture,
|
|
60
|
+
network,
|
|
61
|
+
consoleOptions,
|
|
62
|
+
networkOptions
|
|
63
|
+
]);
|
|
64
|
+
const captureScreenshotFn = useCallback(
|
|
65
|
+
async (opts) => {
|
|
66
|
+
if (!screenshot) {
|
|
67
|
+
throw new Error("Screenshot capture is not enabled");
|
|
68
|
+
}
|
|
69
|
+
if (!isScreenshotSupported()) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"Screenshot capture is not supported in this environment"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return captureScreenshot({ ...screenshotOptions, ...opts });
|
|
75
|
+
},
|
|
76
|
+
[screenshot, screenshotOptions]
|
|
77
|
+
);
|
|
78
|
+
const getConsoleLogsFn = useCallback(
|
|
79
|
+
(opts) => {
|
|
80
|
+
if (!consoleCapture) {
|
|
81
|
+
return { logs: [], totalCaptured: 0 };
|
|
82
|
+
}
|
|
83
|
+
return getConsoleLogs({ ...consoleOptions, ...opts });
|
|
84
|
+
},
|
|
85
|
+
[consoleCapture, consoleOptions]
|
|
86
|
+
);
|
|
87
|
+
const getNetworkRequestsFn = useCallback(
|
|
88
|
+
(opts) => {
|
|
89
|
+
if (!network) {
|
|
90
|
+
return { requests: [], totalCaptured: 0 };
|
|
91
|
+
}
|
|
92
|
+
return getNetworkRequests({ ...networkOptions, ...opts });
|
|
93
|
+
},
|
|
94
|
+
[network, networkOptions]
|
|
95
|
+
);
|
|
96
|
+
const requestConsent = useCallback(
|
|
97
|
+
async (tools, reason = "") => {
|
|
98
|
+
const enabledTools = tools.filter((tool2) => {
|
|
99
|
+
if (tool2 === "screenshot") return screenshot;
|
|
100
|
+
if (tool2 === "console") return consoleCapture;
|
|
101
|
+
if (tool2 === "network") return network;
|
|
102
|
+
return false;
|
|
103
|
+
});
|
|
104
|
+
if (enabledTools.length === 0) {
|
|
105
|
+
return { approved: [], denied: [] };
|
|
106
|
+
}
|
|
107
|
+
if (!requireConsent) {
|
|
108
|
+
return { approved: enabledTools, denied: [] };
|
|
109
|
+
}
|
|
110
|
+
const needsConsent = enabledTools.filter(
|
|
111
|
+
(tool2) => !rememberedConsentRef.current.has(tool2)
|
|
112
|
+
);
|
|
113
|
+
if (needsConsent.length === 0) {
|
|
114
|
+
return { approved: enabledTools, denied: [] };
|
|
115
|
+
}
|
|
116
|
+
const request = {
|
|
117
|
+
tools: needsConsent,
|
|
118
|
+
reason,
|
|
119
|
+
keywords: []
|
|
120
|
+
};
|
|
121
|
+
if (onConsentRequest) {
|
|
122
|
+
const response = await onConsentRequest(request);
|
|
123
|
+
if (response.remember) {
|
|
124
|
+
response.approved.forEach(
|
|
125
|
+
(tool2) => rememberedConsentRef.current.add(tool2)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return response;
|
|
129
|
+
}
|
|
130
|
+
return new Promise((resolve) => {
|
|
131
|
+
setPendingConsent(request);
|
|
132
|
+
consentResolverRef.current = (response) => {
|
|
133
|
+
if (response.remember) {
|
|
134
|
+
response.approved.forEach(
|
|
135
|
+
(tool2) => rememberedConsentRef.current.add(tool2)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
resolve(response);
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
[screenshot, consoleCapture, network, requireConsent, onConsentRequest]
|
|
143
|
+
);
|
|
144
|
+
const respondToConsent = useCallback((response) => {
|
|
145
|
+
if (consentResolverRef.current) {
|
|
146
|
+
consentResolverRef.current(response);
|
|
147
|
+
consentResolverRef.current = null;
|
|
148
|
+
}
|
|
149
|
+
setPendingConsent(null);
|
|
150
|
+
}, []);
|
|
151
|
+
const captureContext = useCallback(
|
|
152
|
+
async (tools) => {
|
|
153
|
+
const toolsToCapture = tools || ["screenshot", "console", "network"];
|
|
154
|
+
const context = {
|
|
155
|
+
timestamp: Date.now()
|
|
156
|
+
};
|
|
157
|
+
const captures = [];
|
|
158
|
+
if (toolsToCapture.includes("screenshot") && screenshot) {
|
|
159
|
+
captures.push(
|
|
160
|
+
captureScreenshotFn().then((result) => {
|
|
161
|
+
context.screenshot = result;
|
|
162
|
+
}).catch(() => {
|
|
163
|
+
})
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
if (toolsToCapture.includes("console") && consoleCapture) {
|
|
167
|
+
context.consoleLogs = getConsoleLogsFn();
|
|
168
|
+
}
|
|
169
|
+
if (toolsToCapture.includes("network") && network) {
|
|
170
|
+
context.networkRequests = getNetworkRequestsFn();
|
|
171
|
+
}
|
|
172
|
+
await Promise.all(captures);
|
|
173
|
+
return context;
|
|
174
|
+
},
|
|
175
|
+
[
|
|
176
|
+
screenshot,
|
|
177
|
+
consoleCapture,
|
|
178
|
+
network,
|
|
179
|
+
captureScreenshotFn,
|
|
180
|
+
getConsoleLogsFn,
|
|
181
|
+
getNetworkRequestsFn
|
|
182
|
+
]
|
|
183
|
+
);
|
|
184
|
+
const startCapturing = useCallback(() => {
|
|
185
|
+
if (consoleCapture && !isConsoleCaptureActive()) {
|
|
186
|
+
startConsoleCapture(consoleOptions);
|
|
187
|
+
setActiveCaptures((prev) => ({ ...prev, console: true }));
|
|
188
|
+
}
|
|
189
|
+
if (network && !isNetworkCaptureActive()) {
|
|
190
|
+
startNetworkCapture(networkOptions);
|
|
191
|
+
setActiveCaptures((prev) => ({ ...prev, network: true }));
|
|
192
|
+
}
|
|
193
|
+
}, [consoleCapture, network, consoleOptions, networkOptions]);
|
|
194
|
+
const stopCapturing = useCallback(() => {
|
|
195
|
+
stopConsoleCapture();
|
|
196
|
+
stopNetworkCapture();
|
|
197
|
+
setActiveCaptures({ console: false, network: false });
|
|
198
|
+
}, []);
|
|
199
|
+
const clearCaptured = useCallback(() => {
|
|
200
|
+
clearConsoleLogs();
|
|
201
|
+
clearNetworkRequests();
|
|
202
|
+
}, []);
|
|
203
|
+
const formatForAI = useCallback((context) => {
|
|
204
|
+
const parts = [];
|
|
205
|
+
if (context.screenshot) {
|
|
206
|
+
parts.push(
|
|
207
|
+
`Screenshot captured (${context.screenshot.width}x${context.screenshot.height}, ${context.screenshot.format})`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
if (context.consoleLogs && context.consoleLogs.logs.length > 0) {
|
|
211
|
+
parts.push(formatLogsForAI(context.consoleLogs.logs));
|
|
212
|
+
}
|
|
213
|
+
if (context.networkRequests && context.networkRequests.requests.length > 0) {
|
|
214
|
+
parts.push(formatRequestsForAI(context.networkRequests.requests));
|
|
215
|
+
}
|
|
216
|
+
return parts.length > 0 ? parts.join("\n\n---\n\n") : "No context captured.";
|
|
217
|
+
}, []);
|
|
218
|
+
const detectIntentFn = useCallback(
|
|
219
|
+
(message) => {
|
|
220
|
+
const result = detectIntent(message);
|
|
221
|
+
result.suggestedTools = result.suggestedTools.filter((tool2) => {
|
|
222
|
+
if (tool2 === "screenshot") return screenshot;
|
|
223
|
+
if (tool2 === "console") return consoleCapture;
|
|
224
|
+
if (tool2 === "network") return network;
|
|
225
|
+
return false;
|
|
226
|
+
});
|
|
227
|
+
return result;
|
|
228
|
+
},
|
|
229
|
+
[screenshot, consoleCapture, network]
|
|
230
|
+
);
|
|
231
|
+
return useMemo(
|
|
232
|
+
() => ({
|
|
233
|
+
isEnabled,
|
|
234
|
+
activeCaptures,
|
|
235
|
+
captureScreenshot: captureScreenshotFn,
|
|
236
|
+
getConsoleLogs: getConsoleLogsFn,
|
|
237
|
+
getNetworkRequests: getNetworkRequestsFn,
|
|
238
|
+
captureContext,
|
|
239
|
+
detectIntent: detectIntentFn,
|
|
240
|
+
requestConsent,
|
|
241
|
+
startCapturing,
|
|
242
|
+
stopCapturing,
|
|
243
|
+
clearCaptured,
|
|
244
|
+
formatForAI,
|
|
245
|
+
pendingConsent,
|
|
246
|
+
respondToConsent
|
|
247
|
+
}),
|
|
248
|
+
[
|
|
249
|
+
isEnabled,
|
|
250
|
+
activeCaptures,
|
|
251
|
+
captureScreenshotFn,
|
|
252
|
+
getConsoleLogsFn,
|
|
253
|
+
getNetworkRequestsFn,
|
|
254
|
+
captureContext,
|
|
255
|
+
detectIntentFn,
|
|
256
|
+
requestConsent,
|
|
257
|
+
startCapturing,
|
|
258
|
+
stopCapturing,
|
|
259
|
+
clearCaptured,
|
|
260
|
+
formatForAI,
|
|
261
|
+
pendingConsent,
|
|
262
|
+
respondToConsent
|
|
263
|
+
]
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
function convertZodSchema(schema, _toolName) {
|
|
267
|
+
try {
|
|
268
|
+
const zodWithJsonSchema = z;
|
|
269
|
+
if (typeof zodWithJsonSchema.toJSONSchema === "function") {
|
|
270
|
+
const jsonSchema = zodWithJsonSchema.toJSONSchema(
|
|
271
|
+
schema
|
|
272
|
+
);
|
|
273
|
+
if (jsonSchema.type === "object") {
|
|
274
|
+
return {
|
|
275
|
+
type: "object",
|
|
276
|
+
properties: jsonSchema.properties || {},
|
|
277
|
+
required: jsonSchema.required
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
} catch {
|
|
282
|
+
}
|
|
283
|
+
return zodObjectToInputSchema(schema);
|
|
284
|
+
}
|
|
285
|
+
function useToolWithSchema(config, dependencies = []) {
|
|
286
|
+
const { registerTool, unregisterTool } = useCopilot();
|
|
287
|
+
const configRef = useRef(config);
|
|
288
|
+
configRef.current = config;
|
|
289
|
+
const inputSchema = useMemo(() => {
|
|
290
|
+
try {
|
|
291
|
+
return convertZodSchema(config.schema, config.name);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.warn(
|
|
294
|
+
`[useToolWithSchema] Failed to convert Zod schema for tool "${config.name}"`,
|
|
295
|
+
error
|
|
296
|
+
);
|
|
297
|
+
return {
|
|
298
|
+
type: "object",
|
|
299
|
+
properties: {}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}, [config.schema, config.name]);
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
const tool2 = {
|
|
305
|
+
name: config.name,
|
|
306
|
+
description: config.description,
|
|
307
|
+
location: "client",
|
|
308
|
+
inputSchema,
|
|
309
|
+
handler: async (params, context) => {
|
|
310
|
+
return configRef.current.handler(params, context);
|
|
311
|
+
},
|
|
312
|
+
render: config.render,
|
|
313
|
+
available: config.available ?? true
|
|
314
|
+
};
|
|
315
|
+
registerTool(tool2);
|
|
316
|
+
return () => {
|
|
317
|
+
unregisterTool(config.name);
|
|
318
|
+
};
|
|
319
|
+
}, [config.name, inputSchema, ...dependencies]);
|
|
320
|
+
}
|
|
321
|
+
function useToolsWithSchema(tools, dependencies = []) {
|
|
322
|
+
const { registerTool, unregisterTool } = useCopilot();
|
|
323
|
+
const toolsRef = useRef(tools);
|
|
324
|
+
toolsRef.current = tools;
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
const toolNames = [];
|
|
327
|
+
for (const config of tools) {
|
|
328
|
+
let inputSchema;
|
|
329
|
+
try {
|
|
330
|
+
inputSchema = convertZodSchema(config.schema, config.name);
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.warn(
|
|
333
|
+
`[useToolsWithSchema] Failed to convert Zod schema for tool "${config.name}"`,
|
|
334
|
+
error
|
|
335
|
+
);
|
|
336
|
+
inputSchema = { type: "object", properties: {} };
|
|
337
|
+
}
|
|
338
|
+
const tool2 = {
|
|
339
|
+
name: config.name,
|
|
340
|
+
description: config.description,
|
|
341
|
+
location: "client",
|
|
342
|
+
inputSchema,
|
|
343
|
+
handler: async (params, context) => {
|
|
344
|
+
const currentConfig = toolsRef.current.find(
|
|
345
|
+
(t) => t.name === config.name
|
|
346
|
+
);
|
|
347
|
+
if (currentConfig) {
|
|
348
|
+
return currentConfig.handler(params, context);
|
|
349
|
+
}
|
|
350
|
+
return { success: false, error: "Tool handler not found" };
|
|
351
|
+
},
|
|
352
|
+
available: config.available ?? true
|
|
353
|
+
};
|
|
354
|
+
registerTool(tool2);
|
|
355
|
+
toolNames.push(config.name);
|
|
356
|
+
}
|
|
357
|
+
return () => {
|
|
358
|
+
for (const name of toolNames) {
|
|
359
|
+
unregisterTool(name);
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}, [tools.map((t) => t.name).join(","), ...dependencies]);
|
|
363
|
+
}
|
|
364
|
+
var CopilotContext = createContext(null);
|
|
365
|
+
function useCopilotContext() {
|
|
366
|
+
const context = useContext(CopilotContext);
|
|
367
|
+
if (!context) {
|
|
368
|
+
throw new Error("useCopilotContext must be used within a CopilotProvider");
|
|
369
|
+
}
|
|
370
|
+
return context;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// src/react/hooks/useToolExecutor.ts
|
|
374
|
+
function useToolExecutor() {
|
|
375
|
+
const {
|
|
376
|
+
registeredTools,
|
|
377
|
+
config,
|
|
378
|
+
chat,
|
|
379
|
+
addToolExecution,
|
|
380
|
+
updateToolExecution
|
|
381
|
+
} = useCopilotContext();
|
|
382
|
+
const toolsRef = useRef(registeredTools);
|
|
383
|
+
toolsRef.current = registeredTools;
|
|
384
|
+
const executeTool = useCallback(
|
|
385
|
+
async (toolCall) => {
|
|
386
|
+
const tool2 = toolsRef.current.find((t) => t.name === toolCall.name);
|
|
387
|
+
if (!tool2) {
|
|
388
|
+
return {
|
|
389
|
+
success: false,
|
|
390
|
+
error: `Unknown tool: ${toolCall.name}`
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
if (!tool2.handler) {
|
|
394
|
+
return {
|
|
395
|
+
success: false,
|
|
396
|
+
error: `Tool "${toolCall.name}" has no handler`
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
const execution = {
|
|
400
|
+
id: toolCall.id,
|
|
401
|
+
name: toolCall.name,
|
|
402
|
+
args: toolCall.input,
|
|
403
|
+
status: "executing",
|
|
404
|
+
timestamp: Date.now(),
|
|
405
|
+
approvalStatus: "none",
|
|
406
|
+
hidden: tool2.hidden
|
|
407
|
+
};
|
|
408
|
+
addToolExecution?.(execution);
|
|
409
|
+
try {
|
|
410
|
+
const startTime = Date.now();
|
|
411
|
+
const result = await tool2.handler(toolCall.input);
|
|
412
|
+
const duration = Date.now() - startTime;
|
|
413
|
+
updateToolExecution?.(toolCall.id, {
|
|
414
|
+
status: result.success ? "completed" : "error",
|
|
415
|
+
result,
|
|
416
|
+
error: result.error,
|
|
417
|
+
duration
|
|
418
|
+
});
|
|
419
|
+
return result;
|
|
420
|
+
} catch (error) {
|
|
421
|
+
const errorMessage = error instanceof Error ? error.message : "Tool execution failed";
|
|
422
|
+
updateToolExecution?.(toolCall.id, {
|
|
423
|
+
status: "error",
|
|
424
|
+
error: errorMessage
|
|
425
|
+
});
|
|
426
|
+
return {
|
|
427
|
+
success: false,
|
|
428
|
+
error: errorMessage
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
[addToolExecution, updateToolExecution]
|
|
433
|
+
);
|
|
434
|
+
const sendToolResult = useCallback(
|
|
435
|
+
async (toolCallId, result) => {
|
|
436
|
+
const runtimeUrl = config.runtimeUrl || config.cloud?.endpoint;
|
|
437
|
+
if (!runtimeUrl) {
|
|
438
|
+
console.warn(
|
|
439
|
+
"[useToolExecutor] No runtime URL configured, cannot send tool result"
|
|
440
|
+
);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const baseUrl = runtimeUrl.replace(/\/chat\/?$/, "");
|
|
444
|
+
try {
|
|
445
|
+
const response = await fetch(`${baseUrl}/tool-result`, {
|
|
446
|
+
method: "POST",
|
|
447
|
+
headers: {
|
|
448
|
+
"Content-Type": "application/json"
|
|
449
|
+
},
|
|
450
|
+
body: JSON.stringify({
|
|
451
|
+
threadId: chat.threadId || "default",
|
|
452
|
+
toolCallId,
|
|
453
|
+
result
|
|
454
|
+
})
|
|
455
|
+
});
|
|
456
|
+
if (!response.ok) {
|
|
457
|
+
console.error(
|
|
458
|
+
"[useToolExecutor] Failed to send tool result:",
|
|
459
|
+
await response.text()
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
} catch (error) {
|
|
463
|
+
console.error("[useToolExecutor] Error sending tool result:", error);
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
[config.runtimeUrl, config.cloud?.endpoint, chat.threadId]
|
|
467
|
+
);
|
|
468
|
+
const getTool = useCallback((name) => {
|
|
469
|
+
return toolsRef.current.find((t) => t.name === name);
|
|
470
|
+
}, []);
|
|
471
|
+
const hasTool = useCallback((name) => {
|
|
472
|
+
return toolsRef.current.some((t) => t.name === name);
|
|
473
|
+
}, []);
|
|
474
|
+
return {
|
|
475
|
+
executeTool,
|
|
476
|
+
sendToolResult,
|
|
477
|
+
getTool,
|
|
478
|
+
hasTool
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
function useSuggestions(options = {}) {
|
|
482
|
+
const {
|
|
483
|
+
count = 3,
|
|
484
|
+
context,
|
|
485
|
+
suggestions: staticSuggestions,
|
|
486
|
+
autoRefresh = true
|
|
487
|
+
} = options;
|
|
488
|
+
const { chat, actions, config } = useCopilotContext();
|
|
489
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
490
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
491
|
+
const normalizedStatic = useMemo(
|
|
492
|
+
() => staticSuggestions?.map((s) => typeof s === "string" ? { text: s } : s),
|
|
493
|
+
[staticSuggestions]
|
|
494
|
+
);
|
|
495
|
+
const refresh = useCallback(async () => {
|
|
496
|
+
if (normalizedStatic) {
|
|
497
|
+
setSuggestions(normalizedStatic.slice(0, count));
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (!config.cloud) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
setIsLoading(true);
|
|
504
|
+
try {
|
|
505
|
+
const endpoint = config.cloud.endpoint || "https://api.yourgpt.ai/v1";
|
|
506
|
+
const response = await fetch(`${endpoint}/suggestions`, {
|
|
507
|
+
method: "POST",
|
|
508
|
+
headers: {
|
|
509
|
+
"Content-Type": "application/json",
|
|
510
|
+
Authorization: `Bearer ${config.cloud.apiKey}`
|
|
511
|
+
},
|
|
512
|
+
body: JSON.stringify({
|
|
513
|
+
botId: config.cloud.botId,
|
|
514
|
+
count,
|
|
515
|
+
context,
|
|
516
|
+
messages: chat.messages.slice(-5)
|
|
517
|
+
// Last 5 messages for context
|
|
518
|
+
})
|
|
519
|
+
});
|
|
520
|
+
if (response.ok) {
|
|
521
|
+
const data = await response.json();
|
|
522
|
+
setSuggestions(
|
|
523
|
+
data.suggestions.map(
|
|
524
|
+
(s) => typeof s === "string" ? { text: s } : s
|
|
525
|
+
)
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
} catch (error) {
|
|
529
|
+
console.error("Failed to fetch suggestions:", error);
|
|
530
|
+
} finally {
|
|
531
|
+
setIsLoading(false);
|
|
532
|
+
}
|
|
533
|
+
}, [config.cloud, count, context, chat.messages, normalizedStatic]);
|
|
534
|
+
const select = useCallback(
|
|
535
|
+
(suggestion) => {
|
|
536
|
+
const text = typeof suggestion === "string" ? suggestion : suggestion.text;
|
|
537
|
+
actions.sendMessage(text);
|
|
538
|
+
},
|
|
539
|
+
[actions]
|
|
540
|
+
);
|
|
541
|
+
useEffect(() => {
|
|
542
|
+
if (autoRefresh && chat.messages.length === 0) {
|
|
543
|
+
refresh();
|
|
544
|
+
}
|
|
545
|
+
}, [autoRefresh, chat.messages.length, refresh]);
|
|
546
|
+
return {
|
|
547
|
+
suggestions: normalizedStatic?.slice(0, count) || suggestions,
|
|
548
|
+
isLoading,
|
|
549
|
+
refresh,
|
|
550
|
+
select
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function useAgent(options) {
|
|
554
|
+
const { name, initialState = {}, onStateChange } = options;
|
|
555
|
+
const { config } = useCopilotContext();
|
|
556
|
+
const [state, setStateInternal] = useState(initialState);
|
|
557
|
+
const [isRunning, setIsRunning] = useState(false);
|
|
558
|
+
const [nodeName, setNodeName] = useState(null);
|
|
559
|
+
const [error, setError] = useState(null);
|
|
560
|
+
const abortControllerRef = useRef(null);
|
|
561
|
+
const getEndpoint = useCallback(() => {
|
|
562
|
+
if (config.cloud) {
|
|
563
|
+
return `${config.cloud.endpoint || "https://api.yourgpt.ai/v1"}/agents/${name}`;
|
|
564
|
+
}
|
|
565
|
+
return `${config.runtimeUrl || "/api"}/agents/${name}`;
|
|
566
|
+
}, [config, name]);
|
|
567
|
+
const start = useCallback(
|
|
568
|
+
async (input) => {
|
|
569
|
+
setIsRunning(true);
|
|
570
|
+
setError(null);
|
|
571
|
+
abortControllerRef.current = new AbortController();
|
|
572
|
+
try {
|
|
573
|
+
const endpoint = getEndpoint();
|
|
574
|
+
const headers = {
|
|
575
|
+
"Content-Type": "application/json"
|
|
576
|
+
};
|
|
577
|
+
if (config.cloud?.apiKey) {
|
|
578
|
+
headers["Authorization"] = `Bearer ${config.cloud.apiKey}`;
|
|
579
|
+
}
|
|
580
|
+
const response = await fetch(`${endpoint}/start`, {
|
|
581
|
+
method: "POST",
|
|
582
|
+
headers,
|
|
583
|
+
body: JSON.stringify({
|
|
584
|
+
input: typeof input === "string" ? { message: input } : input,
|
|
585
|
+
state
|
|
586
|
+
}),
|
|
587
|
+
signal: abortControllerRef.current.signal
|
|
588
|
+
});
|
|
589
|
+
if (!response.ok) {
|
|
590
|
+
throw new Error(`Agent error: ${response.status}`);
|
|
591
|
+
}
|
|
592
|
+
for await (const event of streamSSE(response)) {
|
|
593
|
+
handleAgentEvent(event);
|
|
594
|
+
}
|
|
595
|
+
} catch (err) {
|
|
596
|
+
if (err.name !== "AbortError") {
|
|
597
|
+
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
598
|
+
}
|
|
599
|
+
} finally {
|
|
600
|
+
setIsRunning(false);
|
|
601
|
+
abortControllerRef.current = null;
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
[config, getEndpoint, state]
|
|
605
|
+
);
|
|
606
|
+
const handleAgentEvent = useCallback(
|
|
607
|
+
(event) => {
|
|
608
|
+
if (event.type === "error") {
|
|
609
|
+
setError(new Error(event.message));
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if ("state" in event && event.state) {
|
|
613
|
+
setStateInternal(event.state);
|
|
614
|
+
onStateChange?.(event.state);
|
|
615
|
+
}
|
|
616
|
+
if ("nodeName" in event && event.nodeName) {
|
|
617
|
+
setNodeName(event.nodeName);
|
|
618
|
+
}
|
|
619
|
+
},
|
|
620
|
+
[onStateChange]
|
|
621
|
+
);
|
|
622
|
+
const stop = useCallback(() => {
|
|
623
|
+
abortControllerRef.current?.abort();
|
|
624
|
+
}, []);
|
|
625
|
+
const setState = useCallback(
|
|
626
|
+
(partialState) => {
|
|
627
|
+
setStateInternal((prev) => {
|
|
628
|
+
const newState = { ...prev, ...partialState };
|
|
629
|
+
onStateChange?.(newState);
|
|
630
|
+
return newState;
|
|
631
|
+
});
|
|
632
|
+
},
|
|
633
|
+
[onStateChange]
|
|
634
|
+
);
|
|
635
|
+
useEffect(() => {
|
|
636
|
+
return () => {
|
|
637
|
+
abortControllerRef.current?.abort();
|
|
638
|
+
};
|
|
639
|
+
}, []);
|
|
640
|
+
return {
|
|
641
|
+
state,
|
|
642
|
+
isRunning,
|
|
643
|
+
nodeName,
|
|
644
|
+
start,
|
|
645
|
+
stop,
|
|
646
|
+
setState,
|
|
647
|
+
error
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// src/react/utils/knowledge-base.ts
|
|
652
|
+
var KNOWLEDGE_BASE_API = "https://api.yourgpt.ai/chatbot/v1/searchIndexDocument";
|
|
653
|
+
async function searchKnowledgeBase(query, config) {
|
|
654
|
+
try {
|
|
655
|
+
const response = await fetch(KNOWLEDGE_BASE_API, {
|
|
656
|
+
method: "POST",
|
|
657
|
+
headers: {
|
|
658
|
+
accept: "*/*",
|
|
659
|
+
"content-type": "application/json",
|
|
660
|
+
authorization: `Bearer ${config.token}`
|
|
661
|
+
},
|
|
662
|
+
body: JSON.stringify({
|
|
663
|
+
project_uid: config.projectUid,
|
|
664
|
+
query,
|
|
665
|
+
page: 1,
|
|
666
|
+
limit: String(config.limit || 10),
|
|
667
|
+
app_id: config.appId || "1"
|
|
668
|
+
})
|
|
669
|
+
});
|
|
670
|
+
if (!response.ok) {
|
|
671
|
+
return {
|
|
672
|
+
success: false,
|
|
673
|
+
results: [],
|
|
674
|
+
error: `API error: ${response.status} ${response.statusText}`
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
const data = await response.json();
|
|
678
|
+
const results = (data.data || data.results || []).map((item) => ({
|
|
679
|
+
id: item.id || item._id || String(Math.random()),
|
|
680
|
+
title: item.title || item.name || void 0,
|
|
681
|
+
content: item.content || item.text || item.snippet || "",
|
|
682
|
+
score: item.score || item.relevance || void 0,
|
|
683
|
+
url: item.url || item.source_url || void 0,
|
|
684
|
+
metadata: item.metadata || {}
|
|
685
|
+
}));
|
|
686
|
+
return {
|
|
687
|
+
success: true,
|
|
688
|
+
results,
|
|
689
|
+
total: data.total || results.length,
|
|
690
|
+
page: data.page || 1
|
|
691
|
+
};
|
|
692
|
+
} catch (error) {
|
|
693
|
+
return {
|
|
694
|
+
success: false,
|
|
695
|
+
results: [],
|
|
696
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
function formatKnowledgeResultsForAI(results) {
|
|
701
|
+
if (results.length === 0) {
|
|
702
|
+
return "No relevant documents found in the knowledge base.";
|
|
703
|
+
}
|
|
704
|
+
return results.map((result, index) => {
|
|
705
|
+
const parts = [`[${index + 1}]`];
|
|
706
|
+
if (result.title) parts.push(`**${result.title}**`);
|
|
707
|
+
parts.push(result.content);
|
|
708
|
+
if (result.url) parts.push(`Source: ${result.url}`);
|
|
709
|
+
return parts.join("\n");
|
|
710
|
+
}).join("\n\n---\n\n");
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// src/react/hooks/useKnowledgeBase.ts
|
|
714
|
+
function useKnowledgeBase(config) {
|
|
715
|
+
const { registerTool, unregisterTool } = useCopilot();
|
|
716
|
+
const configRef = useRef(config);
|
|
717
|
+
configRef.current = config;
|
|
718
|
+
const handleSearch = useCallback(
|
|
719
|
+
async (params) => {
|
|
720
|
+
const query = params.query;
|
|
721
|
+
if (!query) {
|
|
722
|
+
return {
|
|
723
|
+
success: false,
|
|
724
|
+
error: "Query is required"
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
const currentConfig = configRef.current;
|
|
728
|
+
const kbConfig = {
|
|
729
|
+
projectUid: currentConfig.projectUid,
|
|
730
|
+
token: currentConfig.token,
|
|
731
|
+
appId: currentConfig.appId,
|
|
732
|
+
limit: currentConfig.limit || 5
|
|
733
|
+
};
|
|
734
|
+
const response = await searchKnowledgeBase(
|
|
735
|
+
query,
|
|
736
|
+
kbConfig
|
|
737
|
+
);
|
|
738
|
+
if (!response.success) {
|
|
739
|
+
return {
|
|
740
|
+
success: false,
|
|
741
|
+
error: response.error || "Knowledge base search failed"
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
const formattedResults = formatKnowledgeResultsForAI(response.results);
|
|
745
|
+
return {
|
|
746
|
+
success: true,
|
|
747
|
+
message: formattedResults,
|
|
748
|
+
data: {
|
|
749
|
+
resultCount: response.results.length,
|
|
750
|
+
total: response.total
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
},
|
|
754
|
+
[]
|
|
755
|
+
);
|
|
756
|
+
useEffect(() => {
|
|
757
|
+
if (config.enabled === false) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
registerTool({
|
|
761
|
+
name: "search_knowledge",
|
|
762
|
+
description: "Search the knowledge base for relevant information about the product, documentation, or company. Use this to answer questions about features, pricing, policies, guides, or any factual information.",
|
|
763
|
+
location: "client",
|
|
764
|
+
inputSchema: {
|
|
765
|
+
type: "object",
|
|
766
|
+
properties: {
|
|
767
|
+
query: {
|
|
768
|
+
type: "string",
|
|
769
|
+
description: "The search query to find relevant information in the knowledge base"
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
required: ["query"]
|
|
773
|
+
},
|
|
774
|
+
handler: handleSearch
|
|
775
|
+
});
|
|
776
|
+
return () => {
|
|
777
|
+
unregisterTool("search_knowledge");
|
|
778
|
+
};
|
|
779
|
+
}, [
|
|
780
|
+
config.enabled,
|
|
781
|
+
config.projectUid,
|
|
782
|
+
config.token,
|
|
783
|
+
registerTool,
|
|
784
|
+
unregisterTool,
|
|
785
|
+
handleSearch
|
|
786
|
+
]);
|
|
787
|
+
}
|
|
788
|
+
var DEFAULT_CAPABILITIES = {
|
|
789
|
+
supportsVision: false,
|
|
790
|
+
supportsTools: true,
|
|
791
|
+
supportsThinking: false,
|
|
792
|
+
supportsStreaming: true,
|
|
793
|
+
supportsPDF: false,
|
|
794
|
+
supportsAudio: false,
|
|
795
|
+
supportsVideo: false,
|
|
796
|
+
maxTokens: 8192,
|
|
797
|
+
supportedImageTypes: [],
|
|
798
|
+
supportsJsonMode: false,
|
|
799
|
+
supportsSystemMessages: true
|
|
800
|
+
};
|
|
801
|
+
function useCapabilities() {
|
|
802
|
+
const { config } = useCopilotContext();
|
|
803
|
+
const [capabilities, setCapabilities] = useState(DEFAULT_CAPABILITIES);
|
|
804
|
+
const [provider, setProvider] = useState("unknown");
|
|
805
|
+
const [model, setModel] = useState("unknown");
|
|
806
|
+
const [supportedModels, setSupportedModels] = useState([]);
|
|
807
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
808
|
+
const [error, setError] = useState(null);
|
|
809
|
+
const capabilitiesUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/chat\/?$/, "/capabilities") : null;
|
|
810
|
+
const fetchCapabilities = useCallback(async () => {
|
|
811
|
+
if (!capabilitiesUrl) {
|
|
812
|
+
setIsLoading(false);
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
try {
|
|
816
|
+
setIsLoading(true);
|
|
817
|
+
setError(null);
|
|
818
|
+
const response = await fetch(capabilitiesUrl);
|
|
819
|
+
if (!response.ok) {
|
|
820
|
+
throw new Error(`Failed to fetch capabilities: ${response.status}`);
|
|
821
|
+
}
|
|
822
|
+
const data = await response.json();
|
|
823
|
+
setCapabilities(data.capabilities);
|
|
824
|
+
setProvider(data.provider);
|
|
825
|
+
setModel(data.model);
|
|
826
|
+
setSupportedModels(data.supportedModels);
|
|
827
|
+
} catch (err) {
|
|
828
|
+
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
829
|
+
} finally {
|
|
830
|
+
setIsLoading(false);
|
|
831
|
+
}
|
|
832
|
+
}, [capabilitiesUrl]);
|
|
833
|
+
useEffect(() => {
|
|
834
|
+
fetchCapabilities();
|
|
835
|
+
}, [fetchCapabilities]);
|
|
836
|
+
return {
|
|
837
|
+
/** Current model capabilities */
|
|
838
|
+
capabilities,
|
|
839
|
+
/** Current provider name */
|
|
840
|
+
provider,
|
|
841
|
+
/** Current model ID */
|
|
842
|
+
model,
|
|
843
|
+
/** List of supported models for current provider */
|
|
844
|
+
supportedModels,
|
|
845
|
+
/** Whether capabilities are being loaded */
|
|
846
|
+
isLoading,
|
|
847
|
+
/** Error if fetch failed */
|
|
848
|
+
error,
|
|
849
|
+
/** Refetch capabilities */
|
|
850
|
+
refetch: fetchCapabilities
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function useFeatureSupport(feature) {
|
|
854
|
+
const { capabilities } = useCapabilities();
|
|
855
|
+
return capabilities[feature] ?? false;
|
|
856
|
+
}
|
|
857
|
+
function useSupportedMediaTypes() {
|
|
858
|
+
const { capabilities } = useCapabilities();
|
|
859
|
+
return {
|
|
860
|
+
/** Supported image MIME types */
|
|
861
|
+
imageTypes: capabilities.supportedImageTypes || [],
|
|
862
|
+
/** Supported audio MIME types */
|
|
863
|
+
audioTypes: capabilities.supportedAudioTypes || [],
|
|
864
|
+
/** Supported video MIME types */
|
|
865
|
+
videoTypes: capabilities.supportedVideoTypes || [],
|
|
866
|
+
/** Whether any image types are supported */
|
|
867
|
+
hasImageSupport: (capabilities.supportedImageTypes?.length ?? 0) > 0,
|
|
868
|
+
/** Whether any audio types are supported */
|
|
869
|
+
hasAudioSupport: (capabilities.supportedAudioTypes?.length ?? 0) > 0,
|
|
870
|
+
/** Whether any video types are supported */
|
|
871
|
+
hasVideoSupport: (capabilities.supportedVideoTypes?.length ?? 0) > 0
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
function useDevLogger() {
|
|
875
|
+
const ctx = useCopilotContext();
|
|
876
|
+
return useMemo(() => {
|
|
877
|
+
const toolExecutions = (ctx.agentLoop?.toolExecutions || []).map(
|
|
878
|
+
(exec) => ({
|
|
879
|
+
id: exec.id,
|
|
880
|
+
name: exec.name,
|
|
881
|
+
status: exec.status,
|
|
882
|
+
approvalStatus: exec.approvalStatus || "not_required"
|
|
883
|
+
})
|
|
884
|
+
);
|
|
885
|
+
const pendingApprovalsCount = ctx.pendingApprovals?.length || 0;
|
|
886
|
+
const registeredTools = (ctx.registeredTools || []).map((tool2) => ({
|
|
887
|
+
name: tool2.name,
|
|
888
|
+
location: tool2.location || "client"
|
|
889
|
+
}));
|
|
890
|
+
const registeredActions = (ctx.registeredActions || []).map((action) => ({
|
|
891
|
+
name: action.name
|
|
892
|
+
}));
|
|
893
|
+
const storedPermissions = (ctx.storedPermissions || []).map((p) => ({
|
|
894
|
+
toolName: p.toolName,
|
|
895
|
+
level: p.level
|
|
896
|
+
}));
|
|
897
|
+
return {
|
|
898
|
+
chat: {
|
|
899
|
+
isLoading: ctx.chat?.isLoading || false,
|
|
900
|
+
messageCount: ctx.chat?.messages?.length || 0,
|
|
901
|
+
threadId: ctx.chat?.threadId || "none",
|
|
902
|
+
error: ctx.chat?.error?.message || null
|
|
903
|
+
},
|
|
904
|
+
tools: {
|
|
905
|
+
isEnabled: !!ctx.toolsConfig,
|
|
906
|
+
isCapturing: ctx.tools?.isCapturing || false,
|
|
907
|
+
pendingConsent: !!ctx.tools?.pendingConsent
|
|
908
|
+
},
|
|
909
|
+
agentLoop: {
|
|
910
|
+
toolExecutions,
|
|
911
|
+
pendingApprovals: pendingApprovalsCount,
|
|
912
|
+
iteration: ctx.agentLoop?.iteration || 0,
|
|
913
|
+
maxIterations: ctx.agentLoop?.maxIterations || 10
|
|
914
|
+
},
|
|
915
|
+
registered: {
|
|
916
|
+
tools: registeredTools,
|
|
917
|
+
actions: registeredActions,
|
|
918
|
+
contextCount: ctx.contextTree?.length || 0
|
|
919
|
+
},
|
|
920
|
+
permissions: {
|
|
921
|
+
stored: storedPermissions,
|
|
922
|
+
loaded: ctx.permissionsLoaded || false
|
|
923
|
+
},
|
|
924
|
+
config: {
|
|
925
|
+
runtimeUrl: ctx.config?.runtimeUrl || ctx.config?.cloud?.endpoint || ""
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
}, [
|
|
929
|
+
ctx.chat,
|
|
930
|
+
ctx.tools,
|
|
931
|
+
ctx.toolsConfig,
|
|
932
|
+
ctx.agentLoop,
|
|
933
|
+
ctx.pendingApprovals,
|
|
934
|
+
ctx.registeredTools,
|
|
935
|
+
ctx.registeredActions,
|
|
936
|
+
ctx.contextTree,
|
|
937
|
+
ctx.storedPermissions,
|
|
938
|
+
ctx.permissionsLoaded,
|
|
939
|
+
ctx.config
|
|
940
|
+
]);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// src/react/internal/ReactThreadManagerState.ts
|
|
944
|
+
var ReactThreadManagerState = class {
|
|
945
|
+
constructor(initialThreads) {
|
|
946
|
+
this._threads = [];
|
|
947
|
+
this._currentThreadId = null;
|
|
948
|
+
this._currentThread = null;
|
|
949
|
+
this._loadStatus = "idle";
|
|
950
|
+
this._error = void 0;
|
|
951
|
+
// Callbacks for React subscriptions (useSyncExternalStore)
|
|
952
|
+
this.subscribers = /* @__PURE__ */ new Set();
|
|
953
|
+
// ============================================
|
|
954
|
+
// Subscription (for useSyncExternalStore)
|
|
955
|
+
// ============================================
|
|
956
|
+
/**
|
|
957
|
+
* Subscribe to state changes.
|
|
958
|
+
* Returns an unsubscribe function.
|
|
959
|
+
*
|
|
960
|
+
* @example
|
|
961
|
+
* ```tsx
|
|
962
|
+
* const threads = useSyncExternalStore(
|
|
963
|
+
* state.subscribe,
|
|
964
|
+
* () => state.threads
|
|
965
|
+
* );
|
|
966
|
+
* ```
|
|
967
|
+
*/
|
|
968
|
+
this.subscribe = (callback) => {
|
|
969
|
+
this.subscribers.add(callback);
|
|
970
|
+
return () => {
|
|
971
|
+
this.subscribers.delete(callback);
|
|
972
|
+
};
|
|
973
|
+
};
|
|
974
|
+
if (initialThreads) {
|
|
975
|
+
this._threads = initialThreads;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
// ============================================
|
|
979
|
+
// Getters
|
|
980
|
+
// ============================================
|
|
981
|
+
get threads() {
|
|
982
|
+
return this._threads;
|
|
983
|
+
}
|
|
984
|
+
get currentThreadId() {
|
|
985
|
+
return this._currentThreadId;
|
|
986
|
+
}
|
|
987
|
+
get currentThread() {
|
|
988
|
+
return this._currentThread;
|
|
989
|
+
}
|
|
990
|
+
get loadStatus() {
|
|
991
|
+
return this._loadStatus;
|
|
992
|
+
}
|
|
993
|
+
get error() {
|
|
994
|
+
return this._error;
|
|
995
|
+
}
|
|
996
|
+
// ============================================
|
|
997
|
+
// Setters (trigger reactivity)
|
|
998
|
+
// ============================================
|
|
999
|
+
set threads(value) {
|
|
1000
|
+
this._threads = value;
|
|
1001
|
+
this.notify();
|
|
1002
|
+
}
|
|
1003
|
+
// ============================================
|
|
1004
|
+
// Mutations
|
|
1005
|
+
// ============================================
|
|
1006
|
+
setThreads(threads) {
|
|
1007
|
+
this._threads = threads;
|
|
1008
|
+
this.notify();
|
|
1009
|
+
}
|
|
1010
|
+
setCurrentThread(thread) {
|
|
1011
|
+
this._currentThread = thread;
|
|
1012
|
+
this._currentThreadId = thread?.id ?? null;
|
|
1013
|
+
this.notify();
|
|
1014
|
+
}
|
|
1015
|
+
setCurrentThreadId(id) {
|
|
1016
|
+
this._currentThreadId = id;
|
|
1017
|
+
this.notify();
|
|
1018
|
+
}
|
|
1019
|
+
addThread(thread) {
|
|
1020
|
+
this._threads = [thread, ...this._threads];
|
|
1021
|
+
this.notify();
|
|
1022
|
+
}
|
|
1023
|
+
updateThread(id, updates) {
|
|
1024
|
+
this._threads = this._threads.map(
|
|
1025
|
+
(t) => t.id === id ? { ...t, ...updates } : t
|
|
1026
|
+
);
|
|
1027
|
+
if (updates.updatedAt) {
|
|
1028
|
+
this._threads = [...this._threads].sort(
|
|
1029
|
+
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
if (this._currentThread?.id === id) {
|
|
1033
|
+
this._currentThread = { ...this._currentThread, ...updates };
|
|
1034
|
+
}
|
|
1035
|
+
this.notify();
|
|
1036
|
+
}
|
|
1037
|
+
removeThread(id) {
|
|
1038
|
+
this._threads = this._threads.filter((t) => t.id !== id);
|
|
1039
|
+
if (this._currentThreadId === id) {
|
|
1040
|
+
this._currentThreadId = null;
|
|
1041
|
+
this._currentThread = null;
|
|
1042
|
+
}
|
|
1043
|
+
this.notify();
|
|
1044
|
+
}
|
|
1045
|
+
setLoadStatus(status) {
|
|
1046
|
+
this._loadStatus = status;
|
|
1047
|
+
this.notify();
|
|
1048
|
+
}
|
|
1049
|
+
setError(error) {
|
|
1050
|
+
this._error = error;
|
|
1051
|
+
this.notify();
|
|
1052
|
+
}
|
|
1053
|
+
// ============================================
|
|
1054
|
+
// Snapshots (for useSyncExternalStore)
|
|
1055
|
+
// ============================================
|
|
1056
|
+
getThreadsSnapshot() {
|
|
1057
|
+
return this._threads;
|
|
1058
|
+
}
|
|
1059
|
+
getCurrentThreadSnapshot() {
|
|
1060
|
+
return this._currentThread;
|
|
1061
|
+
}
|
|
1062
|
+
getLoadStatusSnapshot() {
|
|
1063
|
+
return this._loadStatus;
|
|
1064
|
+
}
|
|
1065
|
+
getErrorSnapshot() {
|
|
1066
|
+
return this._error;
|
|
1067
|
+
}
|
|
1068
|
+
// ============================================
|
|
1069
|
+
// Private Methods
|
|
1070
|
+
// ============================================
|
|
1071
|
+
notify() {
|
|
1072
|
+
this.subscribers.forEach((cb) => cb());
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Cleanup subscriptions
|
|
1076
|
+
*/
|
|
1077
|
+
dispose() {
|
|
1078
|
+
this.subscribers.clear();
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
function createReactThreadManagerState(initialThreads) {
|
|
1082
|
+
return new ReactThreadManagerState(initialThreads);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// src/react/internal/ReactThreadManager.ts
|
|
1086
|
+
var _ReactThreadManager = class _ReactThreadManager extends ThreadManager {
|
|
1087
|
+
constructor(config = {}, callbacks = {}) {
|
|
1088
|
+
const reactState = new ReactThreadManagerState();
|
|
1089
|
+
super({ ...config, state: reactState }, callbacks);
|
|
1090
|
+
// ============================================
|
|
1091
|
+
// Subscription Methods (for useSyncExternalStore)
|
|
1092
|
+
// ============================================
|
|
1093
|
+
/**
|
|
1094
|
+
* Subscribe to state changes
|
|
1095
|
+
* Use with useSyncExternalStore
|
|
1096
|
+
*/
|
|
1097
|
+
this.subscribe = (callback) => {
|
|
1098
|
+
return this.state.subscribe(callback);
|
|
1099
|
+
};
|
|
1100
|
+
// ============================================
|
|
1101
|
+
// Snapshot Getters (for useSyncExternalStore)
|
|
1102
|
+
// ============================================
|
|
1103
|
+
/**
|
|
1104
|
+
* Get threads snapshot
|
|
1105
|
+
*/
|
|
1106
|
+
this.getThreadsSnapshot = () => {
|
|
1107
|
+
return this.state.getThreadsSnapshot();
|
|
1108
|
+
};
|
|
1109
|
+
/**
|
|
1110
|
+
* Get current thread snapshot
|
|
1111
|
+
*/
|
|
1112
|
+
this.getCurrentThreadSnapshot = () => {
|
|
1113
|
+
return this.state.getCurrentThreadSnapshot();
|
|
1114
|
+
};
|
|
1115
|
+
/**
|
|
1116
|
+
* Get current thread ID snapshot
|
|
1117
|
+
*/
|
|
1118
|
+
this.getCurrentThreadIdSnapshot = () => {
|
|
1119
|
+
return this.state.currentThreadId;
|
|
1120
|
+
};
|
|
1121
|
+
/**
|
|
1122
|
+
* Get load status snapshot
|
|
1123
|
+
*/
|
|
1124
|
+
this.getLoadStatusSnapshot = () => {
|
|
1125
|
+
return this.state.getLoadStatusSnapshot();
|
|
1126
|
+
};
|
|
1127
|
+
/**
|
|
1128
|
+
* Get error snapshot
|
|
1129
|
+
*/
|
|
1130
|
+
this.getErrorSnapshot = () => {
|
|
1131
|
+
return this.state.getErrorSnapshot();
|
|
1132
|
+
};
|
|
1133
|
+
/**
|
|
1134
|
+
* Get isLoading snapshot
|
|
1135
|
+
*/
|
|
1136
|
+
this.getIsLoadingSnapshot = () => {
|
|
1137
|
+
return this.state.getLoadStatusSnapshot() === "loading";
|
|
1138
|
+
};
|
|
1139
|
+
/**
|
|
1140
|
+
* Get threads snapshot for server (always empty for hydration consistency)
|
|
1141
|
+
*/
|
|
1142
|
+
this.getThreadsServerSnapshot = () => {
|
|
1143
|
+
return _ReactThreadManager.EMPTY_THREADS;
|
|
1144
|
+
};
|
|
1145
|
+
/**
|
|
1146
|
+
* Get current thread snapshot for server (always null)
|
|
1147
|
+
*/
|
|
1148
|
+
this.getCurrentThreadServerSnapshot = () => {
|
|
1149
|
+
return null;
|
|
1150
|
+
};
|
|
1151
|
+
/**
|
|
1152
|
+
* Get current thread ID snapshot for server (always null)
|
|
1153
|
+
*/
|
|
1154
|
+
this.getCurrentThreadIdServerSnapshot = () => {
|
|
1155
|
+
return null;
|
|
1156
|
+
};
|
|
1157
|
+
/**
|
|
1158
|
+
* Get load status snapshot for server (always "idle")
|
|
1159
|
+
*/
|
|
1160
|
+
this.getLoadStatusServerSnapshot = () => {
|
|
1161
|
+
return _ReactThreadManager.IDLE_STATUS;
|
|
1162
|
+
};
|
|
1163
|
+
/**
|
|
1164
|
+
* Get error snapshot for server (always undefined)
|
|
1165
|
+
*/
|
|
1166
|
+
this.getErrorServerSnapshot = () => {
|
|
1167
|
+
return void 0;
|
|
1168
|
+
};
|
|
1169
|
+
/**
|
|
1170
|
+
* Get isLoading snapshot for server (always false)
|
|
1171
|
+
*/
|
|
1172
|
+
this.getIsLoadingServerSnapshot = () => {
|
|
1173
|
+
return false;
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
// ============================================
|
|
1177
|
+
// Cleanup
|
|
1178
|
+
// ============================================
|
|
1179
|
+
/**
|
|
1180
|
+
* Dispose of the manager
|
|
1181
|
+
*/
|
|
1182
|
+
async dispose() {
|
|
1183
|
+
this.state.dispose();
|
|
1184
|
+
await super.dispose();
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
// ============================================
|
|
1188
|
+
// Server Snapshots (for SSR - stable cached values)
|
|
1189
|
+
// ============================================
|
|
1190
|
+
// Cached values for server snapshots (must be stable references)
|
|
1191
|
+
_ReactThreadManager.EMPTY_THREADS = [];
|
|
1192
|
+
_ReactThreadManager.IDLE_STATUS = "idle";
|
|
1193
|
+
var ReactThreadManager = _ReactThreadManager;
|
|
1194
|
+
function createReactThreadManager(config, callbacks) {
|
|
1195
|
+
return new ReactThreadManager(config, callbacks);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
// src/react/hooks/useThreadManager.ts
|
|
1199
|
+
var defaultManager = null;
|
|
1200
|
+
function getDefaultManager() {
|
|
1201
|
+
if (!defaultManager) {
|
|
1202
|
+
defaultManager = createReactThreadManager();
|
|
1203
|
+
}
|
|
1204
|
+
return defaultManager;
|
|
1205
|
+
}
|
|
1206
|
+
var internalManager = null;
|
|
1207
|
+
function getInternalManager(config) {
|
|
1208
|
+
if (!internalManager) {
|
|
1209
|
+
internalManager = createReactThreadManager(
|
|
1210
|
+
{
|
|
1211
|
+
adapter: config.adapter,
|
|
1212
|
+
saveDebounce: config.saveDebounce,
|
|
1213
|
+
autoLoad: config.autoLoad,
|
|
1214
|
+
autoRestoreLastThread: config.autoRestoreLastThread
|
|
1215
|
+
},
|
|
1216
|
+
config.callbacks
|
|
1217
|
+
);
|
|
1218
|
+
}
|
|
1219
|
+
return internalManager;
|
|
1220
|
+
}
|
|
1221
|
+
function useThreadManager(config) {
|
|
1222
|
+
const manager = useMemo(() => {
|
|
1223
|
+
if (!config) {
|
|
1224
|
+
return getDefaultManager();
|
|
1225
|
+
}
|
|
1226
|
+
if (!config.adapter) {
|
|
1227
|
+
return getInternalManager(config);
|
|
1228
|
+
}
|
|
1229
|
+
return createReactThreadManager(
|
|
1230
|
+
{
|
|
1231
|
+
adapter: config.adapter,
|
|
1232
|
+
saveDebounce: config.saveDebounce,
|
|
1233
|
+
autoLoad: config.autoLoad,
|
|
1234
|
+
autoRestoreLastThread: config.autoRestoreLastThread
|
|
1235
|
+
},
|
|
1236
|
+
config.callbacks
|
|
1237
|
+
);
|
|
1238
|
+
}, [
|
|
1239
|
+
config?.adapter,
|
|
1240
|
+
config?.saveDebounce,
|
|
1241
|
+
config?.autoLoad,
|
|
1242
|
+
config?.autoRestoreLastThread
|
|
1243
|
+
// Note: callbacks are intentionally not in deps to avoid recreating manager
|
|
1244
|
+
]);
|
|
1245
|
+
const threads = useSyncExternalStore(
|
|
1246
|
+
manager.subscribe,
|
|
1247
|
+
manager.getThreadsSnapshot,
|
|
1248
|
+
manager.getThreadsServerSnapshot
|
|
1249
|
+
// SSR - always empty array
|
|
1250
|
+
);
|
|
1251
|
+
const currentThread = useSyncExternalStore(
|
|
1252
|
+
manager.subscribe,
|
|
1253
|
+
manager.getCurrentThreadSnapshot,
|
|
1254
|
+
manager.getCurrentThreadServerSnapshot
|
|
1255
|
+
// SSR - always null
|
|
1256
|
+
);
|
|
1257
|
+
const currentThreadId = useSyncExternalStore(
|
|
1258
|
+
manager.subscribe,
|
|
1259
|
+
manager.getCurrentThreadIdSnapshot,
|
|
1260
|
+
manager.getCurrentThreadIdServerSnapshot
|
|
1261
|
+
// SSR - always null
|
|
1262
|
+
);
|
|
1263
|
+
const loadStatus = useSyncExternalStore(
|
|
1264
|
+
manager.subscribe,
|
|
1265
|
+
manager.getLoadStatusSnapshot,
|
|
1266
|
+
manager.getLoadStatusServerSnapshot
|
|
1267
|
+
// SSR - always "idle"
|
|
1268
|
+
);
|
|
1269
|
+
const error = useSyncExternalStore(
|
|
1270
|
+
manager.subscribe,
|
|
1271
|
+
manager.getErrorSnapshot,
|
|
1272
|
+
manager.getErrorServerSnapshot
|
|
1273
|
+
// SSR - always undefined
|
|
1274
|
+
);
|
|
1275
|
+
const isLoading = useSyncExternalStore(
|
|
1276
|
+
manager.subscribe,
|
|
1277
|
+
manager.getIsLoadingSnapshot,
|
|
1278
|
+
manager.getIsLoadingServerSnapshot
|
|
1279
|
+
// SSR - always false
|
|
1280
|
+
);
|
|
1281
|
+
useEffect(() => {
|
|
1282
|
+
return () => {
|
|
1283
|
+
if (config?.adapter && manager !== defaultManager && manager !== internalManager) {
|
|
1284
|
+
manager.dispose();
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
}, [manager, config]);
|
|
1288
|
+
useEffect(() => {
|
|
1289
|
+
const handleBeforeUnload = () => {
|
|
1290
|
+
if (manager.hasPendingChanges) {
|
|
1291
|
+
manager.saveNow().catch(() => {
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
if (typeof window !== "undefined") {
|
|
1296
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
1297
|
+
return () => {
|
|
1298
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
}, [manager]);
|
|
1302
|
+
const createThread = useCallback(
|
|
1303
|
+
(options) => manager.createThread(options),
|
|
1304
|
+
[manager]
|
|
1305
|
+
);
|
|
1306
|
+
const switchThread = useCallback(
|
|
1307
|
+
(id) => manager.switchThread(id),
|
|
1308
|
+
[manager]
|
|
1309
|
+
);
|
|
1310
|
+
const updateCurrentThread = useCallback(
|
|
1311
|
+
(updates) => manager.updateCurrentThread(updates),
|
|
1312
|
+
[manager]
|
|
1313
|
+
);
|
|
1314
|
+
const deleteThread = useCallback(
|
|
1315
|
+
(id) => manager.deleteThread(id),
|
|
1316
|
+
[manager]
|
|
1317
|
+
);
|
|
1318
|
+
const clearCurrentThread = useCallback(
|
|
1319
|
+
() => manager.clearCurrentThread(),
|
|
1320
|
+
[manager]
|
|
1321
|
+
);
|
|
1322
|
+
const refreshThreads = useCallback(() => manager.loadThreads(), [manager]);
|
|
1323
|
+
const saveNow = useCallback(() => manager.saveNow(), [manager]);
|
|
1324
|
+
const clearAllThreads = useCallback(
|
|
1325
|
+
() => manager.clearAllThreads(),
|
|
1326
|
+
[manager]
|
|
1327
|
+
);
|
|
1328
|
+
const messages = useMemo(
|
|
1329
|
+
() => currentThread?.messages ?? [],
|
|
1330
|
+
[currentThread]
|
|
1331
|
+
);
|
|
1332
|
+
const setMessages = useCallback(
|
|
1333
|
+
(newMessages) => updateCurrentThread({ messages: newMessages }),
|
|
1334
|
+
[updateCurrentThread]
|
|
1335
|
+
);
|
|
1336
|
+
const hasPendingChanges = manager.hasPendingChanges;
|
|
1337
|
+
return {
|
|
1338
|
+
// State
|
|
1339
|
+
threads,
|
|
1340
|
+
currentThread,
|
|
1341
|
+
currentThreadId,
|
|
1342
|
+
isLoading,
|
|
1343
|
+
loadStatus,
|
|
1344
|
+
error,
|
|
1345
|
+
// Actions
|
|
1346
|
+
createThread,
|
|
1347
|
+
switchThread,
|
|
1348
|
+
updateCurrentThread,
|
|
1349
|
+
deleteThread,
|
|
1350
|
+
clearCurrentThread,
|
|
1351
|
+
refreshThreads,
|
|
1352
|
+
saveNow,
|
|
1353
|
+
clearAllThreads,
|
|
1354
|
+
// Utilities
|
|
1355
|
+
messages,
|
|
1356
|
+
setMessages,
|
|
1357
|
+
hasPendingChanges
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// src/react/hooks/useThread.ts
|
|
1362
|
+
function useThread() {
|
|
1363
|
+
const { threadId, setActiveThread, renewSession, sessionStatus } = useCopilot();
|
|
1364
|
+
return {
|
|
1365
|
+
threadId,
|
|
1366
|
+
sessionStatus,
|
|
1367
|
+
switchThread: setActiveThread,
|
|
1368
|
+
newThread: () => setActiveThread(null),
|
|
1369
|
+
renewSession
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
function useMCPUIIntents(config = {}) {
|
|
1373
|
+
const {
|
|
1374
|
+
onToolCall,
|
|
1375
|
+
onIntent,
|
|
1376
|
+
onPrompt,
|
|
1377
|
+
onNotify,
|
|
1378
|
+
onLink,
|
|
1379
|
+
requireConsent = { tool: false, link: true }
|
|
1380
|
+
} = config;
|
|
1381
|
+
const handleIntent = useCallback(
|
|
1382
|
+
async (intent, context) => {
|
|
1383
|
+
switch (intent.type) {
|
|
1384
|
+
case "tool": {
|
|
1385
|
+
if (requireConsent.tool) ;
|
|
1386
|
+
await onToolCall?.(intent.name, intent.arguments, context);
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
case "intent": {
|
|
1390
|
+
await onIntent?.(intent.action, intent.data, context);
|
|
1391
|
+
break;
|
|
1392
|
+
}
|
|
1393
|
+
case "prompt": {
|
|
1394
|
+
onPrompt?.(intent.text, context);
|
|
1395
|
+
break;
|
|
1396
|
+
}
|
|
1397
|
+
case "notify": {
|
|
1398
|
+
onNotify?.(intent.message, intent.level, context);
|
|
1399
|
+
break;
|
|
1400
|
+
}
|
|
1401
|
+
case "link": {
|
|
1402
|
+
const shouldContinue = onLink?.(intent.url, intent.newTab, context);
|
|
1403
|
+
if (shouldContinue === false) {
|
|
1404
|
+
break;
|
|
1405
|
+
}
|
|
1406
|
+
if (requireConsent.link) {
|
|
1407
|
+
const isSafeUrl = intent.url.startsWith("https://") || intent.url.startsWith("http://localhost");
|
|
1408
|
+
if (!isSafeUrl) {
|
|
1409
|
+
console.warn(
|
|
1410
|
+
"[MCP-UI] Blocked potentially unsafe link:",
|
|
1411
|
+
intent.url
|
|
1412
|
+
);
|
|
1413
|
+
break;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
if (typeof window !== "undefined") {
|
|
1417
|
+
if (intent.newTab !== false) {
|
|
1418
|
+
window.open(intent.url, "_blank", "noopener,noreferrer");
|
|
1419
|
+
} else {
|
|
1420
|
+
window.location.href = intent.url;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
break;
|
|
1424
|
+
}
|
|
1425
|
+
default: {
|
|
1426
|
+
console.warn(
|
|
1427
|
+
"[MCP-UI] Unknown intent type:",
|
|
1428
|
+
intent.type
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
},
|
|
1433
|
+
[onToolCall, onIntent, onPrompt, onNotify, onLink, requireConsent]
|
|
1434
|
+
);
|
|
1435
|
+
return useMemo(
|
|
1436
|
+
() => ({
|
|
1437
|
+
handleIntent
|
|
1438
|
+
}),
|
|
1439
|
+
[handleIntent]
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
function createMessageIntentHandler(sendMessage) {
|
|
1443
|
+
return {
|
|
1444
|
+
onIntent: async (action, data) => {
|
|
1445
|
+
const dataStr = data ? ` with ${JSON.stringify(data)}` : "";
|
|
1446
|
+
await sendMessage(`User action: ${action}${dataStr}`);
|
|
1447
|
+
},
|
|
1448
|
+
onPrompt: (text) => {
|
|
1449
|
+
sendMessage(text);
|
|
1450
|
+
},
|
|
1451
|
+
onNotify: (message, level) => {
|
|
1452
|
+
if (level === "error") {
|
|
1453
|
+
sendMessage(`Error: ${message}`);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
function createToolIntentHandler(callTool) {
|
|
1459
|
+
return {
|
|
1460
|
+
onToolCall: async (name, args) => {
|
|
1461
|
+
await callTool(name, args);
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
function getLastResponseUsage(messages) {
|
|
1466
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1467
|
+
const msg = messages[i];
|
|
1468
|
+
if (msg.role === "assistant" && msg.metadata?.usage) {
|
|
1469
|
+
const u = msg.metadata.usage;
|
|
1470
|
+
const prompt = u.prompt_tokens ?? 0;
|
|
1471
|
+
const completion = u.completion_tokens ?? 0;
|
|
1472
|
+
return {
|
|
1473
|
+
prompt_tokens: prompt,
|
|
1474
|
+
completion_tokens: completion,
|
|
1475
|
+
total_tokens: u.total_tokens ?? prompt + completion
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
function useContextStats() {
|
|
1482
|
+
const { contextChars, contextUsage, registeredTools, messages } = useCopilot();
|
|
1483
|
+
const toolCount = useMemo(() => registeredTools.length, [registeredTools]);
|
|
1484
|
+
const messageCount = useMemo(
|
|
1485
|
+
() => messages.filter((m) => m.role !== "system").length,
|
|
1486
|
+
[messages]
|
|
1487
|
+
);
|
|
1488
|
+
const totalTokens = useMemo(() => {
|
|
1489
|
+
if (contextUsage) return contextUsage.total.tokens;
|
|
1490
|
+
return Math.ceil(contextChars / 3.5);
|
|
1491
|
+
}, [contextUsage, contextChars]);
|
|
1492
|
+
const usagePercent = useMemo(() => {
|
|
1493
|
+
if (contextUsage) return contextUsage.total.percent;
|
|
1494
|
+
return 0;
|
|
1495
|
+
}, [contextUsage]);
|
|
1496
|
+
const lastResponseUsage = useMemo(
|
|
1497
|
+
() => getLastResponseUsage(messages),
|
|
1498
|
+
[messages]
|
|
1499
|
+
);
|
|
1500
|
+
return {
|
|
1501
|
+
contextUsage,
|
|
1502
|
+
totalTokens,
|
|
1503
|
+
usagePercent,
|
|
1504
|
+
contextChars,
|
|
1505
|
+
toolCount,
|
|
1506
|
+
messageCount,
|
|
1507
|
+
lastResponseUsage
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
var DEV_CONTENT_WARN_THRESHOLD = 2e3;
|
|
1511
|
+
function useSkill(skill) {
|
|
1512
|
+
const { register, unregister } = useSkillContext();
|
|
1513
|
+
if (process.env.NODE_ENV !== "production" && skill.source.type === "inline" && skill.source.content.length > DEV_CONTENT_WARN_THRESHOLD) {
|
|
1514
|
+
console.warn(
|
|
1515
|
+
`[copilot-sdk/skills] Inline skill "${skill.name}" has ${skill.source.content.length} characters. Inline skills are sent on every request \u2014 keep them under ${DEV_CONTENT_WARN_THRESHOLD} characters. Consider using a file or URL skill instead.`
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
useEffect(() => {
|
|
1519
|
+
if (skill.source.type !== "inline") {
|
|
1520
|
+
console.warn(
|
|
1521
|
+
`[copilot-sdk/skills] useSkill only supports inline skills client-side. Skill "${skill.name}" has source type "${skill.source.type}" and will be skipped.`
|
|
1522
|
+
);
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
const resolved = {
|
|
1526
|
+
...skill,
|
|
1527
|
+
content: skill.source.content
|
|
1528
|
+
};
|
|
1529
|
+
register(resolved);
|
|
1530
|
+
return () => {
|
|
1531
|
+
unregister(skill.name);
|
|
1532
|
+
};
|
|
1533
|
+
}, [
|
|
1534
|
+
skill.name,
|
|
1535
|
+
skill.source.type === "inline" ? skill.source.content : "",
|
|
1536
|
+
skill.strategy,
|
|
1537
|
+
skill.description
|
|
1538
|
+
]);
|
|
1539
|
+
}
|
|
1540
|
+
function useSkillStatus() {
|
|
1541
|
+
const { skills, registry } = useSkillContext();
|
|
1542
|
+
const has = useCallback(
|
|
1543
|
+
(name) => registry.has(name),
|
|
1544
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1545
|
+
[skills]
|
|
1546
|
+
);
|
|
1547
|
+
return {
|
|
1548
|
+
skills,
|
|
1549
|
+
count: skills.length,
|
|
1550
|
+
has
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function useMessageCheckpoints() {
|
|
1554
|
+
const { messages, setMessages } = useCopilot();
|
|
1555
|
+
const checkpointMapRef = useRef(/* @__PURE__ */ new Map());
|
|
1556
|
+
const prevUserMsgCountRef = useRef(0);
|
|
1557
|
+
const [revision, bump] = useReducer((n) => n + 1, 0);
|
|
1558
|
+
useEffect(() => {
|
|
1559
|
+
const userMessages = messages.filter((m) => m.role === "user");
|
|
1560
|
+
const count = userMessages.length;
|
|
1561
|
+
if (count > prevUserMsgCountRef.current) {
|
|
1562
|
+
const newUserMsg = userMessages[count - 1];
|
|
1563
|
+
if (!checkpointMapRef.current.has(newUserMsg.id)) {
|
|
1564
|
+
const msgIndex = messages.findIndex((m) => m.id === newUserMsg.id);
|
|
1565
|
+
checkpointMapRef.current.set(newUserMsg.id, {
|
|
1566
|
+
id: `cp_${newUserMsg.id}`,
|
|
1567
|
+
messageId: newUserMsg.id,
|
|
1568
|
+
timestamp: Date.now(),
|
|
1569
|
+
messages: structuredClone(messages.slice(0, msgIndex))
|
|
1570
|
+
});
|
|
1571
|
+
bump();
|
|
1572
|
+
}
|
|
1573
|
+
prevUserMsgCountRef.current = count;
|
|
1574
|
+
}
|
|
1575
|
+
}, [messages]);
|
|
1576
|
+
const hasCheckpoint = useCallback(
|
|
1577
|
+
(messageId) => checkpointMapRef.current.has(messageId),
|
|
1578
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1579
|
+
[revision]
|
|
1580
|
+
);
|
|
1581
|
+
const getCheckpoint = useCallback(
|
|
1582
|
+
(messageId) => checkpointMapRef.current.get(messageId),
|
|
1583
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1584
|
+
[revision]
|
|
1585
|
+
);
|
|
1586
|
+
const restore = useCallback(
|
|
1587
|
+
(messageId) => {
|
|
1588
|
+
const cp = checkpointMapRef.current.get(messageId);
|
|
1589
|
+
if (!cp) return void 0;
|
|
1590
|
+
setMessages(cp.messages);
|
|
1591
|
+
checkpointMapRef.current.forEach((c, k) => {
|
|
1592
|
+
if (c.timestamp > cp.timestamp) checkpointMapRef.current.delete(k);
|
|
1593
|
+
});
|
|
1594
|
+
prevUserMsgCountRef.current = cp.messages.filter(
|
|
1595
|
+
(m) => m.role === "user"
|
|
1596
|
+
).length;
|
|
1597
|
+
bump();
|
|
1598
|
+
return cp;
|
|
1599
|
+
},
|
|
1600
|
+
[setMessages]
|
|
1601
|
+
);
|
|
1602
|
+
const save = useCallback(
|
|
1603
|
+
(messageId, label) => {
|
|
1604
|
+
const msgIndex = messages.findIndex((m) => m.id === messageId);
|
|
1605
|
+
if (msgIndex === -1) return void 0;
|
|
1606
|
+
const cp = {
|
|
1607
|
+
id: `cp_${messageId}`,
|
|
1608
|
+
messageId,
|
|
1609
|
+
timestamp: Date.now(),
|
|
1610
|
+
label,
|
|
1611
|
+
messages: structuredClone(messages.slice(0, msgIndex))
|
|
1612
|
+
};
|
|
1613
|
+
checkpointMapRef.current.set(messageId, cp);
|
|
1614
|
+
bump();
|
|
1615
|
+
return cp;
|
|
1616
|
+
},
|
|
1617
|
+
[messages]
|
|
1618
|
+
);
|
|
1619
|
+
const clear = useCallback(() => {
|
|
1620
|
+
checkpointMapRef.current.clear();
|
|
1621
|
+
prevUserMsgCountRef.current = 0;
|
|
1622
|
+
bump();
|
|
1623
|
+
}, []);
|
|
1624
|
+
const checkpoints = useMemo(
|
|
1625
|
+
() => Array.from(checkpointMapRef.current.values()).sort(
|
|
1626
|
+
(a, b) => a.timestamp - b.timestamp
|
|
1627
|
+
),
|
|
1628
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1629
|
+
[revision]
|
|
1630
|
+
);
|
|
1631
|
+
return { checkpoints, hasCheckpoint, getCheckpoint, restore, save, clear };
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// src/react/utils/permission-storage.ts
|
|
1635
|
+
var DEFAULT_KEY_PREFIX = "yourgpt-permissions";
|
|
1636
|
+
function createPermissionStorage(config) {
|
|
1637
|
+
switch (config.type) {
|
|
1638
|
+
case "localStorage":
|
|
1639
|
+
return createBrowserStorageAdapter(
|
|
1640
|
+
typeof window !== "undefined" ? localStorage : null,
|
|
1641
|
+
config.keyPrefix
|
|
1642
|
+
);
|
|
1643
|
+
case "sessionStorage":
|
|
1644
|
+
return createBrowserStorageAdapter(
|
|
1645
|
+
typeof window !== "undefined" ? sessionStorage : null,
|
|
1646
|
+
config.keyPrefix
|
|
1647
|
+
);
|
|
1648
|
+
case "memory":
|
|
1649
|
+
default:
|
|
1650
|
+
return createMemoryStorageAdapter();
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
function createBrowserStorageAdapter(storage, keyPrefix = DEFAULT_KEY_PREFIX) {
|
|
1654
|
+
const getStorageKey = () => keyPrefix;
|
|
1655
|
+
const loadPermissions = () => {
|
|
1656
|
+
if (!storage) return /* @__PURE__ */ new Map();
|
|
1657
|
+
try {
|
|
1658
|
+
const data = storage.getItem(getStorageKey());
|
|
1659
|
+
if (!data) return /* @__PURE__ */ new Map();
|
|
1660
|
+
const parsed = JSON.parse(data);
|
|
1661
|
+
return new Map(parsed.map((p) => [p.toolName, p]));
|
|
1662
|
+
} catch {
|
|
1663
|
+
return /* @__PURE__ */ new Map();
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
const savePermissions = (permissions) => {
|
|
1667
|
+
if (!storage) return;
|
|
1668
|
+
try {
|
|
1669
|
+
storage.setItem(
|
|
1670
|
+
getStorageKey(),
|
|
1671
|
+
JSON.stringify(Array.from(permissions.values()))
|
|
1672
|
+
);
|
|
1673
|
+
} catch (e) {
|
|
1674
|
+
console.warn("[PermissionStorage] Failed to save permissions:", e);
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
return {
|
|
1678
|
+
async get(toolName) {
|
|
1679
|
+
const permissions = loadPermissions();
|
|
1680
|
+
return permissions.get(toolName) || null;
|
|
1681
|
+
},
|
|
1682
|
+
async set(permission) {
|
|
1683
|
+
const permissions = loadPermissions();
|
|
1684
|
+
permissions.set(permission.toolName, permission);
|
|
1685
|
+
savePermissions(permissions);
|
|
1686
|
+
},
|
|
1687
|
+
async remove(toolName) {
|
|
1688
|
+
const permissions = loadPermissions();
|
|
1689
|
+
permissions.delete(toolName);
|
|
1690
|
+
savePermissions(permissions);
|
|
1691
|
+
},
|
|
1692
|
+
async getAll() {
|
|
1693
|
+
const permissions = loadPermissions();
|
|
1694
|
+
return Array.from(permissions.values());
|
|
1695
|
+
},
|
|
1696
|
+
async clear() {
|
|
1697
|
+
if (!storage) return;
|
|
1698
|
+
storage.removeItem(getStorageKey());
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function createMemoryStorageAdapter() {
|
|
1703
|
+
const permissions = /* @__PURE__ */ new Map();
|
|
1704
|
+
return {
|
|
1705
|
+
async get(toolName) {
|
|
1706
|
+
return permissions.get(toolName) || null;
|
|
1707
|
+
},
|
|
1708
|
+
async set(permission) {
|
|
1709
|
+
permissions.set(permission.toolName, permission);
|
|
1710
|
+
},
|
|
1711
|
+
async remove(toolName) {
|
|
1712
|
+
permissions.delete(toolName);
|
|
1713
|
+
},
|
|
1714
|
+
async getAll() {
|
|
1715
|
+
return Array.from(permissions.values());
|
|
1716
|
+
},
|
|
1717
|
+
async clear() {
|
|
1718
|
+
permissions.clear();
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
function createSessionPermissionCache() {
|
|
1723
|
+
return /* @__PURE__ */ new Map();
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
// src/react/internal/ReactChat.ts
|
|
1727
|
+
var ReactChat = class extends AbstractChat {
|
|
1728
|
+
constructor(config) {
|
|
1729
|
+
const reactState = new ReactChatState(config.initialMessages);
|
|
1730
|
+
const init = {
|
|
1731
|
+
runtimeUrl: config.runtimeUrl,
|
|
1732
|
+
systemPrompt: config.systemPrompt,
|
|
1733
|
+
llm: config.llm,
|
|
1734
|
+
threadId: config.threadId,
|
|
1735
|
+
onCreateSession: config.onCreateSession,
|
|
1736
|
+
yourgptConfig: config.yourgptConfig,
|
|
1737
|
+
streaming: config.streaming ?? true,
|
|
1738
|
+
headers: config.headers,
|
|
1739
|
+
initialMessages: config.initialMessages,
|
|
1740
|
+
state: reactState,
|
|
1741
|
+
callbacks: config.callbacks,
|
|
1742
|
+
debug: config.debug
|
|
1743
|
+
};
|
|
1744
|
+
super(init);
|
|
1745
|
+
// ============================================
|
|
1746
|
+
// Subscribe (for useSyncExternalStore)
|
|
1747
|
+
// ============================================
|
|
1748
|
+
/**
|
|
1749
|
+
* Subscribe to state changes.
|
|
1750
|
+
* Returns an unsubscribe function.
|
|
1751
|
+
*
|
|
1752
|
+
* @example
|
|
1753
|
+
* ```tsx
|
|
1754
|
+
* const messages = useSyncExternalStore(
|
|
1755
|
+
* chat.subscribe,
|
|
1756
|
+
* () => chat.messages
|
|
1757
|
+
* );
|
|
1758
|
+
* ```
|
|
1759
|
+
*/
|
|
1760
|
+
this.subscribe = (callback) => {
|
|
1761
|
+
return this.reactState.subscribe(callback);
|
|
1762
|
+
};
|
|
1763
|
+
this.reactState = reactState;
|
|
1764
|
+
}
|
|
1765
|
+
// ============================================
|
|
1766
|
+
// Event handling shortcuts
|
|
1767
|
+
// ============================================
|
|
1768
|
+
/**
|
|
1769
|
+
* Subscribe to tool calls events
|
|
1770
|
+
*/
|
|
1771
|
+
onToolCalls(handler) {
|
|
1772
|
+
return this.on("toolCalls", handler);
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Subscribe to done events
|
|
1776
|
+
*/
|
|
1777
|
+
onDone(handler) {
|
|
1778
|
+
return this.on("done", handler);
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Subscribe to error events
|
|
1782
|
+
*/
|
|
1783
|
+
onError(handler) {
|
|
1784
|
+
return this.on("error", handler);
|
|
1785
|
+
}
|
|
1786
|
+
// ============================================
|
|
1787
|
+
// Branching API — pass-throughs to ReactChatState
|
|
1788
|
+
// ============================================
|
|
1789
|
+
/**
|
|
1790
|
+
* Navigate to a sibling branch (makes it the active path).
|
|
1791
|
+
*/
|
|
1792
|
+
switchBranch(messageId) {
|
|
1793
|
+
this.reactState.switchBranch(messageId);
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Get branch navigation info for a message.
|
|
1797
|
+
* Returns null if the message has no siblings.
|
|
1798
|
+
*/
|
|
1799
|
+
getBranchInfo(messageId) {
|
|
1800
|
+
return this.reactState.getBranchInfo(messageId);
|
|
1801
|
+
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Get all messages across all branches (for persistence).
|
|
1804
|
+
*/
|
|
1805
|
+
getAllMessages() {
|
|
1806
|
+
return this.reactState.getAllMessages();
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Whether any message has siblings (branching has occurred).
|
|
1810
|
+
*/
|
|
1811
|
+
get hasBranches() {
|
|
1812
|
+
return this.reactState.hasBranches;
|
|
1813
|
+
}
|
|
1814
|
+
// ============================================
|
|
1815
|
+
// Override dispose to clean up state
|
|
1816
|
+
// ============================================
|
|
1817
|
+
dispose() {
|
|
1818
|
+
super.dispose();
|
|
1819
|
+
this.reactState.dispose();
|
|
1820
|
+
}
|
|
1821
|
+
/**
|
|
1822
|
+
* Revive a disposed instance (for React StrictMode compatibility)
|
|
1823
|
+
*/
|
|
1824
|
+
revive() {
|
|
1825
|
+
super.revive();
|
|
1826
|
+
this.reactState.revive();
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
function createReactChat(config) {
|
|
1830
|
+
return new ReactChat(config);
|
|
1831
|
+
}
|
|
1832
|
+
function useChat(config) {
|
|
1833
|
+
const chatRef = useRef(null);
|
|
1834
|
+
const isThreadIdControlled = Object.prototype.hasOwnProperty.call(
|
|
1835
|
+
config,
|
|
1836
|
+
"threadId"
|
|
1837
|
+
);
|
|
1838
|
+
const lastControlledThreadIdRef = useRef({
|
|
1839
|
+
controlled: isThreadIdControlled,
|
|
1840
|
+
value: config.threadId
|
|
1841
|
+
});
|
|
1842
|
+
const [input, setInput] = useState("");
|
|
1843
|
+
if (chatRef.current !== null && chatRef.current.disposed) {
|
|
1844
|
+
chatRef.current.revive();
|
|
1845
|
+
}
|
|
1846
|
+
if (chatRef.current === null) {
|
|
1847
|
+
chatRef.current = createReactChat({
|
|
1848
|
+
runtimeUrl: config.runtimeUrl,
|
|
1849
|
+
systemPrompt: config.systemPrompt,
|
|
1850
|
+
llm: config.llm,
|
|
1851
|
+
threadId: config.threadId,
|
|
1852
|
+
onCreateSession: config.onCreateSession,
|
|
1853
|
+
yourgptConfig: config.yourgptConfig,
|
|
1854
|
+
streaming: config.streaming,
|
|
1855
|
+
headers: config.headers,
|
|
1856
|
+
initialMessages: config.initialMessages,
|
|
1857
|
+
debug: config.debug,
|
|
1858
|
+
callbacks: {
|
|
1859
|
+
onMessagesChange: config.onMessagesChange,
|
|
1860
|
+
onError: config.onError,
|
|
1861
|
+
onFinish: config.onFinish,
|
|
1862
|
+
onToolCalls: config.onToolCalls
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
useEffect(() => {
|
|
1867
|
+
const prev = lastControlledThreadIdRef.current;
|
|
1868
|
+
const controlChanged = prev.controlled !== isThreadIdControlled;
|
|
1869
|
+
const valueChanged = prev.value !== config.threadId;
|
|
1870
|
+
if (!controlChanged && !valueChanged) {
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
lastControlledThreadIdRef.current = {
|
|
1874
|
+
controlled: isThreadIdControlled,
|
|
1875
|
+
value: config.threadId
|
|
1876
|
+
};
|
|
1877
|
+
if (!isThreadIdControlled) {
|
|
1878
|
+
return;
|
|
1879
|
+
}
|
|
1880
|
+
chatRef.current?.setActiveThread(config.threadId ?? null);
|
|
1881
|
+
}, [config.threadId, isThreadIdControlled]);
|
|
1882
|
+
const messages = useSyncExternalStore(
|
|
1883
|
+
chatRef.current.subscribe,
|
|
1884
|
+
() => chatRef.current.messages,
|
|
1885
|
+
() => chatRef.current.messages
|
|
1886
|
+
// Server snapshot
|
|
1887
|
+
);
|
|
1888
|
+
const status = useSyncExternalStore(
|
|
1889
|
+
chatRef.current.subscribe,
|
|
1890
|
+
() => chatRef.current.status,
|
|
1891
|
+
() => "ready"
|
|
1892
|
+
// Server snapshot
|
|
1893
|
+
);
|
|
1894
|
+
const error = useSyncExternalStore(
|
|
1895
|
+
chatRef.current.subscribe,
|
|
1896
|
+
() => chatRef.current.error,
|
|
1897
|
+
() => void 0
|
|
1898
|
+
// Server snapshot
|
|
1899
|
+
);
|
|
1900
|
+
const hasBranches = useSyncExternalStore(
|
|
1901
|
+
chatRef.current.subscribe,
|
|
1902
|
+
() => chatRef.current.hasBranches,
|
|
1903
|
+
() => false
|
|
1904
|
+
);
|
|
1905
|
+
const isLoading = status === "streaming" || status === "submitted";
|
|
1906
|
+
const sendMessage = useCallback(
|
|
1907
|
+
async (content, attachments) => {
|
|
1908
|
+
await chatRef.current?.sendMessage(content, attachments);
|
|
1909
|
+
setInput("");
|
|
1910
|
+
},
|
|
1911
|
+
[]
|
|
1912
|
+
);
|
|
1913
|
+
const stop = useCallback(() => {
|
|
1914
|
+
chatRef.current?.stop();
|
|
1915
|
+
}, []);
|
|
1916
|
+
const clearMessages = useCallback(() => {
|
|
1917
|
+
chatRef.current?.clearMessages();
|
|
1918
|
+
}, []);
|
|
1919
|
+
const setMessages = useCallback((messages2) => {
|
|
1920
|
+
chatRef.current?.setMessages(messages2);
|
|
1921
|
+
}, []);
|
|
1922
|
+
const regenerate = useCallback(async (messageId) => {
|
|
1923
|
+
await chatRef.current?.regenerate(messageId);
|
|
1924
|
+
}, []);
|
|
1925
|
+
const continueWithToolResults = useCallback(
|
|
1926
|
+
async (toolResults) => {
|
|
1927
|
+
await chatRef.current?.continueWithToolResults(toolResults);
|
|
1928
|
+
},
|
|
1929
|
+
[]
|
|
1930
|
+
);
|
|
1931
|
+
const switchBranch = useCallback((messageId) => {
|
|
1932
|
+
chatRef.current?.switchBranch(messageId);
|
|
1933
|
+
}, []);
|
|
1934
|
+
const getBranchInfo = useCallback((messageId) => {
|
|
1935
|
+
return chatRef.current?.getBranchInfo(messageId) ?? null;
|
|
1936
|
+
}, []);
|
|
1937
|
+
const editMessage = useCallback(
|
|
1938
|
+
async (messageId, newContent) => {
|
|
1939
|
+
await chatRef.current?.sendMessage(newContent, void 0, {
|
|
1940
|
+
editMessageId: messageId
|
|
1941
|
+
});
|
|
1942
|
+
setInput("");
|
|
1943
|
+
},
|
|
1944
|
+
[]
|
|
1945
|
+
);
|
|
1946
|
+
useEffect(() => {
|
|
1947
|
+
return () => {
|
|
1948
|
+
chatRef.current?.dispose();
|
|
1949
|
+
};
|
|
1950
|
+
}, []);
|
|
1951
|
+
return {
|
|
1952
|
+
messages,
|
|
1953
|
+
status,
|
|
1954
|
+
error,
|
|
1955
|
+
isLoading,
|
|
1956
|
+
input,
|
|
1957
|
+
setInput,
|
|
1958
|
+
sendMessage,
|
|
1959
|
+
stop,
|
|
1960
|
+
clearMessages,
|
|
1961
|
+
setMessages,
|
|
1962
|
+
regenerate,
|
|
1963
|
+
continueWithToolResults,
|
|
1964
|
+
chatRef,
|
|
1965
|
+
// Branching
|
|
1966
|
+
switchBranch,
|
|
1967
|
+
getBranchInfo,
|
|
1968
|
+
editMessage,
|
|
1969
|
+
hasBranches
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
// src/react/skill/define-skill.ts
|
|
1974
|
+
function defineSkill(def) {
|
|
1975
|
+
return def;
|
|
1976
|
+
}
|
|
1977
|
+
function useCopilotEvent(eventType, handler) {
|
|
1978
|
+
const { subscribeToStreamEvents } = useCopilot();
|
|
1979
|
+
const handlerRef = useRef(handler);
|
|
1980
|
+
handlerRef.current = handler;
|
|
1981
|
+
useEffect(() => {
|
|
1982
|
+
const unsub = subscribeToStreamEvents((chunk) => {
|
|
1983
|
+
if (eventType === "*" || chunk.type === eventType) {
|
|
1984
|
+
handlerRef.current(chunk);
|
|
1985
|
+
}
|
|
1986
|
+
});
|
|
1987
|
+
return unsub;
|
|
1988
|
+
}, [subscribeToStreamEvents, eventType]);
|
|
1989
|
+
}
|
|
1990
|
+
var EMPTY_META = {};
|
|
1991
|
+
function useMessageMeta(messageId) {
|
|
1992
|
+
const { messageMeta } = useCopilot();
|
|
1993
|
+
const meta = useSyncExternalStore(
|
|
1994
|
+
messageMeta.subscribe,
|
|
1995
|
+
() => messageId ? messageMeta.getMeta(messageId) : EMPTY_META,
|
|
1996
|
+
() => EMPTY_META
|
|
1997
|
+
);
|
|
1998
|
+
const setMeta = useCallback(
|
|
1999
|
+
(next) => {
|
|
2000
|
+
if (!messageId) return;
|
|
2001
|
+
messageMeta.setMeta(messageId, next);
|
|
2002
|
+
},
|
|
2003
|
+
[messageMeta, messageId]
|
|
2004
|
+
);
|
|
2005
|
+
const updateMeta = useCallback(
|
|
2006
|
+
(updater) => {
|
|
2007
|
+
if (!messageId) return;
|
|
2008
|
+
messageMeta.updateMeta(
|
|
2009
|
+
messageId,
|
|
2010
|
+
(prev) => updater(prev)
|
|
2011
|
+
);
|
|
2012
|
+
},
|
|
2013
|
+
[messageMeta, messageId]
|
|
2014
|
+
);
|
|
2015
|
+
return { meta, setMeta, updateMeta };
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
export { ReactChat, ReactThreadManager, ReactThreadManagerState, createMessageIntentHandler, createPermissionStorage, createReactChat, createReactThreadManager, createReactThreadManagerState, createSessionPermissionCache, createToolIntentHandler, defineSkill, formatKnowledgeResultsForAI, searchKnowledgeBase, useAIAction, useAIActions, useAITools, useAgent, useCapabilities, useChat, useContextStats, useCopilotEvent, useDevLogger, useFeatureSupport, useKnowledgeBase, useMCPUIIntents, useMessageCheckpoints, useMessageMeta, useSkill, useSkillStatus, useSuggestions, useSupportedMediaTypes, useThread, useThreadManager, useToolExecutor, useToolWithSchema, useToolsWithSchema };
|
|
2019
|
+
//# sourceMappingURL=chunk-PURFAD2P.js.map
|
|
2020
|
+
//# sourceMappingURL=chunk-PURFAD2P.js.map
|