@yourgpt/copilot-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3152 @@
1
+ 'use strict';
2
+
3
+ var chunk2ZCWVAAK_cjs = require('./chunk-2ZCWVAAK.cjs');
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var z = require('zod');
7
+
8
+ function _interopNamespace(e) {
9
+ if (e && e.__esModule) return e;
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var z__namespace = /*#__PURE__*/_interopNamespace(z);
27
+
28
+ // src/chat/types/tool.ts
29
+ var initialAgentLoopState = {
30
+ toolExecutions: [],
31
+ iteration: 0,
32
+ maxIterations: 20,
33
+ maxIterationsReached: false,
34
+ isProcessing: false
35
+ };
36
+
37
+ // src/chat/interfaces/ChatState.ts
38
+ var SimpleChatState = class {
39
+ constructor() {
40
+ this._messages = [];
41
+ this._status = "ready";
42
+ this._error = void 0;
43
+ this.callbacks = /* @__PURE__ */ new Set();
44
+ }
45
+ get messages() {
46
+ return this._messages;
47
+ }
48
+ set messages(value) {
49
+ this._messages = value;
50
+ this.notify();
51
+ }
52
+ get status() {
53
+ return this._status;
54
+ }
55
+ set status(value) {
56
+ this._status = value;
57
+ this.notify();
58
+ }
59
+ get error() {
60
+ return this._error;
61
+ }
62
+ set error(value) {
63
+ this._error = value;
64
+ this.notify();
65
+ }
66
+ pushMessage(message) {
67
+ this._messages = [...this._messages, message];
68
+ this.notify();
69
+ }
70
+ popMessage() {
71
+ this._messages = this._messages.slice(0, -1);
72
+ this.notify();
73
+ }
74
+ replaceMessage(index, message) {
75
+ this._messages = [
76
+ ...this._messages.slice(0, index),
77
+ message,
78
+ ...this._messages.slice(index + 1)
79
+ ];
80
+ this.notify();
81
+ }
82
+ updateLastMessage(updater) {
83
+ if (this._messages.length === 0) return;
84
+ const lastIndex = this._messages.length - 1;
85
+ this.replaceMessage(lastIndex, updater(this._messages[lastIndex]));
86
+ }
87
+ setMessages(messages) {
88
+ this._messages = messages;
89
+ this.notify();
90
+ }
91
+ clearMessages() {
92
+ this._messages = [];
93
+ this.notify();
94
+ }
95
+ subscribe(callback) {
96
+ this.callbacks.add(callback);
97
+ return () => this.callbacks.delete(callback);
98
+ }
99
+ getMessagesSnapshot() {
100
+ return this._messages;
101
+ }
102
+ getStatusSnapshot() {
103
+ return this._status;
104
+ }
105
+ getErrorSnapshot() {
106
+ return this._error;
107
+ }
108
+ notify() {
109
+ this.callbacks.forEach((cb) => cb());
110
+ }
111
+ };
112
+
113
+ // src/chat/functions/stream/parseSSE.ts
114
+ function parseSSELine(line) {
115
+ if (!line || line.trim() === "") {
116
+ return null;
117
+ }
118
+ if (line.startsWith(":")) {
119
+ return null;
120
+ }
121
+ if (line.startsWith("data: ")) {
122
+ const data = line.slice(6);
123
+ if (data === "[DONE]") {
124
+ return { type: "done" };
125
+ }
126
+ try {
127
+ return JSON.parse(data);
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+ return null;
133
+ }
134
+ function isStreamDone(chunk) {
135
+ return chunk.type === "done" || chunk.type === "error";
136
+ }
137
+ function requiresToolExecution(chunk) {
138
+ if (chunk.type === "done" && chunk.requiresAction) {
139
+ return true;
140
+ }
141
+ if (chunk.type === "tool_calls") {
142
+ return true;
143
+ }
144
+ return false;
145
+ }
146
+
147
+ // src/chat/functions/stream/processChunk.ts
148
+ function processStreamChunk(chunk, state) {
149
+ switch (chunk.type) {
150
+ case "message:start":
151
+ return {
152
+ ...state,
153
+ messageId: chunk.id
154
+ };
155
+ case "message:delta":
156
+ return {
157
+ ...state,
158
+ content: state.content + chunk.content
159
+ };
160
+ case "message:end":
161
+ return {
162
+ ...state,
163
+ finishReason: "stop"
164
+ };
165
+ case "thinking:delta":
166
+ return {
167
+ ...state,
168
+ thinking: state.thinking + chunk.content
169
+ };
170
+ case "tool_calls":
171
+ return {
172
+ ...state,
173
+ toolCalls: parseToolCalls(chunk.toolCalls),
174
+ requiresAction: true
175
+ };
176
+ case "done":
177
+ return {
178
+ ...state,
179
+ requiresAction: chunk.requiresAction ?? false,
180
+ finishReason: "stop"
181
+ };
182
+ case "error":
183
+ return {
184
+ ...state,
185
+ finishReason: "error"
186
+ };
187
+ default:
188
+ return state;
189
+ }
190
+ }
191
+ function parseToolCalls(rawToolCalls) {
192
+ return rawToolCalls.map((tc) => {
193
+ const toolCall = tc;
194
+ if (toolCall.function) {
195
+ return {
196
+ id: toolCall.id,
197
+ name: toolCall.function.name,
198
+ args: JSON.parse(toolCall.function.arguments)
199
+ };
200
+ }
201
+ return {
202
+ id: toolCall.id,
203
+ name: toolCall.name ?? "",
204
+ args: toolCall.args ?? {}
205
+ };
206
+ });
207
+ }
208
+ function createStreamState(messageId) {
209
+ return {
210
+ messageId,
211
+ content: "",
212
+ thinking: "",
213
+ toolCalls: [],
214
+ requiresAction: false,
215
+ finishReason: void 0
216
+ };
217
+ }
218
+
219
+ // src/chat/functions/message/createMessage.ts
220
+ function generateMessageId() {
221
+ return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
222
+ }
223
+ function createUserMessage(content, attachments) {
224
+ return {
225
+ id: generateMessageId(),
226
+ role: "user",
227
+ content,
228
+ attachments,
229
+ createdAt: /* @__PURE__ */ new Date()
230
+ };
231
+ }
232
+ function streamStateToMessage(state) {
233
+ const toolCalls = state.toolCalls.length > 0 ? state.toolCalls.map((tc) => ({
234
+ id: tc.id,
235
+ type: "function",
236
+ function: {
237
+ name: tc.name,
238
+ arguments: JSON.stringify(tc.args)
239
+ }
240
+ })) : void 0;
241
+ return {
242
+ id: state.messageId,
243
+ role: "assistant",
244
+ content: state.content,
245
+ thinking: state.thinking || void 0,
246
+ toolCalls,
247
+ createdAt: /* @__PURE__ */ new Date()
248
+ };
249
+ }
250
+ function createEmptyAssistantMessage(id) {
251
+ return {
252
+ id: generateMessageId(),
253
+ role: "assistant",
254
+ content: "",
255
+ createdAt: /* @__PURE__ */ new Date()
256
+ };
257
+ }
258
+
259
+ // src/chat/adapters/HttpTransport.ts
260
+ var HttpTransport = class {
261
+ constructor(config) {
262
+ this.abortController = null;
263
+ this.streaming = false;
264
+ this.config = {
265
+ streaming: true,
266
+ timeout: 6e4,
267
+ ...config
268
+ };
269
+ }
270
+ /**
271
+ * Send a chat request
272
+ */
273
+ async send(request) {
274
+ this.abortController = new AbortController();
275
+ this.streaming = true;
276
+ try {
277
+ const response = await fetch(this.config.url, {
278
+ method: "POST",
279
+ headers: {
280
+ "Content-Type": "application/json",
281
+ ...this.config.headers
282
+ },
283
+ body: JSON.stringify({
284
+ messages: request.messages,
285
+ threadId: request.threadId,
286
+ systemPrompt: request.systemPrompt,
287
+ llm: request.llm,
288
+ tools: request.tools,
289
+ actions: request.actions,
290
+ streaming: this.config.streaming,
291
+ ...request.body
292
+ }),
293
+ signal: this.abortController.signal
294
+ });
295
+ if (!response.ok) {
296
+ const error = await response.text();
297
+ throw new Error(`HTTP ${response.status}: ${error}`);
298
+ }
299
+ const contentType = response.headers.get("content-type") || "";
300
+ if (contentType.includes("application/json")) {
301
+ this.streaming = false;
302
+ const json = await response.json();
303
+ return json;
304
+ }
305
+ if (!response.body) {
306
+ throw new Error("No response body");
307
+ }
308
+ return this.createStreamIterable(response.body);
309
+ } catch (error) {
310
+ this.streaming = false;
311
+ if (error.name === "AbortError") {
312
+ return (async function* () {
313
+ })();
314
+ }
315
+ throw error;
316
+ }
317
+ }
318
+ /**
319
+ * Abort the current request
320
+ */
321
+ abort() {
322
+ this.abortController?.abort();
323
+ this.abortController = null;
324
+ this.streaming = false;
325
+ }
326
+ /**
327
+ * Check if currently streaming
328
+ */
329
+ isStreaming() {
330
+ return this.streaming;
331
+ }
332
+ /**
333
+ * Create an async iterable from a ReadableStream
334
+ */
335
+ createStreamIterable(body) {
336
+ const reader = body.getReader();
337
+ const decoder = new TextDecoder();
338
+ let buffer = "";
339
+ const chunkQueue = [];
340
+ let streamDone = false;
341
+ const self = this;
342
+ return {
343
+ [Symbol.asyncIterator]() {
344
+ return {
345
+ async next() {
346
+ if (chunkQueue.length > 0) {
347
+ return { value: chunkQueue.shift(), done: false };
348
+ }
349
+ if (streamDone) {
350
+ return { value: void 0, done: true };
351
+ }
352
+ try {
353
+ const { done, value } = await reader.read();
354
+ if (done) {
355
+ self.streaming = false;
356
+ streamDone = true;
357
+ if (buffer.trim()) {
358
+ const chunk = parseSSELine(buffer.trim());
359
+ if (chunk) {
360
+ buffer = "";
361
+ return { value: chunk, done: false };
362
+ }
363
+ }
364
+ return {
365
+ value: void 0,
366
+ done: true
367
+ };
368
+ }
369
+ buffer += decoder.decode(value, { stream: true });
370
+ const lines = buffer.split("\n");
371
+ buffer = lines.pop() || "";
372
+ for (const line of lines) {
373
+ const chunk = parseSSELine(line);
374
+ if (chunk) {
375
+ chunkQueue.push(chunk);
376
+ }
377
+ }
378
+ if (chunkQueue.length > 0) {
379
+ return { value: chunkQueue.shift(), done: false };
380
+ }
381
+ return this.next();
382
+ } catch (error) {
383
+ self.streaming = false;
384
+ streamDone = true;
385
+ if (error.name === "AbortError") {
386
+ return {
387
+ value: void 0,
388
+ done: true
389
+ };
390
+ }
391
+ throw error;
392
+ }
393
+ }
394
+ };
395
+ }
396
+ };
397
+ }
398
+ };
399
+
400
+ // src/chat/classes/AbstractChat.ts
401
+ function buildToolResultContentForAI(result, tool, args) {
402
+ if (typeof result === "string") return result;
403
+ const typedResult = result;
404
+ const responseMode = typedResult?._aiResponseMode ?? tool?.aiResponseMode ?? "full";
405
+ if (typedResult?._aiContent) {
406
+ return JSON.stringify(typedResult._aiContent);
407
+ }
408
+ let aiContext = typedResult?._aiContext;
409
+ if (!aiContext && tool?.aiContext) {
410
+ aiContext = typeof tool.aiContext === "function" ? tool.aiContext(typedResult, args ?? {}) : tool.aiContext;
411
+ }
412
+ switch (responseMode) {
413
+ case "none":
414
+ return aiContext ?? "[Result displayed to user]";
415
+ case "brief":
416
+ return aiContext ?? "[Tool executed successfully]";
417
+ case "full":
418
+ default:
419
+ if (aiContext) {
420
+ const { _aiResponseMode, _aiContext, _aiContent, ...dataOnly } = typedResult ?? {};
421
+ return `${aiContext}
422
+
423
+ Full data: ${JSON.stringify(dataOnly)}`;
424
+ }
425
+ return JSON.stringify(result);
426
+ }
427
+ }
428
+ var AbstractChat = class {
429
+ constructor(init) {
430
+ // Event handlers
431
+ this.eventHandlers = /* @__PURE__ */ new Map();
432
+ // Current streaming state
433
+ this.streamState = null;
434
+ /**
435
+ * Dynamic context from useAIContext hook
436
+ */
437
+ this.dynamicContext = "";
438
+ this.config = {
439
+ runtimeUrl: init.runtimeUrl,
440
+ llm: init.llm,
441
+ systemPrompt: init.systemPrompt,
442
+ streaming: init.streaming ?? true,
443
+ headers: init.headers,
444
+ threadId: init.threadId,
445
+ debug: init.debug
446
+ };
447
+ this.state = init.state ?? new SimpleChatState();
448
+ this.transport = init.transport ?? new HttpTransport({
449
+ url: init.runtimeUrl,
450
+ headers: init.headers,
451
+ streaming: init.streaming ?? true
452
+ });
453
+ this.callbacks = init.callbacks ?? {};
454
+ if (init.initialMessages?.length) {
455
+ this.state.setMessages(init.initialMessages);
456
+ }
457
+ }
458
+ // ============================================
459
+ // Public Getters
460
+ // ============================================
461
+ get messages() {
462
+ return this.state.messages;
463
+ }
464
+ get status() {
465
+ return this.state.status;
466
+ }
467
+ get error() {
468
+ return this.state.error;
469
+ }
470
+ get isStreaming() {
471
+ return this.transport.isStreaming();
472
+ }
473
+ // ============================================
474
+ // Public Actions
475
+ // ============================================
476
+ /**
477
+ * Send a message
478
+ */
479
+ async sendMessage(content, attachments) {
480
+ this.debug("sendMessage", { content, attachments });
481
+ try {
482
+ const userMessage = createUserMessage(content, attachments);
483
+ this.state.pushMessage(userMessage);
484
+ this.state.status = "submitted";
485
+ this.state.error = void 0;
486
+ this.callbacks.onMessagesChange?.(this.state.messages);
487
+ this.callbacks.onStatusChange?.("submitted");
488
+ await Promise.resolve();
489
+ await this.processRequest();
490
+ } catch (error) {
491
+ this.handleError(error);
492
+ }
493
+ }
494
+ /**
495
+ * Continue with tool results
496
+ *
497
+ * Automatically handles `addAsUserMessage` flag in results (e.g., screenshots).
498
+ * When a tool result has this flag, the attachment is extracted and sent as
499
+ * a user message so the AI can see it (e.g., for vision analysis).
500
+ */
501
+ async continueWithToolResults(toolResults) {
502
+ this.debug("continueWithToolResults", toolResults);
503
+ try {
504
+ const attachmentsToAdd = [];
505
+ for (const { toolCallId, result } of toolResults) {
506
+ const typedResult = result;
507
+ let messageContent;
508
+ if (typedResult?.addAsUserMessage && typedResult.data?.attachment) {
509
+ this.debug(
510
+ "Tool result has attachment to add as user message",
511
+ typedResult.data.attachment.type
512
+ );
513
+ attachmentsToAdd.push(typedResult.data.attachment);
514
+ messageContent = JSON.stringify({
515
+ success: true,
516
+ message: typedResult.message || "Content shared in conversation."
517
+ });
518
+ } else {
519
+ messageContent = typeof result === "string" ? result : JSON.stringify(result);
520
+ }
521
+ const toolMessage = {
522
+ id: generateMessageId(),
523
+ role: "tool",
524
+ content: messageContent,
525
+ toolCallId,
526
+ createdAt: /* @__PURE__ */ new Date()
527
+ };
528
+ this.state.pushMessage(toolMessage);
529
+ }
530
+ if (attachmentsToAdd.length > 0) {
531
+ this.debug(
532
+ "Adding user message with attachments",
533
+ attachmentsToAdd.length
534
+ );
535
+ const userMessage = {
536
+ id: generateMessageId(),
537
+ role: "user",
538
+ content: "Here's my screen:",
539
+ attachments: attachmentsToAdd,
540
+ createdAt: /* @__PURE__ */ new Date()
541
+ };
542
+ this.state.pushMessage(userMessage);
543
+ }
544
+ this.state.status = "submitted";
545
+ this.callbacks.onMessagesChange?.(this.state.messages);
546
+ this.callbacks.onStatusChange?.("submitted");
547
+ await Promise.resolve();
548
+ await this.processRequest();
549
+ } catch (error) {
550
+ this.handleError(error);
551
+ }
552
+ }
553
+ /**
554
+ * Stop generation
555
+ */
556
+ stop() {
557
+ this.transport.abort();
558
+ this.state.status = "ready";
559
+ this.callbacks.onStatusChange?.("ready");
560
+ }
561
+ /**
562
+ * Clear all messages
563
+ */
564
+ clearMessages() {
565
+ this.state.clearMessages();
566
+ this.callbacks.onMessagesChange?.([]);
567
+ }
568
+ /**
569
+ * Set messages directly
570
+ */
571
+ setMessages(messages) {
572
+ this.state.setMessages(messages);
573
+ this.callbacks.onMessagesChange?.(messages);
574
+ }
575
+ /**
576
+ * Regenerate last response
577
+ */
578
+ async regenerate(messageId) {
579
+ const messages = this.state.messages;
580
+ let targetIndex = messages.length - 1;
581
+ if (messageId) {
582
+ targetIndex = messages.findIndex((m) => m.id === messageId);
583
+ } else {
584
+ for (let i = messages.length - 1; i >= 0; i--) {
585
+ if (messages[i].role === "assistant") {
586
+ targetIndex = i;
587
+ break;
588
+ }
589
+ }
590
+ }
591
+ if (targetIndex > 0) {
592
+ this.state.setMessages(messages.slice(0, targetIndex));
593
+ this.callbacks.onMessagesChange?.(this.state.messages);
594
+ await this.processRequest();
595
+ }
596
+ }
597
+ // ============================================
598
+ // Event Handling
599
+ // ============================================
600
+ /**
601
+ * Subscribe to events
602
+ */
603
+ on(event, handler) {
604
+ if (!this.eventHandlers.has(event)) {
605
+ this.eventHandlers.set(event, /* @__PURE__ */ new Set());
606
+ }
607
+ this.eventHandlers.get(event).add(handler);
608
+ return () => {
609
+ this.eventHandlers.get(event)?.delete(handler);
610
+ };
611
+ }
612
+ /**
613
+ * Emit an event
614
+ */
615
+ emit(type, data) {
616
+ const event = { type, ...data };
617
+ this.eventHandlers.get(type)?.forEach((handler) => handler(event));
618
+ }
619
+ // ============================================
620
+ // Protected Methods
621
+ // ============================================
622
+ /**
623
+ * Process a chat request
624
+ */
625
+ async processRequest() {
626
+ const request = this.buildRequest();
627
+ const response = await this.transport.send(request);
628
+ if (this.isAsyncIterable(response)) {
629
+ await this.handleStreamResponse(response);
630
+ } else {
631
+ this.handleJsonResponse(response);
632
+ }
633
+ }
634
+ /**
635
+ * Set tools available for the LLM
636
+ */
637
+ setTools(tools) {
638
+ this.config.tools = tools;
639
+ }
640
+ /**
641
+ * Set dynamic context (appended to system prompt)
642
+ */
643
+ setContext(context) {
644
+ this.dynamicContext = context;
645
+ this.debug("Context updated", { length: context.length });
646
+ }
647
+ /**
648
+ * Build the request payload
649
+ */
650
+ buildRequest() {
651
+ const tools = this.config.tools?.map((tool) => ({
652
+ name: tool.name,
653
+ description: tool.description,
654
+ inputSchema: tool.inputSchema
655
+ }));
656
+ const toolCallMap = /* @__PURE__ */ new Map();
657
+ for (const msg of this.state.messages) {
658
+ if (msg.role === "assistant" && msg.toolCalls) {
659
+ for (const tc of msg.toolCalls) {
660
+ try {
661
+ const args = tc.function?.arguments ? JSON.parse(tc.function.arguments) : {};
662
+ toolCallMap.set(tc.id, { toolName: tc.function.name, args });
663
+ } catch {
664
+ toolCallMap.set(tc.id, { toolName: tc.function.name, args: {} });
665
+ }
666
+ }
667
+ }
668
+ }
669
+ const toolDefMap = /* @__PURE__ */ new Map();
670
+ if (this.config.tools) {
671
+ for (const tool of this.config.tools) {
672
+ toolDefMap.set(tool.name, {
673
+ name: tool.name,
674
+ aiResponseMode: tool.aiResponseMode,
675
+ aiContext: tool.aiContext
676
+ });
677
+ }
678
+ }
679
+ return {
680
+ messages: this.state.messages.map((m) => {
681
+ if (m.role === "tool" && m.content && m.toolCallId) {
682
+ try {
683
+ const fullResult = JSON.parse(m.content);
684
+ const toolCallInfo = toolCallMap.get(m.toolCallId);
685
+ const toolDef = toolCallInfo ? toolDefMap.get(toolCallInfo.toolName) : void 0;
686
+ const toolArgs = toolCallInfo?.args;
687
+ const transformedContent = buildToolResultContentForAI(
688
+ fullResult,
689
+ toolDef,
690
+ toolArgs
691
+ );
692
+ return {
693
+ role: m.role,
694
+ content: transformedContent,
695
+ tool_call_id: m.toolCallId
696
+ };
697
+ } catch (e) {
698
+ this.debug("Failed to parse tool message JSON", {
699
+ content: m.content?.slice(0, 100),
700
+ error: e instanceof Error ? e.message : String(e)
701
+ });
702
+ return {
703
+ role: m.role,
704
+ content: m.content,
705
+ tool_call_id: m.toolCallId
706
+ };
707
+ }
708
+ }
709
+ return {
710
+ role: m.role,
711
+ content: m.content,
712
+ tool_calls: m.toolCalls,
713
+ tool_call_id: m.toolCallId,
714
+ attachments: m.attachments
715
+ };
716
+ }),
717
+ threadId: this.config.threadId,
718
+ systemPrompt: this.dynamicContext ? `${this.config.systemPrompt || ""}
719
+
720
+ ## Current App Context:
721
+ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
722
+ llm: this.config.llm,
723
+ tools: tools?.length ? tools : void 0
724
+ };
725
+ }
726
+ /**
727
+ * Handle streaming response
728
+ */
729
+ async handleStreamResponse(stream) {
730
+ this.state.status = "streaming";
731
+ this.callbacks.onStatusChange?.("streaming");
732
+ const assistantMessage = createEmptyAssistantMessage();
733
+ this.state.pushMessage(assistantMessage);
734
+ this.streamState = createStreamState(assistantMessage.id);
735
+ this.callbacks.onMessageStart?.(assistantMessage.id);
736
+ this.debug("handleStreamResponse", "Starting to process stream");
737
+ let chunkCount = 0;
738
+ let toolCallsEmitted = false;
739
+ for await (const chunk of stream) {
740
+ chunkCount++;
741
+ this.debug("chunk", { count: chunkCount, type: chunk.type });
742
+ if (chunk.type === "error") {
743
+ const error = new Error(chunk.message || "Stream error");
744
+ this.handleError(error);
745
+ return;
746
+ }
747
+ this.streamState = processStreamChunk(chunk, this.streamState);
748
+ const updatedMessage = streamStateToMessage(this.streamState);
749
+ this.state.updateLastMessage(() => updatedMessage);
750
+ if (chunk.type === "message:delta") {
751
+ this.callbacks.onMessageDelta?.(assistantMessage.id, chunk.content);
752
+ }
753
+ if (requiresToolExecution(chunk) && !toolCallsEmitted) {
754
+ toolCallsEmitted = true;
755
+ this.debug("toolCalls", { toolCalls: updatedMessage.toolCalls });
756
+ this.emit("toolCalls", { toolCalls: updatedMessage.toolCalls });
757
+ }
758
+ if (isStreamDone(chunk)) {
759
+ this.debug("streamDone", { chunk });
760
+ break;
761
+ }
762
+ }
763
+ this.debug("handleStreamResponse", `Processed ${chunkCount} chunks`);
764
+ const finalMessage = streamStateToMessage(this.streamState);
765
+ this.state.updateLastMessage(() => finalMessage);
766
+ this.state.status = "ready";
767
+ if (!finalMessage.content && (!finalMessage.toolCalls || finalMessage.toolCalls.length === 0)) {
768
+ this.debug("warning", "Empty response - no content and no tool calls");
769
+ }
770
+ this.callbacks.onMessageFinish?.(finalMessage);
771
+ this.callbacks.onStatusChange?.("ready");
772
+ this.callbacks.onMessagesChange?.(this.state.messages);
773
+ this.callbacks.onFinish?.(this.state.messages);
774
+ this.emit("done", {});
775
+ this.streamState = null;
776
+ }
777
+ /**
778
+ * Handle JSON (non-streaming) response
779
+ */
780
+ handleJsonResponse(response) {
781
+ for (const msg of response.messages ?? []) {
782
+ const message = {
783
+ id: generateMessageId(),
784
+ role: msg.role,
785
+ content: msg.content ?? "",
786
+ toolCalls: msg.tool_calls,
787
+ createdAt: /* @__PURE__ */ new Date()
788
+ };
789
+ this.state.pushMessage(message);
790
+ }
791
+ this.state.status = "ready";
792
+ this.callbacks.onStatusChange?.("ready");
793
+ this.callbacks.onMessagesChange?.(this.state.messages);
794
+ this.callbacks.onFinish?.(this.state.messages);
795
+ if (response.requiresAction && this.state.messages.length > 0) {
796
+ const lastMessage = this.state.messages[this.state.messages.length - 1];
797
+ if (lastMessage?.toolCalls?.length) {
798
+ this.emit("toolCalls", { toolCalls: lastMessage.toolCalls });
799
+ }
800
+ }
801
+ this.emit("done", {});
802
+ }
803
+ /**
804
+ * Handle errors
805
+ */
806
+ handleError(error) {
807
+ this.debug("error", error);
808
+ this.state.error = error;
809
+ this.state.status = "error";
810
+ this.callbacks.onError?.(error);
811
+ this.callbacks.onStatusChange?.("error");
812
+ this.emit("error", { error });
813
+ }
814
+ /**
815
+ * Debug logging
816
+ */
817
+ debug(action, data) {
818
+ if (this.config.debug) {
819
+ console.log(`[AbstractChat] ${action}`, data);
820
+ }
821
+ }
822
+ /**
823
+ * Type guard for async iterable
824
+ */
825
+ isAsyncIterable(value) {
826
+ return value !== null && typeof value === "object" && Symbol.asyncIterator in value;
827
+ }
828
+ /**
829
+ * Dispose and cleanup
830
+ */
831
+ dispose() {
832
+ this.stop();
833
+ this.eventHandlers.clear();
834
+ }
835
+ };
836
+
837
+ // src/chat/AbstractAgentLoop.ts
838
+ var AbstractAgentLoop = class {
839
+ constructor(config = {}, callbacks = {}) {
840
+ // Internal state
841
+ this._toolExecutions = [];
842
+ this._iteration = 0;
843
+ this._maxIterationsReached = false;
844
+ this._isProcessing = false;
845
+ // Registered tools
846
+ this.registeredTools = /* @__PURE__ */ new Map();
847
+ // Pending approvals
848
+ this.pendingApprovals = /* @__PURE__ */ new Map();
849
+ this.config = config;
850
+ this.callbacks = callbacks;
851
+ this._maxIterations = config.maxIterations ?? 20;
852
+ this._maxExecutionHistory = config.maxExecutionHistory ?? 100;
853
+ if (config.tools) {
854
+ for (const tool of config.tools) {
855
+ this.registerTool(tool);
856
+ }
857
+ }
858
+ }
859
+ // ============================================
860
+ // Getters
861
+ // ============================================
862
+ get toolExecutions() {
863
+ return this._toolExecutions;
864
+ }
865
+ get iteration() {
866
+ return this._iteration;
867
+ }
868
+ get maxIterations() {
869
+ return this._maxIterations;
870
+ }
871
+ get maxIterationsReached() {
872
+ return this._maxIterationsReached;
873
+ }
874
+ get isProcessing() {
875
+ return this._isProcessing;
876
+ }
877
+ get state() {
878
+ return {
879
+ toolExecutions: this._toolExecutions,
880
+ iteration: this._iteration,
881
+ maxIterations: this._maxIterations,
882
+ maxIterationsReached: this._maxIterationsReached,
883
+ isProcessing: this._isProcessing
884
+ };
885
+ }
886
+ get pendingApprovalExecutions() {
887
+ return this._toolExecutions.filter(
888
+ (exec) => exec.approvalStatus === "required"
889
+ );
890
+ }
891
+ get tools() {
892
+ return Array.from(this.registeredTools.values());
893
+ }
894
+ // ============================================
895
+ // Private setters with callbacks
896
+ // ============================================
897
+ setToolExecutions(executions) {
898
+ this._toolExecutions = executions;
899
+ this.callbacks.onExecutionsChange?.(executions);
900
+ }
901
+ setIteration(iteration) {
902
+ this._iteration = iteration;
903
+ }
904
+ setProcessing(processing) {
905
+ this._isProcessing = processing;
906
+ }
907
+ addToolExecution(execution) {
908
+ this._toolExecutions = [...this._toolExecutions, execution];
909
+ if (this._toolExecutions.length > this._maxExecutionHistory) {
910
+ this._toolExecutions = this._toolExecutions.slice(
911
+ -this._maxExecutionHistory
912
+ );
913
+ }
914
+ this.callbacks.onExecutionsChange?.(this._toolExecutions);
915
+ }
916
+ updateToolExecution(id, update) {
917
+ this._toolExecutions = this._toolExecutions.map(
918
+ (exec) => exec.id === id ? { ...exec, ...update } : exec
919
+ );
920
+ this.callbacks.onExecutionsChange?.(this._toolExecutions);
921
+ }
922
+ // ============================================
923
+ // Tool Registration
924
+ // ============================================
925
+ /**
926
+ * Register a tool
927
+ */
928
+ registerTool(tool) {
929
+ this.registeredTools.set(tool.name, tool);
930
+ }
931
+ /**
932
+ * Unregister a tool
933
+ */
934
+ unregisterTool(name) {
935
+ this.registeredTools.delete(name);
936
+ }
937
+ /**
938
+ * Get a registered tool
939
+ */
940
+ getTool(name) {
941
+ return this.registeredTools.get(name);
942
+ }
943
+ // ============================================
944
+ // Tool Execution
945
+ // ============================================
946
+ /**
947
+ * Execute tool calls from LLM response
948
+ * Returns tool results for sending back to LLM
949
+ */
950
+ async executeToolCalls(toolCalls) {
951
+ if (this._iteration >= this._maxIterations) {
952
+ this._maxIterationsReached = true;
953
+ this.callbacks.onMaxIterationsReached?.();
954
+ return [];
955
+ }
956
+ this.setIteration(this._iteration + 1);
957
+ const results = [];
958
+ for (const toolCall of toolCalls) {
959
+ const result = await this.executeSingleTool(toolCall);
960
+ results.push(result);
961
+ }
962
+ return results;
963
+ }
964
+ /**
965
+ * Execute a single tool
966
+ */
967
+ async executeSingleTool(toolCall) {
968
+ const tool = this.registeredTools.get(toolCall.name);
969
+ const execution = {
970
+ id: toolCall.id,
971
+ toolCallId: toolCall.id,
972
+ name: toolCall.name,
973
+ args: toolCall.args,
974
+ status: "pending",
975
+ approvalStatus: "none",
976
+ startedAt: /* @__PURE__ */ new Date()
977
+ };
978
+ this.addToolExecution(execution);
979
+ this.callbacks.onToolStart?.(execution);
980
+ if (!tool) {
981
+ const errorResult = {
982
+ toolCallId: toolCall.id,
983
+ success: false,
984
+ error: `Tool "${toolCall.name}" not found`
985
+ };
986
+ this.updateToolExecution(toolCall.id, {
987
+ status: "failed",
988
+ error: errorResult.error,
989
+ completedAt: /* @__PURE__ */ new Date()
990
+ });
991
+ return errorResult;
992
+ }
993
+ if (tool.needsApproval && !this.config.autoApprove) {
994
+ typeof tool.approvalMessage === "function" ? tool.approvalMessage(toolCall.args) : tool.approvalMessage;
995
+ execution.approvalStatus = "required";
996
+ this.updateToolExecution(toolCall.id, {
997
+ approvalStatus: "required"
998
+ });
999
+ this.callbacks.onApprovalRequired?.(execution);
1000
+ const approved = await this.waitForApproval(toolCall.id, execution);
1001
+ if (!approved) {
1002
+ const rejectedResult = {
1003
+ toolCallId: toolCall.id,
1004
+ success: false,
1005
+ error: "Tool execution was rejected by user"
1006
+ };
1007
+ this.updateToolExecution(toolCall.id, {
1008
+ status: "rejected",
1009
+ approvalStatus: "rejected",
1010
+ error: rejectedResult.error,
1011
+ completedAt: /* @__PURE__ */ new Date()
1012
+ });
1013
+ return rejectedResult;
1014
+ }
1015
+ this.updateToolExecution(toolCall.id, {
1016
+ approvalStatus: "approved"
1017
+ });
1018
+ }
1019
+ this.updateToolExecution(toolCall.id, { status: "executing" });
1020
+ try {
1021
+ if (!tool.handler) {
1022
+ throw new Error(`Tool "${toolCall.name}" has no handler`);
1023
+ }
1024
+ const result = await tool.handler(toolCall.args, {
1025
+ data: { toolCallId: toolCall.id }
1026
+ });
1027
+ this.updateToolExecution(toolCall.id, {
1028
+ status: "completed",
1029
+ result,
1030
+ completedAt: /* @__PURE__ */ new Date()
1031
+ });
1032
+ const updatedExecution = this._toolExecutions.find(
1033
+ (e) => e.id === toolCall.id
1034
+ );
1035
+ if (updatedExecution) {
1036
+ this.callbacks.onToolComplete?.(updatedExecution);
1037
+ }
1038
+ return {
1039
+ toolCallId: toolCall.id,
1040
+ success: true,
1041
+ result
1042
+ };
1043
+ } catch (error) {
1044
+ const errorMessage = error instanceof Error ? error.message : String(error);
1045
+ this.updateToolExecution(toolCall.id, {
1046
+ status: "failed",
1047
+ error: errorMessage,
1048
+ completedAt: /* @__PURE__ */ new Date()
1049
+ });
1050
+ return {
1051
+ toolCallId: toolCall.id,
1052
+ success: false,
1053
+ error: errorMessage
1054
+ };
1055
+ }
1056
+ }
1057
+ /**
1058
+ * Wait for user approval
1059
+ */
1060
+ waitForApproval(executionId, execution) {
1061
+ return new Promise((resolve) => {
1062
+ this.pendingApprovals.set(executionId, { resolve, execution });
1063
+ });
1064
+ }
1065
+ // ============================================
1066
+ // Actions (implements AgentLoopActions)
1067
+ // ============================================
1068
+ /**
1069
+ * Approve a tool execution
1070
+ */
1071
+ approveToolExecution(executionId, _permissionLevel) {
1072
+ const pending = this.pendingApprovals.get(executionId);
1073
+ if (pending) {
1074
+ pending.resolve(true);
1075
+ this.pendingApprovals.delete(executionId);
1076
+ }
1077
+ }
1078
+ /**
1079
+ * Reject a tool execution
1080
+ */
1081
+ rejectToolExecution(executionId, reason, _permissionLevel) {
1082
+ const pending = this.pendingApprovals.get(executionId);
1083
+ if (pending) {
1084
+ if (reason) {
1085
+ this.updateToolExecution(executionId, {
1086
+ error: reason
1087
+ });
1088
+ }
1089
+ pending.resolve(false);
1090
+ this.pendingApprovals.delete(executionId);
1091
+ }
1092
+ }
1093
+ /**
1094
+ * Clear all tool executions
1095
+ */
1096
+ clearToolExecutions() {
1097
+ this.setToolExecutions([]);
1098
+ this.setIteration(0);
1099
+ this._maxIterationsReached = false;
1100
+ }
1101
+ // ============================================
1102
+ // State Management
1103
+ // ============================================
1104
+ /**
1105
+ * Reset the agent loop for a new conversation
1106
+ */
1107
+ reset() {
1108
+ this.clearToolExecutions();
1109
+ this.pendingApprovals.clear();
1110
+ }
1111
+ /**
1112
+ * Update configuration
1113
+ */
1114
+ updateConfig(config) {
1115
+ this.config = { ...this.config, ...config };
1116
+ if (config.maxIterations !== void 0) {
1117
+ this._maxIterations = config.maxIterations;
1118
+ }
1119
+ }
1120
+ /**
1121
+ * Update callbacks
1122
+ */
1123
+ updateCallbacks(callbacks) {
1124
+ this.callbacks = { ...this.callbacks, ...callbacks };
1125
+ }
1126
+ // ============================================
1127
+ // Cleanup
1128
+ // ============================================
1129
+ /**
1130
+ * Dispose of resources
1131
+ */
1132
+ dispose() {
1133
+ for (const [id, pending] of this.pendingApprovals) {
1134
+ pending.resolve(false);
1135
+ }
1136
+ this.pendingApprovals.clear();
1137
+ this.registeredTools.clear();
1138
+ this._toolExecutions = [];
1139
+ }
1140
+ };
1141
+
1142
+ // src/chat/ChatWithTools.ts
1143
+ var ChatWithTools = class {
1144
+ constructor(config, callbacks = {}) {
1145
+ this.config = config;
1146
+ this.callbacks = callbacks;
1147
+ this.agentLoop = new AbstractAgentLoop(
1148
+ {
1149
+ maxIterations: config.maxIterations ?? 20,
1150
+ tools: config.tools
1151
+ },
1152
+ {
1153
+ onExecutionsChange: (executions) => {
1154
+ callbacks.onToolExecutionsChange?.(executions);
1155
+ },
1156
+ onApprovalRequired: (execution) => {
1157
+ callbacks.onApprovalRequired?.(execution);
1158
+ }
1159
+ }
1160
+ );
1161
+ this.chat = new AbstractChat({
1162
+ runtimeUrl: config.runtimeUrl,
1163
+ llm: config.llm,
1164
+ systemPrompt: config.systemPrompt,
1165
+ streaming: config.streaming,
1166
+ headers: config.headers,
1167
+ threadId: config.threadId,
1168
+ debug: config.debug,
1169
+ initialMessages: config.initialMessages,
1170
+ state: config.state,
1171
+ transport: config.transport,
1172
+ callbacks: {
1173
+ onMessagesChange: callbacks.onMessagesChange,
1174
+ onStatusChange: callbacks.onStatusChange,
1175
+ onError: callbacks.onError,
1176
+ onMessageStart: callbacks.onMessageStart,
1177
+ onMessageDelta: callbacks.onMessageDelta,
1178
+ onMessageFinish: callbacks.onMessageFinish,
1179
+ onToolCalls: callbacks.onToolCalls,
1180
+ onFinish: callbacks.onFinish
1181
+ }
1182
+ });
1183
+ this.wireEvents();
1184
+ }
1185
+ /**
1186
+ * Wire up internal events between chat and agent loop
1187
+ */
1188
+ wireEvents() {
1189
+ this.chat.on("toolCalls", async (event) => {
1190
+ const toolCalls = event.toolCalls;
1191
+ if (!toolCalls?.length) return;
1192
+ this.debug("Tool calls received:", toolCalls);
1193
+ const toolCallInfos = toolCalls.map((tc) => {
1194
+ const tcAny = tc;
1195
+ const name = tcAny.function?.name ?? tcAny.name ?? "";
1196
+ let args = {};
1197
+ if (tcAny.function?.arguments) {
1198
+ try {
1199
+ args = JSON.parse(tcAny.function.arguments);
1200
+ } catch {
1201
+ args = {};
1202
+ }
1203
+ } else if (tcAny.args) {
1204
+ args = tcAny.args;
1205
+ }
1206
+ return { id: tc.id, name, args };
1207
+ });
1208
+ try {
1209
+ const results = await this.agentLoop.executeToolCalls(toolCallInfos);
1210
+ this.debug("Tool results:", results);
1211
+ if (results.length > 0) {
1212
+ const toolResults = results.map((r) => ({
1213
+ toolCallId: r.toolCallId,
1214
+ result: r.success ? r.result : { success: false, error: r.error }
1215
+ }));
1216
+ await this.chat.continueWithToolResults(toolResults);
1217
+ }
1218
+ } catch (error) {
1219
+ this.debug("Error executing tools:", error);
1220
+ console.error("[ChatWithTools] Tool execution error:", error);
1221
+ }
1222
+ });
1223
+ }
1224
+ // ============================================
1225
+ // Chat Getters
1226
+ // ============================================
1227
+ get messages() {
1228
+ return this.chat.messages;
1229
+ }
1230
+ get status() {
1231
+ return this.chat.status;
1232
+ }
1233
+ get error() {
1234
+ return this.chat.error;
1235
+ }
1236
+ get isStreaming() {
1237
+ return this.chat.isStreaming;
1238
+ }
1239
+ // ============================================
1240
+ // Tool Execution Getters
1241
+ // ============================================
1242
+ get toolExecutions() {
1243
+ return this.agentLoop.toolExecutions;
1244
+ }
1245
+ get tools() {
1246
+ return this.agentLoop.tools;
1247
+ }
1248
+ get iteration() {
1249
+ return this.agentLoop.iteration;
1250
+ }
1251
+ get maxIterations() {
1252
+ return this.agentLoop.maxIterations;
1253
+ }
1254
+ get isProcessing() {
1255
+ return this.agentLoop.isProcessing;
1256
+ }
1257
+ // ============================================
1258
+ // Chat Actions
1259
+ // ============================================
1260
+ /**
1261
+ * Send a message
1262
+ */
1263
+ async sendMessage(content, attachments) {
1264
+ await this.chat.sendMessage(content, attachments);
1265
+ }
1266
+ /**
1267
+ * Stop generation
1268
+ */
1269
+ stop() {
1270
+ this.chat.stop();
1271
+ }
1272
+ /**
1273
+ * Clear all messages
1274
+ */
1275
+ clearMessages() {
1276
+ this.chat.clearMessages();
1277
+ this.agentLoop.clearToolExecutions();
1278
+ }
1279
+ /**
1280
+ * Set messages directly
1281
+ */
1282
+ setMessages(messages) {
1283
+ this.chat.setMessages(messages);
1284
+ }
1285
+ /**
1286
+ * Regenerate last response
1287
+ */
1288
+ async regenerate(messageId) {
1289
+ await this.chat.regenerate(messageId);
1290
+ }
1291
+ /**
1292
+ * Set tools available for the LLM
1293
+ */
1294
+ setTools(tools) {
1295
+ this.chat.setTools(tools);
1296
+ }
1297
+ /**
1298
+ * Set dynamic context (from useAIContext hook)
1299
+ */
1300
+ setContext(context) {
1301
+ this.chat.setContext(context);
1302
+ }
1303
+ // ============================================
1304
+ // Tool Registration
1305
+ // ============================================
1306
+ /**
1307
+ * Register a tool
1308
+ */
1309
+ registerTool(tool) {
1310
+ this.agentLoop.registerTool(tool);
1311
+ this.chat.setTools(this.agentLoop.tools);
1312
+ }
1313
+ /**
1314
+ * Unregister a tool
1315
+ */
1316
+ unregisterTool(name) {
1317
+ this.agentLoop.unregisterTool(name);
1318
+ this.chat.setTools(this.agentLoop.tools);
1319
+ }
1320
+ // ============================================
1321
+ // Tool Approval
1322
+ // ============================================
1323
+ /**
1324
+ * Approve a tool execution
1325
+ */
1326
+ approveToolExecution(id, permissionLevel) {
1327
+ this.agentLoop.approveToolExecution(id, permissionLevel);
1328
+ }
1329
+ /**
1330
+ * Reject a tool execution
1331
+ */
1332
+ rejectToolExecution(id, reason, permissionLevel) {
1333
+ this.agentLoop.rejectToolExecution(id, reason, permissionLevel);
1334
+ }
1335
+ /**
1336
+ * Clear tool executions
1337
+ */
1338
+ clearToolExecutions() {
1339
+ this.agentLoop.clearToolExecutions();
1340
+ }
1341
+ // ============================================
1342
+ // Event Subscriptions (for framework adapters)
1343
+ // ============================================
1344
+ /**
1345
+ * Subscribe to chat events
1346
+ */
1347
+ on(event, handler) {
1348
+ return this.chat.on(event, handler);
1349
+ }
1350
+ // ============================================
1351
+ // Cleanup
1352
+ // ============================================
1353
+ /**
1354
+ * Dispose and cleanup
1355
+ */
1356
+ dispose() {
1357
+ this.chat.dispose();
1358
+ this.agentLoop.dispose();
1359
+ }
1360
+ // ============================================
1361
+ // Private
1362
+ // ============================================
1363
+ debug(message, ...args) {
1364
+ if (this.config.debug) {
1365
+ console.log(`[ChatWithTools] ${message}`, ...args);
1366
+ }
1367
+ }
1368
+ };
1369
+
1370
+ // src/react/internal/ReactChatState.ts
1371
+ var ReactChatState = class {
1372
+ constructor(initialMessages) {
1373
+ this._messages = [];
1374
+ this._status = "ready";
1375
+ this._error = void 0;
1376
+ // Callbacks for React subscriptions (useSyncExternalStore)
1377
+ this.subscribers = /* @__PURE__ */ new Set();
1378
+ // ============================================
1379
+ // Subscription (for useSyncExternalStore)
1380
+ // ============================================
1381
+ /**
1382
+ * Subscribe to state changes.
1383
+ * Returns an unsubscribe function.
1384
+ *
1385
+ * @example
1386
+ * ```tsx
1387
+ * const messages = useSyncExternalStore(
1388
+ * state.subscribe,
1389
+ * () => state.messages
1390
+ * );
1391
+ * ```
1392
+ */
1393
+ this.subscribe = (callback) => {
1394
+ this.subscribers.add(callback);
1395
+ return () => {
1396
+ this.subscribers.delete(callback);
1397
+ };
1398
+ };
1399
+ if (initialMessages) {
1400
+ this._messages = initialMessages;
1401
+ }
1402
+ }
1403
+ // ============================================
1404
+ // Getters
1405
+ // ============================================
1406
+ get messages() {
1407
+ return this._messages;
1408
+ }
1409
+ get status() {
1410
+ return this._status;
1411
+ }
1412
+ get error() {
1413
+ return this._error;
1414
+ }
1415
+ // ============================================
1416
+ // Setters (trigger reactivity)
1417
+ // ============================================
1418
+ set messages(value) {
1419
+ this._messages = value;
1420
+ this.notify();
1421
+ }
1422
+ set status(value) {
1423
+ this._status = value;
1424
+ this.notify();
1425
+ }
1426
+ set error(value) {
1427
+ this._error = value;
1428
+ this.notify();
1429
+ }
1430
+ // ============================================
1431
+ // Mutations
1432
+ // ============================================
1433
+ pushMessage(message) {
1434
+ this._messages = [...this._messages, message];
1435
+ this.notify();
1436
+ }
1437
+ popMessage() {
1438
+ this._messages = this._messages.slice(0, -1);
1439
+ this.notify();
1440
+ }
1441
+ replaceMessage(index, message) {
1442
+ this._messages = this._messages.map((m, i) => i === index ? message : m);
1443
+ this.notify();
1444
+ }
1445
+ updateLastMessage(updater) {
1446
+ if (this._messages.length === 0) return;
1447
+ const lastIndex = this._messages.length - 1;
1448
+ const lastMessage = this._messages[lastIndex];
1449
+ this._messages = [
1450
+ ...this._messages.slice(0, lastIndex),
1451
+ updater(lastMessage)
1452
+ ];
1453
+ this.notify();
1454
+ }
1455
+ setMessages(messages) {
1456
+ this._messages = messages;
1457
+ this.notify();
1458
+ }
1459
+ clearMessages() {
1460
+ this._messages = [];
1461
+ this.notify();
1462
+ }
1463
+ // ============================================
1464
+ // Private Methods
1465
+ // ============================================
1466
+ notify() {
1467
+ this.subscribers.forEach((cb) => cb());
1468
+ }
1469
+ /**
1470
+ * Cleanup subscriptions
1471
+ */
1472
+ dispose() {
1473
+ this.subscribers.clear();
1474
+ }
1475
+ };
1476
+ function createReactChatState(initialMessages) {
1477
+ return new ReactChatState(initialMessages);
1478
+ }
1479
+
1480
+ // src/react/internal/ReactChatWithTools.ts
1481
+ var ReactChatWithTools = class extends ChatWithTools {
1482
+ constructor(config, callbacks = {}) {
1483
+ const reactState = new ReactChatState(config.initialMessages);
1484
+ super({ ...config, state: reactState }, callbacks);
1485
+ /**
1486
+ * Subscribe to state changes (for useSyncExternalStore)
1487
+ */
1488
+ this.subscribe = (callback) => {
1489
+ return this.reactState.subscribe(callback);
1490
+ };
1491
+ this.reactState = reactState;
1492
+ }
1493
+ /**
1494
+ * Dispose and cleanup
1495
+ */
1496
+ dispose() {
1497
+ super.dispose();
1498
+ this.reactState.dispose();
1499
+ }
1500
+ };
1501
+
1502
+ // src/react/utils/context-tree.ts
1503
+ function addNode(tree, node, parentId) {
1504
+ const newNode = {
1505
+ ...node,
1506
+ children: node.children || []
1507
+ };
1508
+ if (!parentId) {
1509
+ return [...tree, newNode];
1510
+ }
1511
+ return tree.map((n) => {
1512
+ if (n.id === parentId) {
1513
+ return { ...n, children: [...n.children, newNode] };
1514
+ }
1515
+ if (n.children.length > 0) {
1516
+ return { ...n, children: addNode(n.children, node, parentId) };
1517
+ }
1518
+ return n;
1519
+ });
1520
+ }
1521
+ function removeNode(tree, id) {
1522
+ return tree.reduce((result, node) => {
1523
+ if (node.id !== id) {
1524
+ const newNode = { ...node, children: removeNode(node.children, id) };
1525
+ result.push(newNode);
1526
+ }
1527
+ return result;
1528
+ }, []);
1529
+ }
1530
+ function getIndentPrefix(index, level) {
1531
+ if (level === 0) {
1532
+ return `${index + 1}.`;
1533
+ } else if (level === 1) {
1534
+ return `${String.fromCharCode(65 + index)}.`;
1535
+ } else if (level === 2) {
1536
+ return `${String.fromCharCode(97 + index)}.`;
1537
+ } else {
1538
+ return "-";
1539
+ }
1540
+ }
1541
+ function printNode(node, prefix = "", indentLevel = 0) {
1542
+ const indent = " ".repeat(indentLevel);
1543
+ const prefixLength = prefix.length + indent.length;
1544
+ const subsequentIndent = " ".repeat(prefixLength);
1545
+ const lines = node.value.split("\n");
1546
+ const firstLine = `${indent}${prefix}${lines[0]}`;
1547
+ const subsequentLines = lines.slice(1).map((line) => `${subsequentIndent}${line}`).join("\n");
1548
+ let output = `${firstLine}
1549
+ `;
1550
+ if (subsequentLines) {
1551
+ output += `${subsequentLines}
1552
+ `;
1553
+ }
1554
+ node.children.forEach((child, index) => {
1555
+ const childPrefix = `${" ".repeat(prefix.length)}${getIndentPrefix(index, indentLevel + 1)} `;
1556
+ output += printNode(child, childPrefix, indentLevel + 1);
1557
+ });
1558
+ return output;
1559
+ }
1560
+ function printTree(tree) {
1561
+ if (tree.length === 0) {
1562
+ return "";
1563
+ }
1564
+ let output = "";
1565
+ tree.forEach((node, index) => {
1566
+ if (index > 0) {
1567
+ output += "\n";
1568
+ }
1569
+ output += printNode(node, `${getIndentPrefix(index, 0)} `);
1570
+ });
1571
+ return output.trim();
1572
+ }
1573
+ var CopilotContext = react.createContext(null);
1574
+ function useCopilot() {
1575
+ const context = react.useContext(CopilotContext);
1576
+ if (!context) {
1577
+ throw new Error("useCopilot must be used within CopilotProvider");
1578
+ }
1579
+ return context;
1580
+ }
1581
+ function CopilotProvider({
1582
+ children,
1583
+ runtimeUrl,
1584
+ config,
1585
+ cloud,
1586
+ systemPrompt,
1587
+ tools: toolsConfig,
1588
+ threadId,
1589
+ initialMessages,
1590
+ onMessagesChange,
1591
+ onError,
1592
+ streaming,
1593
+ debug = false
1594
+ }) {
1595
+ const debugLog = react.useCallback(
1596
+ (...args) => {
1597
+ if (debug) console.log("[Copilot SDK]", ...args);
1598
+ },
1599
+ [debug]
1600
+ );
1601
+ react.useEffect(() => {
1602
+ if (toolsConfig && (toolsConfig.screenshot || toolsConfig.console || toolsConfig.network)) {
1603
+ console.warn(
1604
+ "[Copilot SDK] The `tools` prop is deprecated. Use the `useTools` hook instead."
1605
+ );
1606
+ }
1607
+ }, [toolsConfig]);
1608
+ const [toolExecutions, setToolExecutions] = react.useState([]);
1609
+ const chatRef = react.useRef(null);
1610
+ if (chatRef.current === null) {
1611
+ const uiInitialMessages = initialMessages?.map(
1612
+ (m) => ({
1613
+ id: m.id,
1614
+ role: m.role,
1615
+ content: m.content ?? "",
1616
+ createdAt: m.created_at ?? /* @__PURE__ */ new Date(),
1617
+ attachments: m.metadata?.attachments,
1618
+ toolCalls: m.tool_calls,
1619
+ toolCallId: m.tool_call_id
1620
+ })
1621
+ );
1622
+ chatRef.current = new ReactChatWithTools(
1623
+ {
1624
+ runtimeUrl,
1625
+ llm: config,
1626
+ systemPrompt,
1627
+ threadId,
1628
+ initialMessages: uiInitialMessages,
1629
+ streaming,
1630
+ debug
1631
+ },
1632
+ {
1633
+ onToolExecutionsChange: (executions) => {
1634
+ debugLog("Tool executions changed:", executions.length);
1635
+ setToolExecutions(executions);
1636
+ },
1637
+ onApprovalRequired: (execution) => {
1638
+ debugLog("Tool approval required:", execution.name);
1639
+ },
1640
+ onError: (error2) => {
1641
+ if (error2) onError?.(error2);
1642
+ }
1643
+ }
1644
+ );
1645
+ }
1646
+ const messages = react.useSyncExternalStore(
1647
+ chatRef.current.subscribe,
1648
+ () => chatRef.current.messages,
1649
+ () => chatRef.current.messages
1650
+ );
1651
+ const status = react.useSyncExternalStore(
1652
+ chatRef.current.subscribe,
1653
+ () => chatRef.current.status,
1654
+ () => "ready"
1655
+ );
1656
+ const errorFromChat = react.useSyncExternalStore(
1657
+ chatRef.current.subscribe,
1658
+ () => chatRef.current.error,
1659
+ () => void 0
1660
+ );
1661
+ const error = errorFromChat ?? null;
1662
+ const isLoading = status === "streaming" || status === "submitted";
1663
+ const registerTool = react.useCallback((tool) => {
1664
+ chatRef.current?.registerTool(tool);
1665
+ }, []);
1666
+ const unregisterTool = react.useCallback((name) => {
1667
+ chatRef.current?.unregisterTool(name);
1668
+ }, []);
1669
+ const approveToolExecution = react.useCallback(
1670
+ (id, permissionLevel) => {
1671
+ chatRef.current?.approveToolExecution(id, permissionLevel);
1672
+ },
1673
+ []
1674
+ );
1675
+ const rejectToolExecution = react.useCallback(
1676
+ (id, reason, permissionLevel) => {
1677
+ chatRef.current?.rejectToolExecution(id, reason, permissionLevel);
1678
+ },
1679
+ []
1680
+ );
1681
+ const registeredTools = chatRef.current?.tools ?? [];
1682
+ const pendingApprovals = toolExecutions.filter(
1683
+ (e) => e.approvalStatus === "required"
1684
+ );
1685
+ const actionsRef = react.useRef(/* @__PURE__ */ new Map());
1686
+ const [actionsVersion, setActionsVersion] = react.useState(0);
1687
+ const registerAction = react.useCallback((action) => {
1688
+ actionsRef.current.set(action.name, action);
1689
+ setActionsVersion((v) => v + 1);
1690
+ }, []);
1691
+ const unregisterAction = react.useCallback((name) => {
1692
+ actionsRef.current.delete(name);
1693
+ setActionsVersion((v) => v + 1);
1694
+ }, []);
1695
+ const registeredActions = react.useMemo(
1696
+ () => Array.from(actionsRef.current.values()),
1697
+ [actionsVersion]
1698
+ );
1699
+ const contextTreeRef = react.useRef([]);
1700
+ const contextIdCounter = react.useRef(0);
1701
+ const addContext = react.useCallback(
1702
+ (context, parentId) => {
1703
+ const id = `ctx-${++contextIdCounter.current}`;
1704
+ contextTreeRef.current = addNode(
1705
+ contextTreeRef.current,
1706
+ { id, value: context, parentId },
1707
+ parentId
1708
+ );
1709
+ const contextString = printTree(contextTreeRef.current);
1710
+ chatRef.current?.setContext(contextString);
1711
+ debugLog("Context added:", id);
1712
+ return id;
1713
+ },
1714
+ [debugLog]
1715
+ );
1716
+ const removeContext = react.useCallback(
1717
+ (id) => {
1718
+ contextTreeRef.current = removeNode(contextTreeRef.current, id);
1719
+ const contextString = printTree(contextTreeRef.current);
1720
+ chatRef.current?.setContext(contextString);
1721
+ debugLog("Context removed:", id);
1722
+ },
1723
+ [debugLog]
1724
+ );
1725
+ const sendMessage = react.useCallback(
1726
+ async (content, attachments) => {
1727
+ debugLog("Sending message:", content);
1728
+ await chatRef.current?.sendMessage(content, attachments);
1729
+ },
1730
+ [debugLog]
1731
+ );
1732
+ const stop = react.useCallback(() => {
1733
+ chatRef.current?.stop();
1734
+ }, []);
1735
+ const clearMessages = react.useCallback(() => {
1736
+ chatRef.current?.clearMessages();
1737
+ }, []);
1738
+ const regenerate = react.useCallback(async (messageId) => {
1739
+ await chatRef.current?.regenerate(messageId);
1740
+ }, []);
1741
+ react.useEffect(() => {
1742
+ if (onMessagesChange && messages.length > 0) {
1743
+ const coreMessages = messages.map((m) => ({
1744
+ id: m.id,
1745
+ role: m.role,
1746
+ content: m.content,
1747
+ created_at: m.createdAt,
1748
+ tool_calls: m.toolCalls,
1749
+ tool_call_id: m.toolCallId,
1750
+ metadata: {
1751
+ attachments: m.attachments,
1752
+ thinking: m.thinking
1753
+ }
1754
+ }));
1755
+ onMessagesChange(coreMessages);
1756
+ }
1757
+ }, [messages, onMessagesChange]);
1758
+ react.useEffect(() => {
1759
+ if (error && onError) {
1760
+ onError(error);
1761
+ }
1762
+ }, [error, onError]);
1763
+ react.useEffect(() => {
1764
+ return () => {
1765
+ chatRef.current?.dispose();
1766
+ };
1767
+ }, []);
1768
+ const contextValue = react.useMemo(
1769
+ () => ({
1770
+ // Chat state
1771
+ messages,
1772
+ status,
1773
+ error,
1774
+ isLoading,
1775
+ // Chat actions
1776
+ sendMessage,
1777
+ stop,
1778
+ clearMessages,
1779
+ regenerate,
1780
+ // Tool execution
1781
+ registerTool,
1782
+ unregisterTool,
1783
+ registeredTools,
1784
+ toolExecutions,
1785
+ pendingApprovals,
1786
+ approveToolExecution,
1787
+ rejectToolExecution,
1788
+ // Actions
1789
+ registerAction,
1790
+ unregisterAction,
1791
+ registeredActions,
1792
+ // AI Context
1793
+ addContext,
1794
+ removeContext,
1795
+ // Config
1796
+ threadId,
1797
+ runtimeUrl,
1798
+ toolsConfig
1799
+ }),
1800
+ [
1801
+ messages,
1802
+ status,
1803
+ error,
1804
+ isLoading,
1805
+ sendMessage,
1806
+ stop,
1807
+ clearMessages,
1808
+ regenerate,
1809
+ registerTool,
1810
+ unregisterTool,
1811
+ registeredTools,
1812
+ toolExecutions,
1813
+ pendingApprovals,
1814
+ approveToolExecution,
1815
+ rejectToolExecution,
1816
+ registerAction,
1817
+ unregisterAction,
1818
+ registeredActions,
1819
+ addContext,
1820
+ removeContext,
1821
+ threadId,
1822
+ runtimeUrl,
1823
+ toolsConfig
1824
+ ]
1825
+ );
1826
+ return /* @__PURE__ */ jsxRuntime.jsx(CopilotContext.Provider, { value: contextValue, children });
1827
+ }
1828
+ function useAIActions(actions) {
1829
+ const { registerAction, unregisterAction } = useCopilot();
1830
+ react.useEffect(() => {
1831
+ for (const action of actions) {
1832
+ registerAction(action);
1833
+ }
1834
+ return () => {
1835
+ for (const action of actions) {
1836
+ unregisterAction(action.name);
1837
+ }
1838
+ };
1839
+ }, [actions, registerAction, unregisterAction]);
1840
+ }
1841
+ function useAIAction(action) {
1842
+ useAIActions([action]);
1843
+ }
1844
+ function useAIContext(item) {
1845
+ const { addContext, removeContext } = useCopilot();
1846
+ const contextIdRef = react.useRef(null);
1847
+ const serializedData = typeof item.data === "string" ? item.data : JSON.stringify(item.data);
1848
+ react.useEffect(() => {
1849
+ const formattedValue = typeof item.data === "string" ? item.data : JSON.stringify(item.data, null, 2);
1850
+ const contextString = item.description ? `${item.description}:
1851
+ ${formattedValue}` : `${item.key}:
1852
+ ${formattedValue}`;
1853
+ contextIdRef.current = addContext(contextString, item.parentId);
1854
+ return () => {
1855
+ if (contextIdRef.current) {
1856
+ removeContext(contextIdRef.current);
1857
+ contextIdRef.current = null;
1858
+ }
1859
+ };
1860
+ }, [
1861
+ item.key,
1862
+ serializedData,
1863
+ item.description,
1864
+ item.parentId,
1865
+ addContext,
1866
+ removeContext
1867
+ ]);
1868
+ return contextIdRef.current ?? void 0;
1869
+ }
1870
+ function useAIContexts(items) {
1871
+ const { addContext, removeContext } = useCopilot();
1872
+ const contextIdsRef = react.useRef([]);
1873
+ const serializedItems = JSON.stringify(
1874
+ items.map((item) => ({
1875
+ key: item.key,
1876
+ data: item.data,
1877
+ description: item.description,
1878
+ parentId: item.parentId
1879
+ }))
1880
+ );
1881
+ react.useEffect(() => {
1882
+ contextIdsRef.current.forEach((id) => removeContext(id));
1883
+ contextIdsRef.current = [];
1884
+ const parsedItems = JSON.parse(serializedItems);
1885
+ for (const item of parsedItems) {
1886
+ const formattedValue = typeof item.data === "string" ? item.data : JSON.stringify(item.data, null, 2);
1887
+ const contextString = item.description ? `${item.description}:
1888
+ ${formattedValue}` : `${item.key}:
1889
+ ${formattedValue}`;
1890
+ const id = addContext(contextString, item.parentId);
1891
+ contextIdsRef.current.push(id);
1892
+ }
1893
+ return () => {
1894
+ contextIdsRef.current.forEach((id) => removeContext(id));
1895
+ contextIdsRef.current = [];
1896
+ };
1897
+ }, [serializedItems, addContext, removeContext]);
1898
+ }
1899
+ function useAITools(options = {}) {
1900
+ const {
1901
+ screenshot = false,
1902
+ console: consoleCapture = false,
1903
+ network = false,
1904
+ requireConsent = true,
1905
+ screenshotOptions,
1906
+ consoleOptions,
1907
+ networkOptions,
1908
+ onConsentRequest,
1909
+ autoStart = true
1910
+ } = options;
1911
+ const [isEnabled] = react.useState(screenshot || consoleCapture || network);
1912
+ const [activeCaptures, setActiveCaptures] = react.useState({
1913
+ console: false,
1914
+ network: false
1915
+ });
1916
+ const [pendingConsent, setPendingConsent] = react.useState(null);
1917
+ const consentResolverRef = react.useRef(null);
1918
+ const rememberedConsentRef = react.useRef(/* @__PURE__ */ new Set());
1919
+ react.useEffect(() => {
1920
+ if (!autoStart || !isEnabled) return;
1921
+ if (consoleCapture && !chunk2ZCWVAAK_cjs.isConsoleCaptureActive()) {
1922
+ chunk2ZCWVAAK_cjs.startConsoleCapture(consoleOptions);
1923
+ setActiveCaptures((prev) => ({ ...prev, console: true }));
1924
+ }
1925
+ if (network && !chunk2ZCWVAAK_cjs.isNetworkCaptureActive()) {
1926
+ chunk2ZCWVAAK_cjs.startNetworkCapture(networkOptions);
1927
+ setActiveCaptures((prev) => ({ ...prev, network: true }));
1928
+ }
1929
+ return () => {
1930
+ chunk2ZCWVAAK_cjs.stopConsoleCapture();
1931
+ chunk2ZCWVAAK_cjs.stopNetworkCapture();
1932
+ };
1933
+ }, [
1934
+ autoStart,
1935
+ isEnabled,
1936
+ consoleCapture,
1937
+ network,
1938
+ consoleOptions,
1939
+ networkOptions
1940
+ ]);
1941
+ const captureScreenshotFn = react.useCallback(
1942
+ async (opts) => {
1943
+ if (!screenshot) {
1944
+ throw new Error("Screenshot capture is not enabled");
1945
+ }
1946
+ if (!chunk2ZCWVAAK_cjs.isScreenshotSupported()) {
1947
+ throw new Error(
1948
+ "Screenshot capture is not supported in this environment"
1949
+ );
1950
+ }
1951
+ return chunk2ZCWVAAK_cjs.captureScreenshot({ ...screenshotOptions, ...opts });
1952
+ },
1953
+ [screenshot, screenshotOptions]
1954
+ );
1955
+ const getConsoleLogsFn = react.useCallback(
1956
+ (opts) => {
1957
+ if (!consoleCapture) {
1958
+ return { logs: [], totalCaptured: 0 };
1959
+ }
1960
+ return chunk2ZCWVAAK_cjs.getConsoleLogs({ ...consoleOptions, ...opts });
1961
+ },
1962
+ [consoleCapture, consoleOptions]
1963
+ );
1964
+ const getNetworkRequestsFn = react.useCallback(
1965
+ (opts) => {
1966
+ if (!network) {
1967
+ return { requests: [], totalCaptured: 0 };
1968
+ }
1969
+ return chunk2ZCWVAAK_cjs.getNetworkRequests({ ...networkOptions, ...opts });
1970
+ },
1971
+ [network, networkOptions]
1972
+ );
1973
+ const requestConsent = react.useCallback(
1974
+ async (tools, reason = "") => {
1975
+ const enabledTools = tools.filter((tool) => {
1976
+ if (tool === "screenshot") return screenshot;
1977
+ if (tool === "console") return consoleCapture;
1978
+ if (tool === "network") return network;
1979
+ return false;
1980
+ });
1981
+ if (enabledTools.length === 0) {
1982
+ return { approved: [], denied: [] };
1983
+ }
1984
+ if (!requireConsent) {
1985
+ return { approved: enabledTools, denied: [] };
1986
+ }
1987
+ const needsConsent = enabledTools.filter(
1988
+ (tool) => !rememberedConsentRef.current.has(tool)
1989
+ );
1990
+ if (needsConsent.length === 0) {
1991
+ return { approved: enabledTools, denied: [] };
1992
+ }
1993
+ const request = {
1994
+ tools: needsConsent,
1995
+ reason,
1996
+ keywords: []
1997
+ };
1998
+ if (onConsentRequest) {
1999
+ const response = await onConsentRequest(request);
2000
+ if (response.remember) {
2001
+ response.approved.forEach(
2002
+ (tool) => rememberedConsentRef.current.add(tool)
2003
+ );
2004
+ }
2005
+ return response;
2006
+ }
2007
+ return new Promise((resolve) => {
2008
+ setPendingConsent(request);
2009
+ consentResolverRef.current = (response) => {
2010
+ if (response.remember) {
2011
+ response.approved.forEach(
2012
+ (tool) => rememberedConsentRef.current.add(tool)
2013
+ );
2014
+ }
2015
+ resolve(response);
2016
+ };
2017
+ });
2018
+ },
2019
+ [screenshot, consoleCapture, network, requireConsent, onConsentRequest]
2020
+ );
2021
+ const respondToConsent = react.useCallback((response) => {
2022
+ if (consentResolverRef.current) {
2023
+ consentResolverRef.current(response);
2024
+ consentResolverRef.current = null;
2025
+ }
2026
+ setPendingConsent(null);
2027
+ }, []);
2028
+ const captureContext = react.useCallback(
2029
+ async (tools) => {
2030
+ const toolsToCapture = tools || ["screenshot", "console", "network"];
2031
+ const context = {
2032
+ timestamp: Date.now()
2033
+ };
2034
+ const captures = [];
2035
+ if (toolsToCapture.includes("screenshot") && screenshot) {
2036
+ captures.push(
2037
+ captureScreenshotFn().then((result) => {
2038
+ context.screenshot = result;
2039
+ }).catch(() => {
2040
+ })
2041
+ );
2042
+ }
2043
+ if (toolsToCapture.includes("console") && consoleCapture) {
2044
+ context.consoleLogs = getConsoleLogsFn();
2045
+ }
2046
+ if (toolsToCapture.includes("network") && network) {
2047
+ context.networkRequests = getNetworkRequestsFn();
2048
+ }
2049
+ await Promise.all(captures);
2050
+ return context;
2051
+ },
2052
+ [
2053
+ screenshot,
2054
+ consoleCapture,
2055
+ network,
2056
+ captureScreenshotFn,
2057
+ getConsoleLogsFn,
2058
+ getNetworkRequestsFn
2059
+ ]
2060
+ );
2061
+ const startCapturing = react.useCallback(() => {
2062
+ if (consoleCapture && !chunk2ZCWVAAK_cjs.isConsoleCaptureActive()) {
2063
+ chunk2ZCWVAAK_cjs.startConsoleCapture(consoleOptions);
2064
+ setActiveCaptures((prev) => ({ ...prev, console: true }));
2065
+ }
2066
+ if (network && !chunk2ZCWVAAK_cjs.isNetworkCaptureActive()) {
2067
+ chunk2ZCWVAAK_cjs.startNetworkCapture(networkOptions);
2068
+ setActiveCaptures((prev) => ({ ...prev, network: true }));
2069
+ }
2070
+ }, [consoleCapture, network, consoleOptions, networkOptions]);
2071
+ const stopCapturing = react.useCallback(() => {
2072
+ chunk2ZCWVAAK_cjs.stopConsoleCapture();
2073
+ chunk2ZCWVAAK_cjs.stopNetworkCapture();
2074
+ setActiveCaptures({ console: false, network: false });
2075
+ }, []);
2076
+ const clearCaptured = react.useCallback(() => {
2077
+ chunk2ZCWVAAK_cjs.clearConsoleLogs();
2078
+ chunk2ZCWVAAK_cjs.clearNetworkRequests();
2079
+ }, []);
2080
+ const formatForAI = react.useCallback((context) => {
2081
+ const parts = [];
2082
+ if (context.screenshot) {
2083
+ parts.push(
2084
+ `Screenshot captured (${context.screenshot.width}x${context.screenshot.height}, ${context.screenshot.format})`
2085
+ );
2086
+ }
2087
+ if (context.consoleLogs && context.consoleLogs.logs.length > 0) {
2088
+ parts.push(chunk2ZCWVAAK_cjs.formatLogsForAI(context.consoleLogs.logs));
2089
+ }
2090
+ if (context.networkRequests && context.networkRequests.requests.length > 0) {
2091
+ parts.push(chunk2ZCWVAAK_cjs.formatRequestsForAI(context.networkRequests.requests));
2092
+ }
2093
+ return parts.length > 0 ? parts.join("\n\n---\n\n") : "No context captured.";
2094
+ }, []);
2095
+ const detectIntentFn = react.useCallback(
2096
+ (message) => {
2097
+ const result = chunk2ZCWVAAK_cjs.detectIntent(message);
2098
+ result.suggestedTools = result.suggestedTools.filter((tool) => {
2099
+ if (tool === "screenshot") return screenshot;
2100
+ if (tool === "console") return consoleCapture;
2101
+ if (tool === "network") return network;
2102
+ return false;
2103
+ });
2104
+ return result;
2105
+ },
2106
+ [screenshot, consoleCapture, network]
2107
+ );
2108
+ return react.useMemo(
2109
+ () => ({
2110
+ isEnabled,
2111
+ activeCaptures,
2112
+ captureScreenshot: captureScreenshotFn,
2113
+ getConsoleLogs: getConsoleLogsFn,
2114
+ getNetworkRequests: getNetworkRequestsFn,
2115
+ captureContext,
2116
+ detectIntent: detectIntentFn,
2117
+ requestConsent,
2118
+ startCapturing,
2119
+ stopCapturing,
2120
+ clearCaptured,
2121
+ formatForAI,
2122
+ pendingConsent,
2123
+ respondToConsent
2124
+ }),
2125
+ [
2126
+ isEnabled,
2127
+ activeCaptures,
2128
+ captureScreenshotFn,
2129
+ getConsoleLogsFn,
2130
+ getNetworkRequestsFn,
2131
+ captureContext,
2132
+ detectIntentFn,
2133
+ requestConsent,
2134
+ startCapturing,
2135
+ stopCapturing,
2136
+ clearCaptured,
2137
+ formatForAI,
2138
+ pendingConsent,
2139
+ respondToConsent
2140
+ ]
2141
+ );
2142
+ }
2143
+ function useTool(config, dependencies = []) {
2144
+ const { registerTool, unregisterTool } = useCopilot();
2145
+ const configRef = react.useRef(config);
2146
+ configRef.current = config;
2147
+ react.useEffect(() => {
2148
+ const tool = {
2149
+ name: config.name,
2150
+ description: config.description,
2151
+ location: "client",
2152
+ inputSchema: config.inputSchema,
2153
+ handler: async (params, context) => {
2154
+ return configRef.current.handler(params, context);
2155
+ },
2156
+ render: config.render,
2157
+ available: config.available ?? true,
2158
+ needsApproval: config.needsApproval,
2159
+ approvalMessage: config.approvalMessage
2160
+ };
2161
+ registerTool(tool);
2162
+ return () => {
2163
+ unregisterTool(config.name);
2164
+ };
2165
+ }, [config.name, ...dependencies]);
2166
+ }
2167
+ function useTools(tools) {
2168
+ const { registerTool, unregisterTool } = useCopilot();
2169
+ const registeredToolsRef = react.useRef([]);
2170
+ const toolsRef = react.useRef(tools);
2171
+ toolsRef.current = tools;
2172
+ const toolsKey = Object.keys(tools).sort().join(",");
2173
+ react.useEffect(() => {
2174
+ const currentTools = toolsRef.current;
2175
+ const toolNames = [];
2176
+ for (const [name, toolDef] of Object.entries(currentTools)) {
2177
+ const fullTool = {
2178
+ ...toolDef,
2179
+ name
2180
+ // Use the key as the name
2181
+ };
2182
+ registerTool(fullTool);
2183
+ toolNames.push(name);
2184
+ }
2185
+ registeredToolsRef.current = toolNames;
2186
+ return () => {
2187
+ for (const name of registeredToolsRef.current) {
2188
+ unregisterTool(name);
2189
+ }
2190
+ registeredToolsRef.current = [];
2191
+ };
2192
+ }, [toolsKey]);
2193
+ }
2194
+ var CopilotContext2 = react.createContext(null);
2195
+ function useCopilotContext() {
2196
+ const context = react.useContext(CopilotContext2);
2197
+ if (!context) {
2198
+ throw new Error("useCopilotContext must be used within a CopilotProvider");
2199
+ }
2200
+ return context;
2201
+ }
2202
+
2203
+ // src/react/hooks/useToolWithSchema.ts
2204
+ function convertZodSchema(schema, _toolName) {
2205
+ try {
2206
+ const zodWithJsonSchema = z__namespace;
2207
+ if (typeof zodWithJsonSchema.toJSONSchema === "function") {
2208
+ const jsonSchema = zodWithJsonSchema.toJSONSchema(
2209
+ schema
2210
+ );
2211
+ if (jsonSchema.type === "object") {
2212
+ return {
2213
+ type: "object",
2214
+ properties: jsonSchema.properties || {},
2215
+ required: jsonSchema.required
2216
+ };
2217
+ }
2218
+ }
2219
+ } catch {
2220
+ }
2221
+ return chunk2ZCWVAAK_cjs.zodObjectToInputSchema(schema);
2222
+ }
2223
+ function useToolWithSchema(config, dependencies = []) {
2224
+ const { registerTool, unregisterTool } = useCopilotContext();
2225
+ const configRef = react.useRef(config);
2226
+ configRef.current = config;
2227
+ const inputSchema = react.useMemo(() => {
2228
+ try {
2229
+ return convertZodSchema(config.schema, config.name);
2230
+ } catch (error) {
2231
+ console.warn(
2232
+ `[useToolWithSchema] Failed to convert Zod schema for tool "${config.name}"`,
2233
+ error
2234
+ );
2235
+ return {
2236
+ type: "object",
2237
+ properties: {}
2238
+ };
2239
+ }
2240
+ }, [config.schema, config.name]);
2241
+ react.useEffect(() => {
2242
+ const tool = {
2243
+ name: config.name,
2244
+ description: config.description,
2245
+ location: "client",
2246
+ inputSchema,
2247
+ handler: async (params, context) => {
2248
+ return configRef.current.handler(params, context);
2249
+ },
2250
+ render: config.render,
2251
+ available: config.available ?? true
2252
+ };
2253
+ registerTool(tool);
2254
+ return () => {
2255
+ unregisterTool(config.name);
2256
+ };
2257
+ }, [config.name, inputSchema, ...dependencies]);
2258
+ }
2259
+ function useToolsWithSchema(tools, dependencies = []) {
2260
+ const { registerTool, unregisterTool } = useCopilotContext();
2261
+ const toolsRef = react.useRef(tools);
2262
+ toolsRef.current = tools;
2263
+ react.useEffect(() => {
2264
+ const toolNames = [];
2265
+ for (const config of tools) {
2266
+ let inputSchema;
2267
+ try {
2268
+ inputSchema = convertZodSchema(config.schema, config.name);
2269
+ } catch (error) {
2270
+ console.warn(
2271
+ `[useToolsWithSchema] Failed to convert Zod schema for tool "${config.name}"`,
2272
+ error
2273
+ );
2274
+ inputSchema = { type: "object", properties: {} };
2275
+ }
2276
+ const tool = {
2277
+ name: config.name,
2278
+ description: config.description,
2279
+ location: "client",
2280
+ inputSchema,
2281
+ handler: async (params, context) => {
2282
+ const currentConfig = toolsRef.current.find(
2283
+ (t) => t.name === config.name
2284
+ );
2285
+ if (currentConfig) {
2286
+ return currentConfig.handler(params, context);
2287
+ }
2288
+ return { success: false, error: "Tool handler not found" };
2289
+ },
2290
+ available: config.available ?? true
2291
+ };
2292
+ registerTool(tool);
2293
+ toolNames.push(config.name);
2294
+ }
2295
+ return () => {
2296
+ for (const name of toolNames) {
2297
+ unregisterTool(name);
2298
+ }
2299
+ };
2300
+ }, [tools.map((t) => t.name).join(","), ...dependencies]);
2301
+ }
2302
+ function useToolExecutor() {
2303
+ const {
2304
+ registeredTools,
2305
+ config,
2306
+ chat,
2307
+ addToolExecution,
2308
+ updateToolExecution
2309
+ } = useCopilotContext();
2310
+ const toolsRef = react.useRef(registeredTools);
2311
+ toolsRef.current = registeredTools;
2312
+ const executeTool = react.useCallback(
2313
+ async (toolCall) => {
2314
+ const tool = toolsRef.current.find((t) => t.name === toolCall.name);
2315
+ if (!tool) {
2316
+ return {
2317
+ success: false,
2318
+ error: `Unknown tool: ${toolCall.name}`
2319
+ };
2320
+ }
2321
+ if (!tool.handler) {
2322
+ return {
2323
+ success: false,
2324
+ error: `Tool "${toolCall.name}" has no handler`
2325
+ };
2326
+ }
2327
+ const execution = {
2328
+ id: toolCall.id,
2329
+ name: toolCall.name,
2330
+ args: toolCall.input,
2331
+ status: "executing",
2332
+ timestamp: Date.now(),
2333
+ approvalStatus: "none"
2334
+ };
2335
+ addToolExecution?.(execution);
2336
+ try {
2337
+ const startTime = Date.now();
2338
+ const result = await tool.handler(toolCall.input);
2339
+ const duration = Date.now() - startTime;
2340
+ updateToolExecution?.(toolCall.id, {
2341
+ status: result.success ? "completed" : "error",
2342
+ result,
2343
+ error: result.error,
2344
+ duration
2345
+ });
2346
+ return result;
2347
+ } catch (error) {
2348
+ const errorMessage = error instanceof Error ? error.message : "Tool execution failed";
2349
+ updateToolExecution?.(toolCall.id, {
2350
+ status: "error",
2351
+ error: errorMessage
2352
+ });
2353
+ return {
2354
+ success: false,
2355
+ error: errorMessage
2356
+ };
2357
+ }
2358
+ },
2359
+ [addToolExecution, updateToolExecution]
2360
+ );
2361
+ const sendToolResult = react.useCallback(
2362
+ async (toolCallId, result) => {
2363
+ const runtimeUrl = config.runtimeUrl || config.cloud?.endpoint;
2364
+ if (!runtimeUrl) {
2365
+ console.warn(
2366
+ "[useToolExecutor] No runtime URL configured, cannot send tool result"
2367
+ );
2368
+ return;
2369
+ }
2370
+ const baseUrl = runtimeUrl.replace(/\/chat\/?$/, "");
2371
+ try {
2372
+ const response = await fetch(`${baseUrl}/tool-result`, {
2373
+ method: "POST",
2374
+ headers: {
2375
+ "Content-Type": "application/json"
2376
+ },
2377
+ body: JSON.stringify({
2378
+ threadId: chat.threadId || "default",
2379
+ toolCallId,
2380
+ result
2381
+ })
2382
+ });
2383
+ if (!response.ok) {
2384
+ console.error(
2385
+ "[useToolExecutor] Failed to send tool result:",
2386
+ await response.text()
2387
+ );
2388
+ }
2389
+ } catch (error) {
2390
+ console.error("[useToolExecutor] Error sending tool result:", error);
2391
+ }
2392
+ },
2393
+ [config.runtimeUrl, config.cloud?.endpoint, chat.threadId]
2394
+ );
2395
+ const getTool = react.useCallback((name) => {
2396
+ return toolsRef.current.find((t) => t.name === name);
2397
+ }, []);
2398
+ const hasTool = react.useCallback((name) => {
2399
+ return toolsRef.current.some((t) => t.name === name);
2400
+ }, []);
2401
+ return {
2402
+ executeTool,
2403
+ sendToolResult,
2404
+ getTool,
2405
+ hasTool
2406
+ };
2407
+ }
2408
+ function useSuggestions(options = {}) {
2409
+ const {
2410
+ count = 3,
2411
+ context,
2412
+ suggestions: staticSuggestions,
2413
+ autoRefresh = true
2414
+ } = options;
2415
+ const { chat, actions, config } = useCopilotContext();
2416
+ const [suggestions, setSuggestions] = react.useState([]);
2417
+ const [isLoading, setIsLoading] = react.useState(false);
2418
+ const normalizedStatic = react.useMemo(
2419
+ () => staticSuggestions?.map((s) => typeof s === "string" ? { text: s } : s),
2420
+ [staticSuggestions]
2421
+ );
2422
+ const refresh = react.useCallback(async () => {
2423
+ if (normalizedStatic) {
2424
+ setSuggestions(normalizedStatic.slice(0, count));
2425
+ return;
2426
+ }
2427
+ if (!config.cloud) {
2428
+ return;
2429
+ }
2430
+ setIsLoading(true);
2431
+ try {
2432
+ const endpoint = config.cloud.endpoint || "https://api.yourgpt.ai/v1";
2433
+ const response = await fetch(`${endpoint}/suggestions`, {
2434
+ method: "POST",
2435
+ headers: {
2436
+ "Content-Type": "application/json",
2437
+ Authorization: `Bearer ${config.cloud.apiKey}`
2438
+ },
2439
+ body: JSON.stringify({
2440
+ botId: config.cloud.botId,
2441
+ count,
2442
+ context,
2443
+ messages: chat.messages.slice(-5)
2444
+ // Last 5 messages for context
2445
+ })
2446
+ });
2447
+ if (response.ok) {
2448
+ const data = await response.json();
2449
+ setSuggestions(
2450
+ data.suggestions.map(
2451
+ (s) => typeof s === "string" ? { text: s } : s
2452
+ )
2453
+ );
2454
+ }
2455
+ } catch (error) {
2456
+ console.error("Failed to fetch suggestions:", error);
2457
+ } finally {
2458
+ setIsLoading(false);
2459
+ }
2460
+ }, [config.cloud, count, context, chat.messages, normalizedStatic]);
2461
+ const select = react.useCallback(
2462
+ (suggestion) => {
2463
+ const text = typeof suggestion === "string" ? suggestion : suggestion.text;
2464
+ actions.sendMessage(text);
2465
+ },
2466
+ [actions]
2467
+ );
2468
+ react.useEffect(() => {
2469
+ if (autoRefresh && chat.messages.length === 0) {
2470
+ refresh();
2471
+ }
2472
+ }, [autoRefresh, chat.messages.length, refresh]);
2473
+ return {
2474
+ suggestions: normalizedStatic?.slice(0, count) || suggestions,
2475
+ isLoading,
2476
+ refresh,
2477
+ select
2478
+ };
2479
+ }
2480
+ function useAgent(options) {
2481
+ const { name, initialState = {}, onStateChange } = options;
2482
+ const { config } = useCopilotContext();
2483
+ const [state, setStateInternal] = react.useState(initialState);
2484
+ const [isRunning, setIsRunning] = react.useState(false);
2485
+ const [nodeName, setNodeName] = react.useState(null);
2486
+ const [error, setError] = react.useState(null);
2487
+ const abortControllerRef = react.useRef(null);
2488
+ const getEndpoint = react.useCallback(() => {
2489
+ if (config.cloud) {
2490
+ return `${config.cloud.endpoint || "https://api.yourgpt.ai/v1"}/agents/${name}`;
2491
+ }
2492
+ return `${config.runtimeUrl || "/api"}/agents/${name}`;
2493
+ }, [config, name]);
2494
+ const start = react.useCallback(
2495
+ async (input) => {
2496
+ setIsRunning(true);
2497
+ setError(null);
2498
+ abortControllerRef.current = new AbortController();
2499
+ try {
2500
+ const endpoint = getEndpoint();
2501
+ const headers = {
2502
+ "Content-Type": "application/json"
2503
+ };
2504
+ if (config.cloud?.apiKey) {
2505
+ headers["Authorization"] = `Bearer ${config.cloud.apiKey}`;
2506
+ }
2507
+ const response = await fetch(`${endpoint}/start`, {
2508
+ method: "POST",
2509
+ headers,
2510
+ body: JSON.stringify({
2511
+ input: typeof input === "string" ? { message: input } : input,
2512
+ state
2513
+ }),
2514
+ signal: abortControllerRef.current.signal
2515
+ });
2516
+ if (!response.ok) {
2517
+ throw new Error(`Agent error: ${response.status}`);
2518
+ }
2519
+ for await (const event of chunk2ZCWVAAK_cjs.streamSSE(response)) {
2520
+ handleAgentEvent(event);
2521
+ }
2522
+ } catch (err) {
2523
+ if (err.name !== "AbortError") {
2524
+ setError(err instanceof Error ? err : new Error("Unknown error"));
2525
+ }
2526
+ } finally {
2527
+ setIsRunning(false);
2528
+ abortControllerRef.current = null;
2529
+ }
2530
+ },
2531
+ [config, getEndpoint, state]
2532
+ );
2533
+ const handleAgentEvent = react.useCallback(
2534
+ (event) => {
2535
+ if (event.type === "error") {
2536
+ setError(new Error(event.message));
2537
+ return;
2538
+ }
2539
+ if ("state" in event && event.state) {
2540
+ setStateInternal(event.state);
2541
+ onStateChange?.(event.state);
2542
+ }
2543
+ if ("nodeName" in event && event.nodeName) {
2544
+ setNodeName(event.nodeName);
2545
+ }
2546
+ },
2547
+ [onStateChange]
2548
+ );
2549
+ const stop = react.useCallback(() => {
2550
+ abortControllerRef.current?.abort();
2551
+ }, []);
2552
+ const setState = react.useCallback(
2553
+ (partialState) => {
2554
+ setStateInternal((prev) => {
2555
+ const newState = { ...prev, ...partialState };
2556
+ onStateChange?.(newState);
2557
+ return newState;
2558
+ });
2559
+ },
2560
+ [onStateChange]
2561
+ );
2562
+ react.useEffect(() => {
2563
+ return () => {
2564
+ abortControllerRef.current?.abort();
2565
+ };
2566
+ }, []);
2567
+ return {
2568
+ state,
2569
+ isRunning,
2570
+ nodeName,
2571
+ start,
2572
+ stop,
2573
+ setState,
2574
+ error
2575
+ };
2576
+ }
2577
+
2578
+ // src/react/utils/knowledge-base.ts
2579
+ var KNOWLEDGE_BASE_API = "https://api.yourgpt.ai/chatbot/v1/searchIndexDocument";
2580
+ async function searchKnowledgeBase(query, config) {
2581
+ try {
2582
+ const response = await fetch(KNOWLEDGE_BASE_API, {
2583
+ method: "POST",
2584
+ headers: {
2585
+ accept: "*/*",
2586
+ "content-type": "application/json",
2587
+ authorization: `Bearer ${config.token}`
2588
+ },
2589
+ body: JSON.stringify({
2590
+ project_uid: config.projectUid,
2591
+ query,
2592
+ page: 1,
2593
+ limit: String(config.limit || 10),
2594
+ app_id: config.appId || "1"
2595
+ })
2596
+ });
2597
+ if (!response.ok) {
2598
+ return {
2599
+ success: false,
2600
+ results: [],
2601
+ error: `API error: ${response.status} ${response.statusText}`
2602
+ };
2603
+ }
2604
+ const data = await response.json();
2605
+ const results = (data.data || data.results || []).map((item) => ({
2606
+ id: item.id || item._id || String(Math.random()),
2607
+ title: item.title || item.name || void 0,
2608
+ content: item.content || item.text || item.snippet || "",
2609
+ score: item.score || item.relevance || void 0,
2610
+ url: item.url || item.source_url || void 0,
2611
+ metadata: item.metadata || {}
2612
+ }));
2613
+ return {
2614
+ success: true,
2615
+ results,
2616
+ total: data.total || results.length,
2617
+ page: data.page || 1
2618
+ };
2619
+ } catch (error) {
2620
+ return {
2621
+ success: false,
2622
+ results: [],
2623
+ error: error instanceof Error ? error.message : "Unknown error"
2624
+ };
2625
+ }
2626
+ }
2627
+ function formatKnowledgeResultsForAI(results) {
2628
+ if (results.length === 0) {
2629
+ return "No relevant documents found in the knowledge base.";
2630
+ }
2631
+ return results.map((result, index) => {
2632
+ const parts = [`[${index + 1}]`];
2633
+ if (result.title) parts.push(`**${result.title}**`);
2634
+ parts.push(result.content);
2635
+ if (result.url) parts.push(`Source: ${result.url}`);
2636
+ return parts.join("\n");
2637
+ }).join("\n\n---\n\n");
2638
+ }
2639
+
2640
+ // src/react/hooks/useKnowledgeBase.ts
2641
+ function useKnowledgeBase(config) {
2642
+ const { registerTool, unregisterTool } = useCopilotContext();
2643
+ const configRef = react.useRef(config);
2644
+ configRef.current = config;
2645
+ const handleSearch = react.useCallback(
2646
+ async (params) => {
2647
+ const query = params.query;
2648
+ if (!query) {
2649
+ return {
2650
+ success: false,
2651
+ error: "Query is required"
2652
+ };
2653
+ }
2654
+ const currentConfig = configRef.current;
2655
+ const kbConfig = {
2656
+ projectUid: currentConfig.projectUid,
2657
+ token: currentConfig.token,
2658
+ appId: currentConfig.appId,
2659
+ limit: currentConfig.limit || 5
2660
+ };
2661
+ const response = await searchKnowledgeBase(
2662
+ query,
2663
+ kbConfig
2664
+ );
2665
+ if (!response.success) {
2666
+ return {
2667
+ success: false,
2668
+ error: response.error || "Knowledge base search failed"
2669
+ };
2670
+ }
2671
+ const formattedResults = formatKnowledgeResultsForAI(response.results);
2672
+ return {
2673
+ success: true,
2674
+ message: formattedResults,
2675
+ data: {
2676
+ resultCount: response.results.length,
2677
+ total: response.total
2678
+ }
2679
+ };
2680
+ },
2681
+ []
2682
+ );
2683
+ react.useEffect(() => {
2684
+ if (config.enabled === false) {
2685
+ return;
2686
+ }
2687
+ registerTool({
2688
+ name: "search_knowledge",
2689
+ 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.",
2690
+ location: "client",
2691
+ inputSchema: {
2692
+ type: "object",
2693
+ properties: {
2694
+ query: {
2695
+ type: "string",
2696
+ description: "The search query to find relevant information in the knowledge base"
2697
+ }
2698
+ },
2699
+ required: ["query"]
2700
+ },
2701
+ handler: handleSearch
2702
+ });
2703
+ return () => {
2704
+ unregisterTool("search_knowledge");
2705
+ };
2706
+ }, [
2707
+ config.enabled,
2708
+ config.projectUid,
2709
+ config.token,
2710
+ registerTool,
2711
+ unregisterTool,
2712
+ handleSearch
2713
+ ]);
2714
+ }
2715
+ var DEFAULT_CAPABILITIES = {
2716
+ supportsVision: false,
2717
+ supportsTools: true,
2718
+ supportsThinking: false,
2719
+ supportsStreaming: true,
2720
+ supportsPDF: false,
2721
+ supportsAudio: false,
2722
+ supportsVideo: false,
2723
+ maxTokens: 8192,
2724
+ supportedImageTypes: [],
2725
+ supportsJsonMode: false,
2726
+ supportsSystemMessages: true
2727
+ };
2728
+ function useCapabilities() {
2729
+ const { config } = useCopilotContext();
2730
+ const [capabilities, setCapabilities] = react.useState(DEFAULT_CAPABILITIES);
2731
+ const [provider, setProvider] = react.useState("unknown");
2732
+ const [model, setModel] = react.useState("unknown");
2733
+ const [supportedModels, setSupportedModels] = react.useState([]);
2734
+ const [isLoading, setIsLoading] = react.useState(true);
2735
+ const [error, setError] = react.useState(null);
2736
+ const capabilitiesUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/chat\/?$/, "/capabilities") : null;
2737
+ const fetchCapabilities = react.useCallback(async () => {
2738
+ if (!capabilitiesUrl) {
2739
+ setIsLoading(false);
2740
+ return;
2741
+ }
2742
+ try {
2743
+ setIsLoading(true);
2744
+ setError(null);
2745
+ const response = await fetch(capabilitiesUrl);
2746
+ if (!response.ok) {
2747
+ throw new Error(`Failed to fetch capabilities: ${response.status}`);
2748
+ }
2749
+ const data = await response.json();
2750
+ setCapabilities(data.capabilities);
2751
+ setProvider(data.provider);
2752
+ setModel(data.model);
2753
+ setSupportedModels(data.supportedModels);
2754
+ } catch (err) {
2755
+ setError(err instanceof Error ? err : new Error("Unknown error"));
2756
+ } finally {
2757
+ setIsLoading(false);
2758
+ }
2759
+ }, [capabilitiesUrl]);
2760
+ react.useEffect(() => {
2761
+ fetchCapabilities();
2762
+ }, [fetchCapabilities]);
2763
+ return {
2764
+ /** Current model capabilities */
2765
+ capabilities,
2766
+ /** Current provider name */
2767
+ provider,
2768
+ /** Current model ID */
2769
+ model,
2770
+ /** List of supported models for current provider */
2771
+ supportedModels,
2772
+ /** Whether capabilities are being loaded */
2773
+ isLoading,
2774
+ /** Error if fetch failed */
2775
+ error,
2776
+ /** Refetch capabilities */
2777
+ refetch: fetchCapabilities
2778
+ };
2779
+ }
2780
+ function useFeatureSupport(feature) {
2781
+ const { capabilities } = useCapabilities();
2782
+ return capabilities[feature] ?? false;
2783
+ }
2784
+ function useSupportedMediaTypes() {
2785
+ const { capabilities } = useCapabilities();
2786
+ return {
2787
+ /** Supported image MIME types */
2788
+ imageTypes: capabilities.supportedImageTypes || [],
2789
+ /** Supported audio MIME types */
2790
+ audioTypes: capabilities.supportedAudioTypes || [],
2791
+ /** Supported video MIME types */
2792
+ videoTypes: capabilities.supportedVideoTypes || [],
2793
+ /** Whether any image types are supported */
2794
+ hasImageSupport: (capabilities.supportedImageTypes?.length ?? 0) > 0,
2795
+ /** Whether any audio types are supported */
2796
+ hasAudioSupport: (capabilities.supportedAudioTypes?.length ?? 0) > 0,
2797
+ /** Whether any video types are supported */
2798
+ hasVideoSupport: (capabilities.supportedVideoTypes?.length ?? 0) > 0
2799
+ };
2800
+ }
2801
+ function useDevLogger() {
2802
+ const ctx = useCopilotContext();
2803
+ return react.useMemo(() => {
2804
+ const toolExecutions = (ctx.agentLoop?.toolExecutions || []).map(
2805
+ (exec) => ({
2806
+ id: exec.id,
2807
+ name: exec.name,
2808
+ status: exec.status,
2809
+ approvalStatus: exec.approvalStatus || "not_required"
2810
+ })
2811
+ );
2812
+ const pendingApprovalsCount = ctx.pendingApprovals?.length || 0;
2813
+ const registeredTools = (ctx.registeredTools || []).map((tool) => ({
2814
+ name: tool.name,
2815
+ location: tool.location || "client"
2816
+ }));
2817
+ const registeredActions = (ctx.registeredActions || []).map((action) => ({
2818
+ name: action.name
2819
+ }));
2820
+ const storedPermissions = (ctx.storedPermissions || []).map((p) => ({
2821
+ toolName: p.toolName,
2822
+ level: p.level
2823
+ }));
2824
+ return {
2825
+ chat: {
2826
+ isLoading: ctx.chat?.isLoading || false,
2827
+ messageCount: ctx.chat?.messages?.length || 0,
2828
+ threadId: ctx.chat?.threadId || "none",
2829
+ error: ctx.chat?.error?.message || null
2830
+ },
2831
+ tools: {
2832
+ isEnabled: !!ctx.toolsConfig,
2833
+ isCapturing: ctx.tools?.isCapturing || false,
2834
+ pendingConsent: !!ctx.tools?.pendingConsent
2835
+ },
2836
+ agentLoop: {
2837
+ toolExecutions,
2838
+ pendingApprovals: pendingApprovalsCount,
2839
+ iteration: ctx.agentLoop?.iteration || 0,
2840
+ maxIterations: ctx.agentLoop?.maxIterations || 10
2841
+ },
2842
+ registered: {
2843
+ tools: registeredTools,
2844
+ actions: registeredActions,
2845
+ contextCount: ctx.contextTree?.length || 0
2846
+ },
2847
+ permissions: {
2848
+ stored: storedPermissions,
2849
+ loaded: ctx.permissionsLoaded || false
2850
+ },
2851
+ config: {
2852
+ provider: ctx.config?.config?.provider || (ctx.config?.cloud ? "yourgpt-cloud" : "unknown"),
2853
+ model: ctx.config?.config?.model || "default",
2854
+ runtimeUrl: ctx.config?.runtimeUrl || ctx.config?.cloud?.endpoint || ""
2855
+ }
2856
+ };
2857
+ }, [
2858
+ ctx.chat,
2859
+ ctx.tools,
2860
+ ctx.toolsConfig,
2861
+ ctx.agentLoop,
2862
+ ctx.pendingApprovals,
2863
+ ctx.registeredTools,
2864
+ ctx.registeredActions,
2865
+ ctx.contextTree,
2866
+ ctx.storedPermissions,
2867
+ ctx.permissionsLoaded,
2868
+ ctx.config
2869
+ ]);
2870
+ }
2871
+
2872
+ // src/react/utils/permission-storage.ts
2873
+ var DEFAULT_KEY_PREFIX = "yourgpt-permissions";
2874
+ function createPermissionStorage(config) {
2875
+ switch (config.type) {
2876
+ case "localStorage":
2877
+ return createBrowserStorageAdapter(
2878
+ typeof window !== "undefined" ? localStorage : null,
2879
+ config.keyPrefix
2880
+ );
2881
+ case "sessionStorage":
2882
+ return createBrowserStorageAdapter(
2883
+ typeof window !== "undefined" ? sessionStorage : null,
2884
+ config.keyPrefix
2885
+ );
2886
+ case "memory":
2887
+ default:
2888
+ return createMemoryStorageAdapter();
2889
+ }
2890
+ }
2891
+ function createBrowserStorageAdapter(storage, keyPrefix = DEFAULT_KEY_PREFIX) {
2892
+ const getStorageKey = () => keyPrefix;
2893
+ const loadPermissions = () => {
2894
+ if (!storage) return /* @__PURE__ */ new Map();
2895
+ try {
2896
+ const data = storage.getItem(getStorageKey());
2897
+ if (!data) return /* @__PURE__ */ new Map();
2898
+ const parsed = JSON.parse(data);
2899
+ return new Map(parsed.map((p) => [p.toolName, p]));
2900
+ } catch {
2901
+ return /* @__PURE__ */ new Map();
2902
+ }
2903
+ };
2904
+ const savePermissions = (permissions) => {
2905
+ if (!storage) return;
2906
+ try {
2907
+ storage.setItem(
2908
+ getStorageKey(),
2909
+ JSON.stringify(Array.from(permissions.values()))
2910
+ );
2911
+ } catch (e) {
2912
+ console.warn("[PermissionStorage] Failed to save permissions:", e);
2913
+ }
2914
+ };
2915
+ return {
2916
+ async get(toolName) {
2917
+ const permissions = loadPermissions();
2918
+ return permissions.get(toolName) || null;
2919
+ },
2920
+ async set(permission) {
2921
+ const permissions = loadPermissions();
2922
+ permissions.set(permission.toolName, permission);
2923
+ savePermissions(permissions);
2924
+ },
2925
+ async remove(toolName) {
2926
+ const permissions = loadPermissions();
2927
+ permissions.delete(toolName);
2928
+ savePermissions(permissions);
2929
+ },
2930
+ async getAll() {
2931
+ const permissions = loadPermissions();
2932
+ return Array.from(permissions.values());
2933
+ },
2934
+ async clear() {
2935
+ if (!storage) return;
2936
+ storage.removeItem(getStorageKey());
2937
+ }
2938
+ };
2939
+ }
2940
+ function createMemoryStorageAdapter() {
2941
+ const permissions = /* @__PURE__ */ new Map();
2942
+ return {
2943
+ async get(toolName) {
2944
+ return permissions.get(toolName) || null;
2945
+ },
2946
+ async set(permission) {
2947
+ permissions.set(permission.toolName, permission);
2948
+ },
2949
+ async remove(toolName) {
2950
+ permissions.delete(toolName);
2951
+ },
2952
+ async getAll() {
2953
+ return Array.from(permissions.values());
2954
+ },
2955
+ async clear() {
2956
+ permissions.clear();
2957
+ }
2958
+ };
2959
+ }
2960
+ function createSessionPermissionCache() {
2961
+ return /* @__PURE__ */ new Map();
2962
+ }
2963
+
2964
+ // src/react/internal/ReactChat.ts
2965
+ var ReactChat = class extends AbstractChat {
2966
+ constructor(config) {
2967
+ const reactState = new ReactChatState(config.initialMessages);
2968
+ const init = {
2969
+ runtimeUrl: config.runtimeUrl,
2970
+ systemPrompt: config.systemPrompt,
2971
+ llm: config.llm,
2972
+ threadId: config.threadId,
2973
+ streaming: config.streaming ?? true,
2974
+ headers: config.headers,
2975
+ initialMessages: config.initialMessages,
2976
+ state: reactState,
2977
+ callbacks: config.callbacks,
2978
+ debug: config.debug
2979
+ };
2980
+ super(init);
2981
+ // ============================================
2982
+ // Subscribe (for useSyncExternalStore)
2983
+ // ============================================
2984
+ /**
2985
+ * Subscribe to state changes.
2986
+ * Returns an unsubscribe function.
2987
+ *
2988
+ * @example
2989
+ * ```tsx
2990
+ * const messages = useSyncExternalStore(
2991
+ * chat.subscribe,
2992
+ * () => chat.messages
2993
+ * );
2994
+ * ```
2995
+ */
2996
+ this.subscribe = (callback) => {
2997
+ return this.reactState.subscribe(callback);
2998
+ };
2999
+ this.reactState = reactState;
3000
+ }
3001
+ // ============================================
3002
+ // Event handling shortcuts
3003
+ // ============================================
3004
+ /**
3005
+ * Subscribe to tool calls events
3006
+ */
3007
+ onToolCalls(handler) {
3008
+ return this.on("toolCalls", handler);
3009
+ }
3010
+ /**
3011
+ * Subscribe to done events
3012
+ */
3013
+ onDone(handler) {
3014
+ return this.on("done", handler);
3015
+ }
3016
+ /**
3017
+ * Subscribe to error events
3018
+ */
3019
+ onError(handler) {
3020
+ return this.on("error", handler);
3021
+ }
3022
+ // ============================================
3023
+ // Override dispose to clean up state
3024
+ // ============================================
3025
+ dispose() {
3026
+ super.dispose();
3027
+ this.reactState.dispose();
3028
+ }
3029
+ };
3030
+ function createReactChat(config) {
3031
+ return new ReactChat(config);
3032
+ }
3033
+ function useChat(config) {
3034
+ const chatRef = react.useRef(null);
3035
+ const [input, setInput] = react.useState("");
3036
+ if (chatRef.current === null) {
3037
+ chatRef.current = createReactChat({
3038
+ runtimeUrl: config.runtimeUrl,
3039
+ systemPrompt: config.systemPrompt,
3040
+ llm: config.llm,
3041
+ threadId: config.threadId,
3042
+ streaming: config.streaming,
3043
+ headers: config.headers,
3044
+ initialMessages: config.initialMessages,
3045
+ debug: config.debug,
3046
+ callbacks: {
3047
+ onMessagesChange: config.onMessagesChange,
3048
+ onError: config.onError,
3049
+ onFinish: config.onFinish,
3050
+ onToolCalls: config.onToolCalls
3051
+ }
3052
+ });
3053
+ }
3054
+ const messages = react.useSyncExternalStore(
3055
+ chatRef.current.subscribe,
3056
+ () => chatRef.current.messages,
3057
+ () => chatRef.current.messages
3058
+ // Server snapshot
3059
+ );
3060
+ const status = react.useSyncExternalStore(
3061
+ chatRef.current.subscribe,
3062
+ () => chatRef.current.status,
3063
+ () => "ready"
3064
+ // Server snapshot
3065
+ );
3066
+ const error = react.useSyncExternalStore(
3067
+ chatRef.current.subscribe,
3068
+ () => chatRef.current.error,
3069
+ () => void 0
3070
+ // Server snapshot
3071
+ );
3072
+ const isLoading = status === "streaming" || status === "submitted";
3073
+ const sendMessage = react.useCallback(
3074
+ async (content, attachments) => {
3075
+ await chatRef.current?.sendMessage(content, attachments);
3076
+ setInput("");
3077
+ },
3078
+ []
3079
+ );
3080
+ const stop = react.useCallback(() => {
3081
+ chatRef.current?.stop();
3082
+ }, []);
3083
+ const clearMessages = react.useCallback(() => {
3084
+ chatRef.current?.clearMessages();
3085
+ }, []);
3086
+ const setMessages = react.useCallback((messages2) => {
3087
+ chatRef.current?.setMessages(messages2);
3088
+ }, []);
3089
+ const regenerate = react.useCallback(async (messageId) => {
3090
+ await chatRef.current?.regenerate(messageId);
3091
+ }, []);
3092
+ const continueWithToolResults = react.useCallback(
3093
+ async (toolResults) => {
3094
+ await chatRef.current?.continueWithToolResults(toolResults);
3095
+ },
3096
+ []
3097
+ );
3098
+ react.useEffect(() => {
3099
+ return () => {
3100
+ chatRef.current?.dispose();
3101
+ };
3102
+ }, []);
3103
+ return {
3104
+ messages,
3105
+ status,
3106
+ error,
3107
+ isLoading,
3108
+ input,
3109
+ setInput,
3110
+ sendMessage,
3111
+ stop,
3112
+ clearMessages,
3113
+ setMessages,
3114
+ regenerate,
3115
+ continueWithToolResults,
3116
+ chatRef
3117
+ };
3118
+ }
3119
+
3120
+ exports.AbstractAgentLoop = AbstractAgentLoop;
3121
+ exports.AbstractChat = AbstractChat;
3122
+ exports.CopilotProvider = CopilotProvider;
3123
+ exports.ReactChat = ReactChat;
3124
+ exports.ReactChatState = ReactChatState;
3125
+ exports.createPermissionStorage = createPermissionStorage;
3126
+ exports.createReactChat = createReactChat;
3127
+ exports.createReactChatState = createReactChatState;
3128
+ exports.createSessionPermissionCache = createSessionPermissionCache;
3129
+ exports.formatKnowledgeResultsForAI = formatKnowledgeResultsForAI;
3130
+ exports.initialAgentLoopState = initialAgentLoopState;
3131
+ exports.searchKnowledgeBase = searchKnowledgeBase;
3132
+ exports.useAIAction = useAIAction;
3133
+ exports.useAIActions = useAIActions;
3134
+ exports.useAIContext = useAIContext;
3135
+ exports.useAIContexts = useAIContexts;
3136
+ exports.useAITools = useAITools;
3137
+ exports.useAgent = useAgent;
3138
+ exports.useCapabilities = useCapabilities;
3139
+ exports.useChat = useChat;
3140
+ exports.useCopilot = useCopilot;
3141
+ exports.useDevLogger = useDevLogger;
3142
+ exports.useFeatureSupport = useFeatureSupport;
3143
+ exports.useKnowledgeBase = useKnowledgeBase;
3144
+ exports.useSuggestions = useSuggestions;
3145
+ exports.useSupportedMediaTypes = useSupportedMediaTypes;
3146
+ exports.useTool = useTool;
3147
+ exports.useToolExecutor = useToolExecutor;
3148
+ exports.useToolWithSchema = useToolWithSchema;
3149
+ exports.useTools = useTools;
3150
+ exports.useToolsWithSchema = useToolsWithSchema;
3151
+ //# sourceMappingURL=chunk-W6KQT7YZ.cjs.map
3152
+ //# sourceMappingURL=chunk-W6KQT7YZ.cjs.map