@yourgpt/copilot-sdk 2.1.5-alpha.3 → 2.1.5-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{MessageTree-CoIt_4nB.d.cts → MessageTree-Clhiv_k2.d.ts} +5 -4
- package/dist/{MessageTree-CzaN9Eul.d.ts → MessageTree-Dt9qfJ55.d.cts} +5 -4
- package/dist/{ThreadManager-BEAECB7Y.d.ts → ThreadManager-D7KwT2FJ.d.ts} +3 -1
- package/dist/{ThreadManager-Cw5fwyCN.d.cts → ThreadManager-DK46fVl3.d.cts} +3 -1
- package/dist/{chunk-NUXLAZOE.cjs → chunk-3ZDRX7J2.cjs} +2 -2
- package/dist/{chunk-NUXLAZOE.cjs.map → chunk-3ZDRX7J2.cjs.map} +1 -1
- package/dist/{chunk-RKGRQRZU.js → chunk-533K2Z7C.js} +4 -4
- package/dist/{chunk-RKGRQRZU.js.map → chunk-533K2Z7C.js.map} +1 -1
- package/dist/chunk-5EGBIQYS.cjs +292 -0
- package/dist/chunk-5EGBIQYS.cjs.map +1 -0
- package/dist/chunk-5UGWLGFS.cjs +2039 -0
- package/dist/chunk-5UGWLGFS.cjs.map +1 -0
- package/dist/{chunk-3AONOZLY.js → chunk-AIVXGTWS.js} +2 -2
- package/dist/chunk-AIVXGTWS.js.map +1 -0
- package/dist/{chunk-LLM7AHMO.js → chunk-DDZLRCVX.js} +2 -2
- package/dist/{chunk-LLM7AHMO.js.map → chunk-DDZLRCVX.js.map} +1 -1
- package/dist/{chunk-PT2TOHG5.js → chunk-DH6EO6NW.js} +1337 -3049
- package/dist/chunk-DH6EO6NW.js.map +1 -0
- package/dist/{chunk-WIXFZUEZ.cjs → chunk-KGYDGK3U.cjs} +84 -30
- package/dist/chunk-KGYDGK3U.cjs.map +1 -0
- package/dist/{chunk-TCPAT3WG.cjs → chunk-LHLVTGIP.cjs} +1339 -3101
- package/dist/chunk-LHLVTGIP.cjs.map +1 -0
- package/dist/{chunk-TPB7XED6.cjs → chunk-TPDMBDQX.cjs} +2 -2
- package/dist/chunk-TPDMBDQX.cjs.map +1 -0
- package/dist/chunk-TXQ37MAO.js +287 -0
- package/dist/chunk-TXQ37MAO.js.map +1 -0
- package/dist/{chunk-MDS23G2S.cjs → chunk-Y2A6AMGO.cjs} +10 -10
- package/dist/{chunk-MDS23G2S.cjs.map → chunk-Y2A6AMGO.cjs.map} +1 -1
- package/dist/{chunk-WZ2TOZ7M.js → chunk-YLZCTR4O.js} +65 -11
- package/dist/chunk-YLZCTR4O.js.map +1 -0
- package/dist/chunk-ZAOTYA5L.js +1983 -0
- package/dist/chunk-ZAOTYA5L.js.map +1 -0
- package/dist/core/index.cjs +93 -93
- package/dist/core/index.d.cts +7 -7
- package/dist/core/index.d.ts +7 -7
- package/dist/core/index.js +5 -5
- package/dist/experimental/index.cjs +644 -0
- package/dist/experimental/index.cjs.map +1 -0
- package/dist/experimental/index.d.cts +924 -0
- package/dist/experimental/index.d.ts +924 -0
- package/dist/experimental/index.js +611 -0
- package/dist/experimental/index.js.map +1 -0
- package/dist/{index-D7169xuR.d.ts → index-D8zza1Q8.d.ts} +1 -1
- package/dist/{index-CzJB8Ddo.d.cts → index-DCVjTdIZ.d.cts} +1 -1
- package/dist/mcp/index.d.cts +3 -3
- package/dist/mcp/index.d.ts +3 -3
- package/dist/react/index.cjs +136 -123
- package/dist/react/index.d.cts +178 -12
- package/dist/react/index.d.ts +178 -12
- package/dist/react/index.js +7 -6
- package/dist/styles.css +45 -0
- package/dist/tools/anthropic/index.cjs +3 -3
- package/dist/tools/anthropic/index.d.cts +1 -1
- package/dist/tools/anthropic/index.d.ts +1 -1
- package/dist/tools/anthropic/index.js +2 -2
- package/dist/tools/brave/index.cjs +6 -6
- package/dist/tools/brave/index.d.cts +1 -1
- package/dist/tools/brave/index.d.ts +1 -1
- package/dist/tools/brave/index.js +3 -3
- package/dist/tools/exa/index.cjs +6 -6
- package/dist/tools/exa/index.d.cts +1 -1
- package/dist/tools/exa/index.d.ts +1 -1
- package/dist/tools/exa/index.js +3 -3
- package/dist/tools/google/index.cjs +6 -6
- package/dist/tools/google/index.d.cts +1 -1
- package/dist/tools/google/index.d.ts +1 -1
- package/dist/tools/google/index.js +4 -4
- package/dist/tools/openai/index.cjs +6 -6
- package/dist/tools/openai/index.d.cts +1 -1
- package/dist/tools/openai/index.d.ts +1 -1
- package/dist/tools/openai/index.js +3 -3
- package/dist/tools/searxng/index.cjs +6 -6
- package/dist/tools/searxng/index.d.cts +1 -1
- package/dist/tools/searxng/index.d.ts +1 -1
- package/dist/tools/searxng/index.js +3 -3
- package/dist/tools/serper/index.cjs +6 -6
- package/dist/tools/serper/index.d.cts +1 -1
- package/dist/tools/serper/index.d.ts +1 -1
- package/dist/tools/serper/index.js +3 -3
- package/dist/tools/tavily/index.cjs +6 -6
- package/dist/tools/tavily/index.d.cts +1 -1
- package/dist/tools/tavily/index.d.ts +1 -1
- package/dist/tools/tavily/index.js +3 -3
- package/dist/tools/web-search/index.cjs +7 -7
- package/dist/tools/web-search/index.d.cts +2 -2
- package/dist/tools/web-search/index.d.ts +2 -2
- package/dist/tools/web-search/index.js +4 -4
- package/dist/{tools-tmksfhUo.d.cts → tools-DcS6Aeao.d.cts} +7 -3
- package/dist/{tools-tmksfhUo.d.ts → tools-DcS6Aeao.d.ts} +7 -3
- package/dist/{types-BqwW3Baj.d.ts → types-BUYni9B8.d.ts} +1 -1
- package/dist/{types-BLw7mxtW.d.cts → types-Cvg4DUoc.d.cts} +1 -1
- package/dist/{types-BeFBBZ5i.d.ts → types-waEqyE4K.d.cts} +5 -0
- package/dist/{types-BeFBBZ5i.d.cts → types-waEqyE4K.d.ts} +5 -0
- package/dist/ui/index.cjs +354 -524
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +22 -4
- package/dist/ui/index.d.ts +22 -4
- package/dist/ui/index.js +197 -372
- package/dist/ui/index.js.map +1 -1
- package/package.json +6 -1
- package/dist/chunk-3AONOZLY.js.map +0 -1
- package/dist/chunk-PT2TOHG5.js.map +0 -1
- package/dist/chunk-TCPAT3WG.cjs.map +0 -1
- package/dist/chunk-TPB7XED6.cjs.map +0 -1
- package/dist/chunk-WIXFZUEZ.cjs.map +0 -1
- package/dist/chunk-WZ2TOZ7M.js.map +0 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createLogger, zodToJsonSchema } from './chunk-YLZCTR4O.js';
|
|
2
2
|
import { createMCPClient, MCPToolAdapter } from './chunk-EWVQWTNV.js';
|
|
3
3
|
import { SkillRegistry } from './chunk-VNLLW3ZI.js';
|
|
4
|
-
import React2, { createContext, useRef, useState, useCallback, useEffect,
|
|
4
|
+
import React2, { createContext, useRef, useState, useCallback, useEffect, useContext, useMemo, useSyncExternalStore } from 'react';
|
|
5
5
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
-
import * as z from 'zod';
|
|
7
6
|
|
|
8
7
|
// src/chat/types/tool.ts
|
|
9
8
|
var initialAgentLoopState = {
|
|
@@ -14,88 +13,6 @@ var initialAgentLoopState = {
|
|
|
14
13
|
isProcessing: false
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
// src/chat/interfaces/ChatState.ts
|
|
18
|
-
var SimpleChatState = class {
|
|
19
|
-
constructor() {
|
|
20
|
-
this._messages = [];
|
|
21
|
-
this._status = "ready";
|
|
22
|
-
this._error = void 0;
|
|
23
|
-
this.callbacks = /* @__PURE__ */ new Set();
|
|
24
|
-
}
|
|
25
|
-
get messages() {
|
|
26
|
-
return this._messages;
|
|
27
|
-
}
|
|
28
|
-
set messages(value) {
|
|
29
|
-
this._messages = value;
|
|
30
|
-
this.notify();
|
|
31
|
-
}
|
|
32
|
-
get status() {
|
|
33
|
-
return this._status;
|
|
34
|
-
}
|
|
35
|
-
set status(value) {
|
|
36
|
-
this._status = value;
|
|
37
|
-
this.notify();
|
|
38
|
-
}
|
|
39
|
-
get error() {
|
|
40
|
-
return this._error;
|
|
41
|
-
}
|
|
42
|
-
set error(value) {
|
|
43
|
-
this._error = value;
|
|
44
|
-
this.notify();
|
|
45
|
-
}
|
|
46
|
-
pushMessage(message) {
|
|
47
|
-
this._messages = [...this._messages, message];
|
|
48
|
-
this.notify();
|
|
49
|
-
}
|
|
50
|
-
popMessage() {
|
|
51
|
-
this._messages = this._messages.slice(0, -1);
|
|
52
|
-
this.notify();
|
|
53
|
-
}
|
|
54
|
-
replaceMessage(index, message) {
|
|
55
|
-
this._messages = [
|
|
56
|
-
...this._messages.slice(0, index),
|
|
57
|
-
message,
|
|
58
|
-
...this._messages.slice(index + 1)
|
|
59
|
-
];
|
|
60
|
-
this.notify();
|
|
61
|
-
}
|
|
62
|
-
updateLastMessage(updater) {
|
|
63
|
-
if (this._messages.length === 0) return;
|
|
64
|
-
const lastIndex = this._messages.length - 1;
|
|
65
|
-
this.replaceMessage(lastIndex, updater(this._messages[lastIndex]));
|
|
66
|
-
}
|
|
67
|
-
updateMessageById(id, updater) {
|
|
68
|
-
const index = this._messages.findIndex((m) => m.id === id);
|
|
69
|
-
if (index === -1) return false;
|
|
70
|
-
this.replaceMessage(index, updater(this._messages[index]));
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
setMessages(messages) {
|
|
74
|
-
this._messages = messages;
|
|
75
|
-
this.notify();
|
|
76
|
-
}
|
|
77
|
-
clearMessages() {
|
|
78
|
-
this._messages = [];
|
|
79
|
-
this.notify();
|
|
80
|
-
}
|
|
81
|
-
subscribe(callback) {
|
|
82
|
-
this.callbacks.add(callback);
|
|
83
|
-
return () => this.callbacks.delete(callback);
|
|
84
|
-
}
|
|
85
|
-
getMessagesSnapshot() {
|
|
86
|
-
return this._messages;
|
|
87
|
-
}
|
|
88
|
-
getStatusSnapshot() {
|
|
89
|
-
return this._status;
|
|
90
|
-
}
|
|
91
|
-
getErrorSnapshot() {
|
|
92
|
-
return this._error;
|
|
93
|
-
}
|
|
94
|
-
notify() {
|
|
95
|
-
this.callbacks.forEach((cb) => cb());
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
16
|
// src/chat/functions/stream/parseSSE.ts
|
|
100
17
|
function parseSSELine(line) {
|
|
101
18
|
if (!line || line.trim() === "") {
|
|
@@ -516,6 +433,88 @@ var HttpTransport = class {
|
|
|
516
433
|
}
|
|
517
434
|
};
|
|
518
435
|
|
|
436
|
+
// src/chat/interfaces/ChatState.ts
|
|
437
|
+
var SimpleChatState = class {
|
|
438
|
+
constructor() {
|
|
439
|
+
this._messages = [];
|
|
440
|
+
this._status = "ready";
|
|
441
|
+
this._error = void 0;
|
|
442
|
+
this.callbacks = /* @__PURE__ */ new Set();
|
|
443
|
+
}
|
|
444
|
+
get messages() {
|
|
445
|
+
return this._messages;
|
|
446
|
+
}
|
|
447
|
+
set messages(value) {
|
|
448
|
+
this._messages = value;
|
|
449
|
+
this.notify();
|
|
450
|
+
}
|
|
451
|
+
get status() {
|
|
452
|
+
return this._status;
|
|
453
|
+
}
|
|
454
|
+
set status(value) {
|
|
455
|
+
this._status = value;
|
|
456
|
+
this.notify();
|
|
457
|
+
}
|
|
458
|
+
get error() {
|
|
459
|
+
return this._error;
|
|
460
|
+
}
|
|
461
|
+
set error(value) {
|
|
462
|
+
this._error = value;
|
|
463
|
+
this.notify();
|
|
464
|
+
}
|
|
465
|
+
pushMessage(message) {
|
|
466
|
+
this._messages = [...this._messages, message];
|
|
467
|
+
this.notify();
|
|
468
|
+
}
|
|
469
|
+
popMessage() {
|
|
470
|
+
this._messages = this._messages.slice(0, -1);
|
|
471
|
+
this.notify();
|
|
472
|
+
}
|
|
473
|
+
replaceMessage(index, message) {
|
|
474
|
+
this._messages = [
|
|
475
|
+
...this._messages.slice(0, index),
|
|
476
|
+
message,
|
|
477
|
+
...this._messages.slice(index + 1)
|
|
478
|
+
];
|
|
479
|
+
this.notify();
|
|
480
|
+
}
|
|
481
|
+
updateLastMessage(updater) {
|
|
482
|
+
if (this._messages.length === 0) return;
|
|
483
|
+
const lastIndex = this._messages.length - 1;
|
|
484
|
+
this.replaceMessage(lastIndex, updater(this._messages[lastIndex]));
|
|
485
|
+
}
|
|
486
|
+
updateMessageById(id, updater) {
|
|
487
|
+
const index = this._messages.findIndex((m) => m.id === id);
|
|
488
|
+
if (index === -1) return false;
|
|
489
|
+
this.replaceMessage(index, updater(this._messages[index]));
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
setMessages(messages) {
|
|
493
|
+
this._messages = messages;
|
|
494
|
+
this.notify();
|
|
495
|
+
}
|
|
496
|
+
clearMessages() {
|
|
497
|
+
this._messages = [];
|
|
498
|
+
this.notify();
|
|
499
|
+
}
|
|
500
|
+
subscribe(callback) {
|
|
501
|
+
this.callbacks.add(callback);
|
|
502
|
+
return () => this.callbacks.delete(callback);
|
|
503
|
+
}
|
|
504
|
+
getMessagesSnapshot() {
|
|
505
|
+
return this._messages;
|
|
506
|
+
}
|
|
507
|
+
getStatusSnapshot() {
|
|
508
|
+
return this._status;
|
|
509
|
+
}
|
|
510
|
+
getErrorSnapshot() {
|
|
511
|
+
return this._error;
|
|
512
|
+
}
|
|
513
|
+
notify() {
|
|
514
|
+
this.callbacks.forEach((cb) => cb());
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
|
|
519
518
|
// src/chat/optimizations.ts
|
|
520
519
|
var DEFAULT_CHARS_PER_TOKEN = 4;
|
|
521
520
|
var DEFAULT_SAFETY_MARGIN = 1.2;
|
|
@@ -637,15 +636,15 @@ ${attachments}`,
|
|
|
637
636
|
charsPerToken
|
|
638
637
|
);
|
|
639
638
|
}
|
|
640
|
-
function estimateToolTokens(
|
|
641
|
-
return estimateTokens(JSON.stringify(
|
|
639
|
+
function estimateToolTokens(tool, charsPerToken = DEFAULT_CHARS_PER_TOKEN) {
|
|
640
|
+
return estimateTokens(JSON.stringify(tool), charsPerToken);
|
|
642
641
|
}
|
|
643
642
|
function buildToolQuery(messages) {
|
|
644
643
|
return messages.filter(
|
|
645
644
|
(message) => message.role === "user" || message.role === "assistant"
|
|
646
645
|
).slice(-3).map((message) => message.content).filter(Boolean).join(" ");
|
|
647
646
|
}
|
|
648
|
-
function matchesSelector(
|
|
647
|
+
function matchesSelector(tool, selector, activeProfile) {
|
|
649
648
|
const normalized = selector.trim().toLowerCase();
|
|
650
649
|
if (!normalized) {
|
|
651
650
|
return false;
|
|
@@ -653,40 +652,40 @@ function matchesSelector(tool2, selector, activeProfile) {
|
|
|
653
652
|
if (normalized === "*" || normalized === "all") {
|
|
654
653
|
return true;
|
|
655
654
|
}
|
|
656
|
-
if (normalized ===
|
|
655
|
+
if (normalized === tool.name.toLowerCase()) {
|
|
657
656
|
return true;
|
|
658
657
|
}
|
|
659
658
|
if (normalized.startsWith("group:")) {
|
|
660
|
-
return (
|
|
659
|
+
return (tool.group ?? "").toLowerCase() === normalized.slice(6);
|
|
661
660
|
}
|
|
662
661
|
if (normalized.startsWith("category:")) {
|
|
663
|
-
return (
|
|
662
|
+
return (tool.category ?? "").toLowerCase() === normalized.slice(9);
|
|
664
663
|
}
|
|
665
664
|
if (normalized.startsWith("profile:")) {
|
|
666
|
-
return (
|
|
665
|
+
return (tool.profiles ?? []).map((value) => value.toLowerCase()).includes(normalized.slice(8));
|
|
667
666
|
}
|
|
668
667
|
if (activeProfile && normalized === activeProfile.toLowerCase()) {
|
|
669
|
-
return (
|
|
668
|
+
return (tool.profiles ?? []).map((value) => value.toLowerCase()).includes(normalized);
|
|
670
669
|
}
|
|
671
670
|
return false;
|
|
672
671
|
}
|
|
673
|
-
function scoreTool(
|
|
672
|
+
function scoreTool(tool, queryTokens, activeProfile) {
|
|
674
673
|
const haystack = [
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
...
|
|
680
|
-
...
|
|
674
|
+
tool.name,
|
|
675
|
+
tool.description,
|
|
676
|
+
tool.category,
|
|
677
|
+
tool.group,
|
|
678
|
+
...tool.profiles ?? [],
|
|
679
|
+
...tool.searchKeywords ?? []
|
|
681
680
|
].filter(Boolean).join(" ").toLowerCase();
|
|
682
|
-
let score =
|
|
683
|
-
if (activeProfile &&
|
|
681
|
+
let score = tool.deferLoading ? 0 : 2;
|
|
682
|
+
if (activeProfile && tool.profiles?.includes(activeProfile)) {
|
|
684
683
|
score += 2;
|
|
685
684
|
}
|
|
686
685
|
for (const token of queryTokens) {
|
|
687
|
-
if (
|
|
686
|
+
if (tool.name.toLowerCase() === token) {
|
|
688
687
|
score += 6;
|
|
689
|
-
} else if (
|
|
688
|
+
} else if (tool.name.toLowerCase().includes(token)) {
|
|
690
689
|
score += 4;
|
|
691
690
|
} else if (haystack.includes(token)) {
|
|
692
691
|
score += 2;
|
|
@@ -900,15 +899,15 @@ function buildToolDefinitions(selectedTools) {
|
|
|
900
899
|
if (selectedTools.length === 0) {
|
|
901
900
|
return void 0;
|
|
902
901
|
}
|
|
903
|
-
return selectedTools.map((
|
|
904
|
-
name:
|
|
905
|
-
description:
|
|
906
|
-
category:
|
|
907
|
-
group:
|
|
908
|
-
deferLoading:
|
|
909
|
-
profiles:
|
|
910
|
-
searchKeywords:
|
|
911
|
-
inputSchema:
|
|
902
|
+
return selectedTools.map((tool) => ({
|
|
903
|
+
name: tool.name,
|
|
904
|
+
description: tool.description,
|
|
905
|
+
category: tool.category,
|
|
906
|
+
group: tool.group,
|
|
907
|
+
deferLoading: tool.deferLoading,
|
|
908
|
+
profiles: tool.profiles,
|
|
909
|
+
searchKeywords: tool.searchKeywords,
|
|
910
|
+
inputSchema: tool.inputSchema
|
|
912
911
|
}));
|
|
913
912
|
}
|
|
914
913
|
function resolveTruncationConfig(params) {
|
|
@@ -938,21 +937,21 @@ function resolveTruncationConfig(params) {
|
|
|
938
937
|
preserveErrors: merged.preserveErrors ?? true
|
|
939
938
|
};
|
|
940
939
|
}
|
|
941
|
-
function buildToolResultContent(result,
|
|
940
|
+
function buildToolResultContent(result, tool, args) {
|
|
942
941
|
if (typeof result === "string") {
|
|
943
942
|
return result;
|
|
944
943
|
}
|
|
945
944
|
const typedResult = result ?? null;
|
|
946
|
-
const responseMode = typedResult?._aiResponseMode ??
|
|
945
|
+
const responseMode = typedResult?._aiResponseMode ?? tool?.aiResponseMode ?? "full";
|
|
947
946
|
if (typedResult?._aiContent) {
|
|
948
947
|
return JSON.stringify(typedResult._aiContent);
|
|
949
948
|
}
|
|
950
949
|
let aiContext = typedResult?._aiContext;
|
|
951
|
-
if (!aiContext &&
|
|
952
|
-
aiContext = typeof
|
|
950
|
+
if (!aiContext && tool?.aiContext) {
|
|
951
|
+
aiContext = typeof tool.aiContext === "function" ? tool.aiContext(
|
|
953
952
|
typedResult ?? { success: true },
|
|
954
953
|
args ?? {}
|
|
955
|
-
) :
|
|
954
|
+
) : tool.aiContext;
|
|
956
955
|
}
|
|
957
956
|
switch (responseMode) {
|
|
958
957
|
case "none":
|
|
@@ -981,9 +980,9 @@ Full data: ${JSON.stringify(dataOnly)}`;
|
|
|
981
980
|
}
|
|
982
981
|
}
|
|
983
982
|
}
|
|
984
|
-
function buildToolResultContentForPrompt(result,
|
|
985
|
-
const text = buildToolResultContent(result,
|
|
986
|
-
const truncation = resolveTruncationConfig({ tool
|
|
983
|
+
function buildToolResultContentForPrompt(result, tool, args, config) {
|
|
984
|
+
const text = buildToolResultContent(result, tool, args);
|
|
985
|
+
const truncation = resolveTruncationConfig({ tool, config });
|
|
987
986
|
if (!truncation.enabled) {
|
|
988
987
|
return text;
|
|
989
988
|
}
|
|
@@ -1089,7 +1088,7 @@ function fitToolsToBudget(params) {
|
|
|
1089
1088
|
return tools;
|
|
1090
1089
|
}
|
|
1091
1090
|
const getToolTokens = () => tools.reduce(
|
|
1092
|
-
(sum,
|
|
1091
|
+
(sum, tool) => sum + estimateToolTokens(tool, params.charsPerToken),
|
|
1093
1092
|
0
|
|
1094
1093
|
);
|
|
1095
1094
|
while (tools.length > 0 && getToolTokens() > params.maxTokens) {
|
|
@@ -1127,7 +1126,7 @@ function calculateBuckets(params) {
|
|
|
1127
1126
|
0
|
|
1128
1127
|
);
|
|
1129
1128
|
const toolDefinitionTokens = (params.requestTools ?? []).reduce(
|
|
1130
|
-
(sum,
|
|
1129
|
+
(sum, tool) => sum + estimateToolTokens(tool, params.charsPerToken),
|
|
1131
1130
|
0
|
|
1132
1131
|
);
|
|
1133
1132
|
const total = systemPromptTokens + historyTokens + toolResultsTokens + toolDefinitionTokens;
|
|
@@ -1366,7 +1365,7 @@ var ChatContextOptimizer = class {
|
|
|
1366
1365
|
if (!tools.length) {
|
|
1367
1366
|
return [];
|
|
1368
1367
|
}
|
|
1369
|
-
const available = tools.filter((
|
|
1368
|
+
const available = tools.filter((tool) => tool.available !== false);
|
|
1370
1369
|
const profileConfig = this.config?.toolProfiles;
|
|
1371
1370
|
if (!profileConfig?.enabled) {
|
|
1372
1371
|
return available;
|
|
@@ -1377,22 +1376,22 @@ var ChatContextOptimizer = class {
|
|
|
1377
1376
|
let filtered = available;
|
|
1378
1377
|
if (profile?.include?.length) {
|
|
1379
1378
|
filtered = filtered.filter(
|
|
1380
|
-
(
|
|
1381
|
-
(selector) => matchesSelector(
|
|
1382
|
-
) || !!activeProfile &&
|
|
1379
|
+
(tool) => profile.include.some(
|
|
1380
|
+
(selector) => matchesSelector(tool, selector, activeProfile)
|
|
1381
|
+
) || !!activeProfile && tool.profiles?.includes(activeProfile)
|
|
1383
1382
|
);
|
|
1384
1383
|
} else if (activeProfile) {
|
|
1385
|
-
filtered = filtered.filter((
|
|
1386
|
-
if (
|
|
1387
|
-
return
|
|
1384
|
+
filtered = filtered.filter((tool) => {
|
|
1385
|
+
if (tool.profiles?.length) {
|
|
1386
|
+
return tool.profiles.includes(activeProfile);
|
|
1388
1387
|
}
|
|
1389
1388
|
return includeUnprofiled;
|
|
1390
1389
|
});
|
|
1391
1390
|
}
|
|
1392
1391
|
if (profile?.exclude?.length) {
|
|
1393
1392
|
filtered = filtered.filter(
|
|
1394
|
-
(
|
|
1395
|
-
(selector) => matchesSelector(
|
|
1393
|
+
(tool) => !profile.exclude.some(
|
|
1394
|
+
(selector) => matchesSelector(tool, selector, activeProfile)
|
|
1396
1395
|
)
|
|
1397
1396
|
);
|
|
1398
1397
|
}
|
|
@@ -1436,7 +1435,7 @@ var ChatContextOptimizer = class {
|
|
|
1436
1435
|
}
|
|
1437
1436
|
}
|
|
1438
1437
|
const toolDefMap = new Map(
|
|
1439
|
-
allTools.map((
|
|
1438
|
+
allTools.map((tool) => [tool.name, tool])
|
|
1440
1439
|
);
|
|
1441
1440
|
return messages.map((message) => {
|
|
1442
1441
|
if (message.role !== "tool") {
|
|
@@ -1449,20 +1448,20 @@ var ChatContextOptimizer = class {
|
|
|
1449
1448
|
};
|
|
1450
1449
|
}
|
|
1451
1450
|
const toolCall = message.toolCallId ? toolCallMap.get(message.toolCallId) : void 0;
|
|
1452
|
-
const
|
|
1451
|
+
const tool = toolCall ? toolDefMap.get(toolCall.toolName) : void 0;
|
|
1453
1452
|
let content = message.content;
|
|
1454
1453
|
try {
|
|
1455
1454
|
const parsed = JSON.parse(message.content);
|
|
1456
1455
|
content = buildToolResultContentForPrompt(
|
|
1457
1456
|
parsed,
|
|
1458
|
-
|
|
1457
|
+
tool,
|
|
1459
1458
|
toolCall?.args ?? {},
|
|
1460
1459
|
this.config
|
|
1461
1460
|
);
|
|
1462
1461
|
} catch {
|
|
1463
1462
|
content = buildToolResultContentForPrompt(
|
|
1464
1463
|
message.content,
|
|
1465
|
-
|
|
1464
|
+
tool,
|
|
1466
1465
|
toolCall?.args ?? {},
|
|
1467
1466
|
this.config
|
|
1468
1467
|
);
|
|
@@ -1585,10 +1584,20 @@ var AbstractChat = class {
|
|
|
1585
1584
|
this.state.pushMessage(userMessage);
|
|
1586
1585
|
this.state.status = "submitted";
|
|
1587
1586
|
this.state.error = void 0;
|
|
1587
|
+
let preCreatedMessageId;
|
|
1588
|
+
if (this.config.streaming !== false) {
|
|
1589
|
+
const visibleMessages2 = this.state.messages;
|
|
1590
|
+
const currentLeafId = visibleMessages2.length > 0 ? visibleMessages2[visibleMessages2.length - 1].id : void 0;
|
|
1591
|
+
const preMsg = createEmptyAssistantMessage(void 0, {
|
|
1592
|
+
parentId: currentLeafId
|
|
1593
|
+
});
|
|
1594
|
+
this.state.pushMessage(preMsg);
|
|
1595
|
+
preCreatedMessageId = preMsg.id;
|
|
1596
|
+
}
|
|
1588
1597
|
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
1589
1598
|
this.callbacks.onStatusChange?.("submitted");
|
|
1590
1599
|
await Promise.resolve();
|
|
1591
|
-
await this.processRequest();
|
|
1600
|
+
await this.processRequest({ preCreatedMessageId });
|
|
1592
1601
|
return true;
|
|
1593
1602
|
} catch (error) {
|
|
1594
1603
|
this.handleError(error);
|
|
@@ -1708,6 +1717,44 @@ var AbstractChat = class {
|
|
|
1708
1717
|
this.handleError(error);
|
|
1709
1718
|
}
|
|
1710
1719
|
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Add tool result messages to history and stop — does NOT trigger a new LLM request.
|
|
1722
|
+
*
|
|
1723
|
+
* Use this instead of continueWithToolResults when you want to close out pending
|
|
1724
|
+
* tool_use blocks (so the history stays valid) without letting the AI continue.
|
|
1725
|
+
* Optionally appends a final assistant message (e.g. an iteration-limit notice).
|
|
1726
|
+
*/
|
|
1727
|
+
async addToolResultMessages(toolResults, finalAssistantContent) {
|
|
1728
|
+
const visibleMessages = this.state.messages;
|
|
1729
|
+
let chainParentId = visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1].id : void 0;
|
|
1730
|
+
for (const { toolCallId, result } of toolResults) {
|
|
1731
|
+
const messageContent = typeof result === "string" ? result : JSON.stringify(result);
|
|
1732
|
+
const toolMessageId = generateMessageId();
|
|
1733
|
+
const toolMessage = {
|
|
1734
|
+
id: toolMessageId,
|
|
1735
|
+
role: "tool",
|
|
1736
|
+
content: messageContent,
|
|
1737
|
+
toolCallId,
|
|
1738
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1739
|
+
...chainParentId !== void 0 ? { parentId: chainParentId } : {}
|
|
1740
|
+
};
|
|
1741
|
+
this.state.pushMessage(toolMessage);
|
|
1742
|
+
chainParentId = toolMessageId;
|
|
1743
|
+
}
|
|
1744
|
+
if (finalAssistantContent) {
|
|
1745
|
+
const assistantMsg = {
|
|
1746
|
+
id: generateMessageId(),
|
|
1747
|
+
role: "assistant",
|
|
1748
|
+
content: finalAssistantContent,
|
|
1749
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1750
|
+
...chainParentId !== void 0 ? { parentId: chainParentId } : {}
|
|
1751
|
+
};
|
|
1752
|
+
this.state.pushMessage(assistantMsg);
|
|
1753
|
+
}
|
|
1754
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
1755
|
+
this.state.status = "ready";
|
|
1756
|
+
this.callbacks.onStatusChange?.("ready");
|
|
1757
|
+
}
|
|
1711
1758
|
/**
|
|
1712
1759
|
* Stop generation
|
|
1713
1760
|
*/
|
|
@@ -1812,10 +1859,10 @@ var AbstractChat = class {
|
|
|
1812
1859
|
/**
|
|
1813
1860
|
* Process a chat request
|
|
1814
1861
|
*/
|
|
1815
|
-
async processRequest() {
|
|
1862
|
+
async processRequest(options) {
|
|
1816
1863
|
const request = this.buildRequest();
|
|
1817
|
-
let preCreatedMessageId;
|
|
1818
|
-
if (this.config.streaming !== false) {
|
|
1864
|
+
let preCreatedMessageId = options?.preCreatedMessageId;
|
|
1865
|
+
if (this.config.streaming !== false && !preCreatedMessageId) {
|
|
1819
1866
|
const visibleMessages = this.state.messages;
|
|
1820
1867
|
const currentLeafId = visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1].id : void 0;
|
|
1821
1868
|
const preMsg = createEmptyAssistantMessage(void 0, {
|
|
@@ -1948,15 +1995,15 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
|
|
|
1948
1995
|
threadId: this.config.threadId,
|
|
1949
1996
|
systemPrompt,
|
|
1950
1997
|
llm: this.config.llm,
|
|
1951
|
-
tools: this.config.tools?.length ? this.config.tools.map((
|
|
1952
|
-
name:
|
|
1953
|
-
description:
|
|
1954
|
-
category:
|
|
1955
|
-
group:
|
|
1956
|
-
deferLoading:
|
|
1957
|
-
profiles:
|
|
1958
|
-
searchKeywords:
|
|
1959
|
-
inputSchema:
|
|
1998
|
+
tools: this.config.tools?.length ? this.config.tools.map((tool) => ({
|
|
1999
|
+
name: tool.name,
|
|
2000
|
+
description: tool.description,
|
|
2001
|
+
category: tool.category,
|
|
2002
|
+
group: tool.group,
|
|
2003
|
+
deferLoading: tool.deferLoading,
|
|
2004
|
+
profiles: tool.profiles,
|
|
2005
|
+
searchKeywords: tool.searchKeywords,
|
|
2006
|
+
inputSchema: tool.inputSchema
|
|
1960
2007
|
})) : void 0,
|
|
1961
2008
|
__skills: this.inlineSkills.length ? this.inlineSkills : void 0
|
|
1962
2009
|
};
|
|
@@ -2033,7 +2080,11 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
|
|
|
2033
2080
|
}
|
|
2034
2081
|
if (chunk.type === "message:start" && this.streamState === null) {
|
|
2035
2082
|
this.debug("message:start after mid-stream end - creating new message");
|
|
2036
|
-
const
|
|
2083
|
+
const currentLeaf = this.state.messages;
|
|
2084
|
+
const currentLeafId = currentLeaf.length > 0 ? currentLeaf[currentLeaf.length - 1].id : void 0;
|
|
2085
|
+
const newMessage = createEmptyAssistantMessage(void 0, {
|
|
2086
|
+
parentId: currentLeafId
|
|
2087
|
+
});
|
|
2037
2088
|
this.state.pushMessage(newMessage);
|
|
2038
2089
|
this.streamState = createStreamState(newMessage.id);
|
|
2039
2090
|
this.callbacks.onMessageStart?.(newMessage.id);
|
|
@@ -2103,10 +2154,19 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
|
|
|
2103
2154
|
break;
|
|
2104
2155
|
}
|
|
2105
2156
|
}
|
|
2157
|
+
const insertParentId = insertIdx > 0 ? currentMessages[insertIdx - 1].id : void 0;
|
|
2158
|
+
const linkedToInsert = messagesToInsert.map((msg, i) => ({
|
|
2159
|
+
...msg,
|
|
2160
|
+
parentId: i === 0 ? insertParentId : messagesToInsert[i - 1].id
|
|
2161
|
+
}));
|
|
2162
|
+
const lastInsertedId = linkedToInsert[linkedToInsert.length - 1].id;
|
|
2163
|
+
const updatedCurrent = currentMessages.map(
|
|
2164
|
+
(m, idx) => idx === insertIdx ? { ...m, parentId: lastInsertedId } : m
|
|
2165
|
+
);
|
|
2106
2166
|
this.state.setMessages([
|
|
2107
|
-
...
|
|
2108
|
-
...
|
|
2109
|
-
...
|
|
2167
|
+
...updatedCurrent.slice(0, insertIdx),
|
|
2168
|
+
...linkedToInsert,
|
|
2169
|
+
...updatedCurrent.slice(insertIdx)
|
|
2110
2170
|
]);
|
|
2111
2171
|
}
|
|
2112
2172
|
}
|
|
@@ -2171,6 +2231,10 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
|
|
|
2171
2231
|
...existing.childrenIds !== void 0 ? { childrenIds: existing.childrenIds } : {}
|
|
2172
2232
|
})
|
|
2173
2233
|
);
|
|
2234
|
+
this.callbacks.onStreamChunk?.({
|
|
2235
|
+
...chunk,
|
|
2236
|
+
messageId: assistantMessage.id
|
|
2237
|
+
});
|
|
2174
2238
|
if (chunk.type === "message:delta") {
|
|
2175
2239
|
this.callbacks.onMessageDelta?.(assistantMessage.id, chunk.content);
|
|
2176
2240
|
}
|
|
@@ -2232,12 +2296,26 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt;
|
|
|
2232
2296
|
(message) => message.id === this.streamState.messageId
|
|
2233
2297
|
) : -1;
|
|
2234
2298
|
if (currentStreamIndex === -1) {
|
|
2235
|
-
|
|
2299
|
+
const appendParentId = currentMessages.length > 0 ? currentMessages[currentMessages.length - 1].id : void 0;
|
|
2300
|
+
const linkedToInsert = messagesToInsert.map((msg, i) => ({
|
|
2301
|
+
...msg,
|
|
2302
|
+
parentId: i === 0 ? appendParentId : messagesToInsert[i - 1].id
|
|
2303
|
+
}));
|
|
2304
|
+
this.state.setMessages([...currentMessages, ...linkedToInsert]);
|
|
2236
2305
|
} else {
|
|
2306
|
+
const insertParentId = currentStreamIndex > 0 ? currentMessages[currentStreamIndex - 1].id : void 0;
|
|
2307
|
+
const linkedToInsert = messagesToInsert.map((msg, i) => ({
|
|
2308
|
+
...msg,
|
|
2309
|
+
parentId: i === 0 ? insertParentId : messagesToInsert[i - 1].id
|
|
2310
|
+
}));
|
|
2311
|
+
const lastInsertedId = linkedToInsert[linkedToInsert.length - 1].id;
|
|
2312
|
+
const updatedCurrent = currentMessages.map(
|
|
2313
|
+
(m, idx) => idx === currentStreamIndex ? { ...m, parentId: lastInsertedId } : m
|
|
2314
|
+
);
|
|
2237
2315
|
this.state.setMessages([
|
|
2238
|
-
...
|
|
2239
|
-
...
|
|
2240
|
-
...
|
|
2316
|
+
...updatedCurrent.slice(0, currentStreamIndex),
|
|
2317
|
+
...linkedToInsert,
|
|
2318
|
+
...updatedCurrent.slice(currentStreamIndex)
|
|
2241
2319
|
]);
|
|
2242
2320
|
}
|
|
2243
2321
|
}
|
|
@@ -2477,8 +2555,8 @@ var AbstractAgentLoop = class {
|
|
|
2477
2555
|
this._maxIterations = config.maxIterations ?? 20;
|
|
2478
2556
|
this._maxExecutionHistory = config.maxExecutionHistory ?? 100;
|
|
2479
2557
|
if (config.tools) {
|
|
2480
|
-
for (const
|
|
2481
|
-
this.registerTool(
|
|
2558
|
+
for (const tool of config.tools) {
|
|
2559
|
+
this.registerTool(tool);
|
|
2482
2560
|
}
|
|
2483
2561
|
}
|
|
2484
2562
|
}
|
|
@@ -2567,15 +2645,15 @@ var AbstractAgentLoop = class {
|
|
|
2567
2645
|
* Register a tool (reference counted for React StrictMode compatibility)
|
|
2568
2646
|
* Tools are never fully removed - ref counting ensures StrictMode works correctly
|
|
2569
2647
|
*/
|
|
2570
|
-
registerTool(
|
|
2571
|
-
const existing = this.registeredTools.get(
|
|
2648
|
+
registerTool(tool) {
|
|
2649
|
+
const existing = this.registeredTools.get(tool.name);
|
|
2572
2650
|
if (existing) {
|
|
2573
|
-
existing.tool =
|
|
2651
|
+
existing.tool = tool;
|
|
2574
2652
|
existing.refCount++;
|
|
2575
2653
|
existing.active = true;
|
|
2576
2654
|
} else {
|
|
2577
|
-
this.registeredTools.set(
|
|
2578
|
-
tool
|
|
2655
|
+
this.registeredTools.set(tool.name, {
|
|
2656
|
+
tool,
|
|
2579
2657
|
refCount: 1,
|
|
2580
2658
|
active: true
|
|
2581
2659
|
});
|
|
@@ -2640,7 +2718,7 @@ var AbstractAgentLoop = class {
|
|
|
2640
2718
|
* Execute a single tool
|
|
2641
2719
|
*/
|
|
2642
2720
|
async executeSingleTool(toolCall) {
|
|
2643
|
-
const
|
|
2721
|
+
const tool = this.getTool(toolCall.name);
|
|
2644
2722
|
const execution = {
|
|
2645
2723
|
id: toolCall.id,
|
|
2646
2724
|
toolCallId: toolCall.id,
|
|
@@ -2649,11 +2727,11 @@ var AbstractAgentLoop = class {
|
|
|
2649
2727
|
status: "pending",
|
|
2650
2728
|
approvalStatus: "none",
|
|
2651
2729
|
startedAt: /* @__PURE__ */ new Date(),
|
|
2652
|
-
hidden:
|
|
2730
|
+
hidden: tool?.hidden
|
|
2653
2731
|
};
|
|
2654
2732
|
this.addToolExecution(execution);
|
|
2655
2733
|
this.callbacks.onToolStart?.(execution);
|
|
2656
|
-
if (!
|
|
2734
|
+
if (!tool) {
|
|
2657
2735
|
const errorResult = {
|
|
2658
2736
|
toolCallId: toolCall.id,
|
|
2659
2737
|
success: false,
|
|
@@ -2667,9 +2745,9 @@ var AbstractAgentLoop = class {
|
|
|
2667
2745
|
return errorResult;
|
|
2668
2746
|
}
|
|
2669
2747
|
let approvalData;
|
|
2670
|
-
if (
|
|
2671
|
-
const approvalTitle = typeof
|
|
2672
|
-
const approvalMessage = typeof
|
|
2748
|
+
if (tool.needsApproval && !this.config.autoApprove) {
|
|
2749
|
+
const approvalTitle = typeof tool.approvalTitle === "function" ? tool.approvalTitle(toolCall.args) : tool.approvalTitle;
|
|
2750
|
+
const approvalMessage = typeof tool.approvalMessage === "function" ? tool.approvalMessage(toolCall.args) : tool.approvalMessage;
|
|
2673
2751
|
execution.approvalStatus = "required";
|
|
2674
2752
|
execution.approvalTitle = approvalTitle;
|
|
2675
2753
|
execution.approvalMessage = approvalMessage;
|
|
@@ -2698,13 +2776,13 @@ var AbstractAgentLoop = class {
|
|
|
2698
2776
|
}
|
|
2699
2777
|
this.updateToolExecution(toolCall.id, { status: "executing" });
|
|
2700
2778
|
try {
|
|
2701
|
-
if (!
|
|
2779
|
+
if (!tool.handler) {
|
|
2702
2780
|
throw new Error(`Tool "${toolCall.name}" has no handler`);
|
|
2703
2781
|
}
|
|
2704
2782
|
if (this._isCancelled || this.abortController?.signal.aborted) {
|
|
2705
2783
|
throw new Error("Tool execution cancelled");
|
|
2706
2784
|
}
|
|
2707
|
-
const result = await
|
|
2785
|
+
const result = await tool.handler(toolCall.args, {
|
|
2708
2786
|
signal: this.abortController?.signal,
|
|
2709
2787
|
data: { toolCallId: toolCall.id },
|
|
2710
2788
|
approvalData
|
|
@@ -2927,738 +3005,747 @@ var AbstractAgentLoop = class {
|
|
|
2927
3005
|
}
|
|
2928
3006
|
};
|
|
2929
3007
|
|
|
2930
|
-
// src/chat/
|
|
2931
|
-
var
|
|
2932
|
-
constructor(
|
|
2933
|
-
|
|
2934
|
-
this.
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
3008
|
+
// src/chat/branching/MessageTree.ts
|
|
3009
|
+
var _MessageTree = class _MessageTree {
|
|
3010
|
+
constructor(messages) {
|
|
3011
|
+
/** All messages by ID */
|
|
3012
|
+
this.nodeMap = /* @__PURE__ */ new Map();
|
|
3013
|
+
/** parentKey → ordered list of child IDs (insertion order = oldest-first) */
|
|
3014
|
+
this.childrenOf = /* @__PURE__ */ new Map();
|
|
3015
|
+
/** parentKey → currently-active child ID */
|
|
3016
|
+
this.activeChildMap = /* @__PURE__ */ new Map();
|
|
3017
|
+
/** Current leaf message ID (tip of the active path) */
|
|
3018
|
+
this._currentLeafId = null;
|
|
3019
|
+
/** Cached visible messages — invalidated on every mutation */
|
|
3020
|
+
this._visibleCache = null;
|
|
3021
|
+
if (messages?.length) {
|
|
3022
|
+
this._buildFromMessages(messages);
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
// ============================================
|
|
3026
|
+
// Static Migration Helpers
|
|
3027
|
+
// ============================================
|
|
3028
|
+
/**
|
|
3029
|
+
* Convert a legacy flat array (no parentId) to a tree-linked array.
|
|
3030
|
+
*
|
|
3031
|
+
* Rules:
|
|
3032
|
+
* - Tool messages get parentId = the owning assistant message's id
|
|
3033
|
+
* (matched via toolCallId → toolCall.id), chained in sequence.
|
|
3034
|
+
* - All other messages get parentId = the previous message in the linear
|
|
3035
|
+
* chain (including tool messages, so the next non-tool after tool results
|
|
3036
|
+
* is a child of the last tool result, not a sibling).
|
|
3037
|
+
*
|
|
3038
|
+
* Returns a new array with parentId/childrenIds filled in.
|
|
3039
|
+
* Does NOT mutate the original messages.
|
|
3040
|
+
*/
|
|
3041
|
+
static fromFlatArray(messages) {
|
|
3042
|
+
if (messages.length === 0) return messages;
|
|
3043
|
+
const alreadyLinked = messages.some((m) => m.parentId !== void 0);
|
|
3044
|
+
if (alreadyLinked) return messages;
|
|
3045
|
+
const result = [];
|
|
3046
|
+
let prevId = null;
|
|
3047
|
+
const assistantById = /* @__PURE__ */ new Map();
|
|
3048
|
+
for (const msg of messages) {
|
|
3049
|
+
if (msg.role === "assistant") {
|
|
3050
|
+
assistantById.set(msg.id, msg);
|
|
2947
3051
|
}
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
threadId: config.threadId,
|
|
2958
|
-
debug: config.debug,
|
|
2959
|
-
initialMessages: config.initialMessages,
|
|
2960
|
-
state: config.state,
|
|
2961
|
-
transport: config.transport,
|
|
2962
|
-
callbacks: {
|
|
2963
|
-
onMessagesChange: callbacks.onMessagesChange,
|
|
2964
|
-
onStatusChange: callbacks.onStatusChange,
|
|
2965
|
-
onError: callbacks.onError,
|
|
2966
|
-
onMessageStart: callbacks.onMessageStart,
|
|
2967
|
-
onMessageDelta: callbacks.onMessageDelta,
|
|
2968
|
-
onMessageFinish: callbacks.onMessageFinish,
|
|
2969
|
-
onToolCalls: callbacks.onToolCalls,
|
|
2970
|
-
onFinish: callbacks.onFinish,
|
|
2971
|
-
onContextUsageChange: callbacks.onContextUsageChange,
|
|
2972
|
-
// Server-side tool callbacks - track in agentLoop for UI display
|
|
2973
|
-
// IMPORTANT: Only track tools that are NOT registered client-side
|
|
2974
|
-
// Client-side tools are tracked via executeToolCalls() path
|
|
2975
|
-
onServerToolStart: (info) => {
|
|
2976
|
-
const existingExecution = this.agentLoop.toolExecutions.find(
|
|
2977
|
-
(e) => e.id === info.id
|
|
2978
|
-
);
|
|
2979
|
-
if (existingExecution) {
|
|
2980
|
-
if (info.hidden !== void 0 && existingExecution.hidden !== info.hidden) {
|
|
2981
|
-
this.debug(
|
|
2982
|
-
"Updating hidden flag for existing execution:",
|
|
2983
|
-
info.name,
|
|
2984
|
-
info.hidden
|
|
2985
|
-
);
|
|
2986
|
-
this.agentLoop.updateToolExecution(info.id, {
|
|
2987
|
-
hidden: info.hidden
|
|
2988
|
-
});
|
|
2989
|
-
}
|
|
2990
|
-
return;
|
|
2991
|
-
}
|
|
2992
|
-
const isClientTool = this.agentLoop.tools.some(
|
|
2993
|
-
(t) => t.name === info.name && t.location === "client"
|
|
2994
|
-
);
|
|
2995
|
-
if (isClientTool) {
|
|
2996
|
-
this.debug("Skipping server tracking for client tool:", info.name);
|
|
2997
|
-
return;
|
|
2998
|
-
}
|
|
2999
|
-
this.debug("Server tool started:", info.name, {
|
|
3000
|
-
hidden: info.hidden,
|
|
3001
|
-
id: info.id
|
|
3002
|
-
});
|
|
3003
|
-
this.agentLoop.addServerToolExecution(info);
|
|
3004
|
-
},
|
|
3005
|
-
onServerToolArgs: (info) => {
|
|
3006
|
-
const isClientTool = this.agentLoop.tools.some(
|
|
3007
|
-
(t) => t.name === info.name && t.location === "client"
|
|
3008
|
-
);
|
|
3009
|
-
if (isClientTool) return;
|
|
3010
|
-
this.debug("Server tool args:", info.name, info.args);
|
|
3011
|
-
this.agentLoop.updateServerToolArgs(info.id, info.args ?? {});
|
|
3012
|
-
},
|
|
3013
|
-
onServerToolEnd: (info) => {
|
|
3014
|
-
const isClientTool = this.agentLoop.tools.some(
|
|
3015
|
-
(t) => t.name === info.name && t.location === "client"
|
|
3016
|
-
);
|
|
3017
|
-
if (isClientTool) return;
|
|
3018
|
-
this.debug("Server tool ended:", info.name, {
|
|
3019
|
-
error: info.error,
|
|
3020
|
-
hasResult: !!info.result
|
|
3021
|
-
});
|
|
3022
|
-
this.agentLoop.completeServerToolExecution(info);
|
|
3023
|
-
}
|
|
3024
|
-
}
|
|
3025
|
-
});
|
|
3026
|
-
this.wireEvents();
|
|
3027
|
-
}
|
|
3028
|
-
/**
|
|
3029
|
-
* Wire up internal events between chat and agent loop
|
|
3030
|
-
*/
|
|
3031
|
-
wireEvents() {
|
|
3032
|
-
this.debug("Wiring up toolCalls event handler");
|
|
3033
|
-
this.chat.on("toolCalls", async (event) => {
|
|
3034
|
-
const toolCalls = event.toolCalls;
|
|
3035
|
-
if (!toolCalls?.length) {
|
|
3036
|
-
return;
|
|
3037
|
-
}
|
|
3038
|
-
this.debug("Tool calls received:", toolCalls.length);
|
|
3039
|
-
const toolCallInfos = toolCalls.map((tc) => {
|
|
3040
|
-
const tcAny = tc;
|
|
3041
|
-
const name = tcAny.function?.name ?? tcAny.name ?? "";
|
|
3042
|
-
let args = {};
|
|
3043
|
-
if (tcAny.function?.arguments) {
|
|
3044
|
-
try {
|
|
3045
|
-
args = JSON.parse(tcAny.function.arguments);
|
|
3046
|
-
} catch {
|
|
3047
|
-
args = {};
|
|
3052
|
+
}
|
|
3053
|
+
const lastToolIdByAssistant = /* @__PURE__ */ new Map();
|
|
3054
|
+
for (const msg of messages) {
|
|
3055
|
+
if (msg.role === "tool" && msg.toolCallId) {
|
|
3056
|
+
let ownerAssistantId = null;
|
|
3057
|
+
for (const [, assistant] of assistantById) {
|
|
3058
|
+
if (assistant.toolCalls?.some((tc) => tc.id === msg.toolCallId)) {
|
|
3059
|
+
ownerAssistantId = assistant.id;
|
|
3060
|
+
break;
|
|
3048
3061
|
}
|
|
3049
|
-
} else if (tcAny.args) {
|
|
3050
|
-
args = tcAny.args;
|
|
3051
3062
|
}
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
result: r.success ? r.result : { success: false, error: r.error }
|
|
3061
|
-
}));
|
|
3062
|
-
await this.chat.continueWithToolResults(toolResults);
|
|
3063
|
-
} else if (this.agentLoop.maxIterationsReached && toolCallInfos.length > 0) {
|
|
3064
|
-
this.debug("Max iterations reached, adding blocked tool results");
|
|
3065
|
-
const errorMessage = this.config.maxIterationsMessage || "Tool execution paused: iteration limit reached. User can say 'continue' to resume.";
|
|
3066
|
-
const blockedResults = toolCallInfos.map((tc) => ({
|
|
3067
|
-
toolCallId: tc.id,
|
|
3068
|
-
result: {
|
|
3069
|
-
success: false,
|
|
3070
|
-
error: errorMessage
|
|
3071
|
-
}
|
|
3072
|
-
}));
|
|
3073
|
-
await this.chat.continueWithToolResults(blockedResults);
|
|
3063
|
+
const chainParent = ownerAssistantId ? lastToolIdByAssistant.get(ownerAssistantId) ?? ownerAssistantId : prevId ?? null;
|
|
3064
|
+
result.push({
|
|
3065
|
+
...msg,
|
|
3066
|
+
parentId: chainParent,
|
|
3067
|
+
childrenIds: []
|
|
3068
|
+
});
|
|
3069
|
+
if (ownerAssistantId) {
|
|
3070
|
+
lastToolIdByAssistant.set(ownerAssistantId, msg.id);
|
|
3074
3071
|
}
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3072
|
+
prevId = msg.id;
|
|
3073
|
+
} else {
|
|
3074
|
+
result.push({
|
|
3075
|
+
...msg,
|
|
3076
|
+
parentId: prevId,
|
|
3077
|
+
childrenIds: []
|
|
3078
|
+
});
|
|
3079
|
+
prevId = msg.id;
|
|
3078
3080
|
}
|
|
3079
|
-
}
|
|
3081
|
+
}
|
|
3082
|
+
const childrenMap = /* @__PURE__ */ new Map();
|
|
3083
|
+
for (const msg of result) {
|
|
3084
|
+
const parentKey = msg.parentId == null ? _MessageTree.ROOT_KEY : msg.parentId;
|
|
3085
|
+
if (!childrenMap.has(parentKey)) {
|
|
3086
|
+
childrenMap.set(parentKey, []);
|
|
3087
|
+
}
|
|
3088
|
+
childrenMap.get(parentKey).push(msg.id);
|
|
3089
|
+
}
|
|
3090
|
+
return result.map((msg) => ({
|
|
3091
|
+
...msg,
|
|
3092
|
+
childrenIds: childrenMap.get(msg.id) ?? []
|
|
3093
|
+
}));
|
|
3080
3094
|
}
|
|
3081
3095
|
// ============================================
|
|
3082
|
-
//
|
|
3096
|
+
// Core Queries
|
|
3083
3097
|
// ============================================
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3098
|
+
/**
|
|
3099
|
+
* Returns the visible path (root → current leaf) — what the UI renders
|
|
3100
|
+
* and what gets sent to the API.
|
|
3101
|
+
*
|
|
3102
|
+
* Backward-compat: if NO message has parentId set (all undefined),
|
|
3103
|
+
* falls back to insertion order (legacy linear mode).
|
|
3104
|
+
*/
|
|
3105
|
+
getVisibleMessages() {
|
|
3106
|
+
if (this._visibleCache !== null) return this._visibleCache;
|
|
3107
|
+
if (this.nodeMap.size === 0) {
|
|
3108
|
+
this._visibleCache = [];
|
|
3109
|
+
return this._visibleCache;
|
|
3110
|
+
}
|
|
3111
|
+
const hasTreeStructure = Array.from(this.nodeMap.values()).some(
|
|
3112
|
+
(m) => m.parentId !== void 0
|
|
3113
|
+
);
|
|
3114
|
+
this._visibleCache = hasTreeStructure ? this._getActivePath().map((id) => this.nodeMap.get(id)) : Array.from(this.nodeMap.values());
|
|
3115
|
+
return this._visibleCache;
|
|
3092
3116
|
}
|
|
3093
|
-
|
|
3094
|
-
|
|
3117
|
+
_invalidateCache() {
|
|
3118
|
+
this._visibleCache = null;
|
|
3095
3119
|
}
|
|
3096
3120
|
/**
|
|
3097
|
-
*
|
|
3098
|
-
* Use this to show loading indicators and disable send button
|
|
3121
|
+
* Returns ALL messages across every branch (for persistence / ThreadManager).
|
|
3099
3122
|
*/
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
const toolsBusy = this.agentLoop.isProcessing;
|
|
3103
|
-
const hasPendingApprovals = this.agentLoop.pendingApprovalExecutions.length > 0;
|
|
3104
|
-
return chatBusy || toolsBusy || hasPendingApprovals;
|
|
3123
|
+
getAllMessages() {
|
|
3124
|
+
return Array.from(this.nodeMap.values());
|
|
3105
3125
|
}
|
|
3106
3126
|
/**
|
|
3107
|
-
*
|
|
3108
|
-
*
|
|
3127
|
+
* Branch navigation info for the UI navigator.
|
|
3128
|
+
* Returns null if the message has no siblings (only child).
|
|
3109
3129
|
*/
|
|
3110
|
-
|
|
3111
|
-
const
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
get iteration() {
|
|
3125
|
-
return this.agentLoop.iteration;
|
|
3130
|
+
getBranchInfo(messageId) {
|
|
3131
|
+
const msg = this.nodeMap.get(messageId);
|
|
3132
|
+
if (!msg) return null;
|
|
3133
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3134
|
+
const siblings = this.childrenOf.get(parentKey) ?? [];
|
|
3135
|
+
if (siblings.length <= 1) return null;
|
|
3136
|
+
const siblingIndex = siblings.indexOf(messageId);
|
|
3137
|
+
return {
|
|
3138
|
+
siblingIndex,
|
|
3139
|
+
totalSiblings: siblings.length,
|
|
3140
|
+
siblingIds: [...siblings],
|
|
3141
|
+
hasPrevious: siblingIndex > 0,
|
|
3142
|
+
hasNext: siblingIndex < siblings.length - 1
|
|
3143
|
+
};
|
|
3126
3144
|
}
|
|
3127
|
-
get
|
|
3128
|
-
return this.
|
|
3145
|
+
get currentLeafId() {
|
|
3146
|
+
return this._currentLeafId;
|
|
3129
3147
|
}
|
|
3130
|
-
get
|
|
3131
|
-
|
|
3148
|
+
get hasBranches() {
|
|
3149
|
+
for (const children of this.childrenOf.values()) {
|
|
3150
|
+
if (children.length > 1) return true;
|
|
3151
|
+
}
|
|
3152
|
+
return false;
|
|
3132
3153
|
}
|
|
3133
3154
|
// ============================================
|
|
3134
|
-
//
|
|
3155
|
+
// Mutations
|
|
3135
3156
|
// ============================================
|
|
3136
3157
|
/**
|
|
3137
|
-
*
|
|
3138
|
-
*
|
|
3139
|
-
*
|
|
3140
|
-
*
|
|
3141
|
-
* same parent as this message ID
|
|
3158
|
+
* Insert a new message.
|
|
3159
|
+
* - Updates childrenOf and nodeMap.
|
|
3160
|
+
* - New branch becomes active (activeChildMap updated).
|
|
3161
|
+
* - Updates current leaf.
|
|
3142
3162
|
*/
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3163
|
+
addMessage(message) {
|
|
3164
|
+
this.nodeMap.set(message.id, message);
|
|
3165
|
+
const parentKey = this._parentKey(message.parentId);
|
|
3166
|
+
if (!this.childrenOf.has(parentKey)) {
|
|
3167
|
+
this.childrenOf.set(parentKey, []);
|
|
3147
3168
|
}
|
|
3148
|
-
this.
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
this.chat.stop();
|
|
3157
|
-
this.debug("Stopped - cancelled tools and aborted stream");
|
|
3158
|
-
}
|
|
3159
|
-
/**
|
|
3160
|
-
* Clear all messages
|
|
3161
|
-
*/
|
|
3162
|
-
clearMessages() {
|
|
3163
|
-
this.chat.clearMessages();
|
|
3164
|
-
this.agentLoop.clearToolExecutions();
|
|
3165
|
-
}
|
|
3166
|
-
/**
|
|
3167
|
-
* Set messages directly
|
|
3168
|
-
*/
|
|
3169
|
-
setMessages(messages) {
|
|
3170
|
-
this.chat.setMessages(messages);
|
|
3171
|
-
}
|
|
3172
|
-
/**
|
|
3173
|
-
* Regenerate last response
|
|
3174
|
-
*/
|
|
3175
|
-
async regenerate(messageId) {
|
|
3176
|
-
await this.chat.regenerate(messageId);
|
|
3177
|
-
}
|
|
3178
|
-
/**
|
|
3179
|
-
* Set tools available for the LLM
|
|
3180
|
-
*/
|
|
3181
|
-
setTools(tools) {
|
|
3182
|
-
this.chat.setTools(tools);
|
|
3183
|
-
}
|
|
3184
|
-
/**
|
|
3185
|
-
* Update prompt/tool optimization controls.
|
|
3186
|
-
*/
|
|
3187
|
-
setOptimizationConfig(config) {
|
|
3188
|
-
this.config.optimization = config;
|
|
3189
|
-
this.chat.setOptimizationConfig(config);
|
|
3190
|
-
}
|
|
3191
|
-
/**
|
|
3192
|
-
* Set the active tool profile used for request-time tool selection.
|
|
3193
|
-
*/
|
|
3194
|
-
setToolProfile(profile) {
|
|
3195
|
-
this.chat.setToolProfile(profile);
|
|
3196
|
-
}
|
|
3197
|
-
/**
|
|
3198
|
-
* Get the most recent prompt context usage snapshot.
|
|
3199
|
-
*/
|
|
3200
|
-
getContextUsage() {
|
|
3201
|
-
return this.chat.getContextUsage();
|
|
3202
|
-
}
|
|
3203
|
-
/**
|
|
3204
|
-
* Set dynamic context (from useAIContext hook)
|
|
3205
|
-
*/
|
|
3206
|
-
setContext(context) {
|
|
3207
|
-
this.chat.setContext(context);
|
|
3208
|
-
}
|
|
3209
|
-
/**
|
|
3210
|
-
* Set system prompt dynamically
|
|
3211
|
-
*/
|
|
3212
|
-
setSystemPrompt(prompt) {
|
|
3213
|
-
this.chat.setSystemPrompt(prompt);
|
|
3214
|
-
}
|
|
3215
|
-
/**
|
|
3216
|
-
* Set headers configuration
|
|
3217
|
-
* Can be static headers or a getter function for dynamic resolution
|
|
3218
|
-
*/
|
|
3219
|
-
setHeaders(headers) {
|
|
3220
|
-
this.chat.setHeaders(headers);
|
|
3221
|
-
}
|
|
3222
|
-
/**
|
|
3223
|
-
* Set URL configuration
|
|
3224
|
-
* Can be static URL or a getter function for dynamic resolution
|
|
3225
|
-
*/
|
|
3226
|
-
setUrl(url) {
|
|
3227
|
-
this.chat.setUrl(url);
|
|
3228
|
-
}
|
|
3229
|
-
/**
|
|
3230
|
-
* Set body configuration
|
|
3231
|
-
* Additional properties merged into every request body
|
|
3232
|
-
*/
|
|
3233
|
-
setBody(body) {
|
|
3234
|
-
this.chat.setBody(body);
|
|
3235
|
-
}
|
|
3236
|
-
setRequestMessageTransform(fn) {
|
|
3237
|
-
this.chat.setRequestMessageTransform(fn);
|
|
3238
|
-
}
|
|
3239
|
-
/**
|
|
3240
|
-
* Set inline skills (forwarded to underlying chat instance)
|
|
3241
|
-
*/
|
|
3242
|
-
setInlineSkills(skills) {
|
|
3243
|
-
this.chat.setInlineSkills(skills);
|
|
3244
|
-
}
|
|
3245
|
-
// ============================================
|
|
3246
|
-
// Tool Registration
|
|
3247
|
-
// ============================================
|
|
3248
|
-
/**
|
|
3249
|
-
* Register a tool
|
|
3250
|
-
*/
|
|
3251
|
-
registerTool(tool2) {
|
|
3252
|
-
this.agentLoop.registerTool(tool2);
|
|
3253
|
-
this.chat.setTools(this.agentLoop.tools);
|
|
3169
|
+
const siblings = this.childrenOf.get(parentKey);
|
|
3170
|
+
if (!siblings.includes(message.id)) {
|
|
3171
|
+
siblings.push(message.id);
|
|
3172
|
+
}
|
|
3173
|
+
this.activeChildMap.set(parentKey, message.id);
|
|
3174
|
+
this._currentLeafId = this._walkToLeaf(message.id);
|
|
3175
|
+
this._invalidateCache();
|
|
3176
|
+
return message;
|
|
3254
3177
|
}
|
|
3255
3178
|
/**
|
|
3256
|
-
*
|
|
3179
|
+
* Navigate: make messageId the active child at its parent fork,
|
|
3180
|
+
* then walk to its leaf and update currentLeafId.
|
|
3257
3181
|
*/
|
|
3258
|
-
|
|
3259
|
-
this.
|
|
3260
|
-
|
|
3182
|
+
switchBranch(messageId) {
|
|
3183
|
+
const msg = this.nodeMap.get(messageId);
|
|
3184
|
+
if (!msg) return;
|
|
3185
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3186
|
+
this.activeChildMap.set(parentKey, messageId);
|
|
3187
|
+
this._currentLeafId = this._walkToLeaf(messageId);
|
|
3188
|
+
this._invalidateCache();
|
|
3261
3189
|
}
|
|
3262
|
-
// ============================================
|
|
3263
|
-
// Tool Approval
|
|
3264
|
-
// ============================================
|
|
3265
3190
|
/**
|
|
3266
|
-
*
|
|
3191
|
+
* Update message content in-place (streaming updates).
|
|
3192
|
+
* No tree structure change.
|
|
3267
3193
|
*/
|
|
3268
|
-
|
|
3269
|
-
this.
|
|
3194
|
+
updateMessage(id, updater) {
|
|
3195
|
+
const existing = this.nodeMap.get(id);
|
|
3196
|
+
if (!existing) return false;
|
|
3197
|
+
this.nodeMap.set(id, updater(existing));
|
|
3198
|
+
this._invalidateCache();
|
|
3199
|
+
return true;
|
|
3270
3200
|
}
|
|
3271
3201
|
/**
|
|
3272
|
-
*
|
|
3202
|
+
* Set current leaf explicitly.
|
|
3203
|
+
* Used by regenerate() to rewind the active path before pushing a new message.
|
|
3273
3204
|
*/
|
|
3274
|
-
|
|
3275
|
-
this.
|
|
3205
|
+
setCurrentLeaf(leafId) {
|
|
3206
|
+
this._currentLeafId = leafId;
|
|
3207
|
+
if (leafId === null) return;
|
|
3208
|
+
const msg = this.nodeMap.get(leafId);
|
|
3209
|
+
if (!msg) return;
|
|
3210
|
+
let current = msg;
|
|
3211
|
+
while (current) {
|
|
3212
|
+
const parentKey = this._parentKey(current.parentId);
|
|
3213
|
+
this.activeChildMap.set(parentKey, current.id);
|
|
3214
|
+
if (current.parentId == null || current.parentId === void 0) break;
|
|
3215
|
+
current = this.nodeMap.get(current.parentId);
|
|
3216
|
+
}
|
|
3217
|
+
this._invalidateCache();
|
|
3276
3218
|
}
|
|
3277
3219
|
/**
|
|
3278
|
-
*
|
|
3220
|
+
* Rebuild entire tree from a message array.
|
|
3221
|
+
* Used by setMessages().
|
|
3279
3222
|
*/
|
|
3280
|
-
|
|
3281
|
-
this.
|
|
3223
|
+
reset(messages) {
|
|
3224
|
+
this.nodeMap.clear();
|
|
3225
|
+
this.childrenOf.clear();
|
|
3226
|
+
this.activeChildMap.clear();
|
|
3227
|
+
this._currentLeafId = null;
|
|
3228
|
+
this._invalidateCache();
|
|
3229
|
+
if (messages.length > 0) {
|
|
3230
|
+
this._buildFromMessages(messages);
|
|
3231
|
+
}
|
|
3282
3232
|
}
|
|
3283
3233
|
// ============================================
|
|
3284
|
-
//
|
|
3234
|
+
// Private Helpers
|
|
3285
3235
|
// ============================================
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3236
|
+
_buildFromMessages(messages) {
|
|
3237
|
+
const linked = messages.some((m) => m.parentId !== void 0) ? messages : _MessageTree.fromFlatArray(messages);
|
|
3238
|
+
for (const msg of linked) {
|
|
3239
|
+
this.nodeMap.set(msg.id, msg);
|
|
3240
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3241
|
+
if (!this.childrenOf.has(parentKey)) {
|
|
3242
|
+
this.childrenOf.set(parentKey, []);
|
|
3243
|
+
}
|
|
3244
|
+
const siblings = this.childrenOf.get(parentKey);
|
|
3245
|
+
if (!siblings.includes(msg.id)) {
|
|
3246
|
+
siblings.push(msg.id);
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
for (const [parentKey, children] of this.childrenOf) {
|
|
3250
|
+
if (children.length > 0) {
|
|
3251
|
+
this.activeChildMap.set(parentKey, children[children.length - 1]);
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
const path = this._getActivePath();
|
|
3255
|
+
this._currentLeafId = path.length > 0 ? path[path.length - 1] : null;
|
|
3291
3256
|
}
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
*/
|
|
3298
|
-
get disposed() {
|
|
3299
|
-
return this.chat.disposed;
|
|
3257
|
+
_parentKey(parentId) {
|
|
3258
|
+
if (parentId == null || parentId === void 0) {
|
|
3259
|
+
return _MessageTree.ROOT_KEY;
|
|
3260
|
+
}
|
|
3261
|
+
return parentId;
|
|
3300
3262
|
}
|
|
3301
3263
|
/**
|
|
3302
|
-
*
|
|
3303
|
-
* This allows reusing an instance after dispose() was called,
|
|
3304
|
-
* preserving registered tools and state
|
|
3264
|
+
* Walk forward from a message along active children to find the leaf.
|
|
3305
3265
|
*/
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3266
|
+
_walkToLeaf(fromId) {
|
|
3267
|
+
let current = fromId;
|
|
3268
|
+
while (true) {
|
|
3269
|
+
const children = this.childrenOf.get(current);
|
|
3270
|
+
if (!children || children.length === 0) break;
|
|
3271
|
+
const activeChild = this.activeChildMap.get(current);
|
|
3272
|
+
if (!activeChild) break;
|
|
3273
|
+
if (!this.nodeMap.has(activeChild)) break;
|
|
3274
|
+
current = activeChild;
|
|
3275
|
+
}
|
|
3276
|
+
return current;
|
|
3309
3277
|
}
|
|
3310
3278
|
/**
|
|
3311
|
-
*
|
|
3279
|
+
* Walk the active path from root to the current leaf.
|
|
3312
3280
|
*/
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3281
|
+
_getActivePath() {
|
|
3282
|
+
const path = [];
|
|
3283
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3284
|
+
const rootChildren = this.childrenOf.get(_MessageTree.ROOT_KEY) ?? [];
|
|
3285
|
+
if (rootChildren.length === 0) return path;
|
|
3286
|
+
let activeId = this.activeChildMap.get(_MessageTree.ROOT_KEY);
|
|
3287
|
+
if (!activeId) {
|
|
3288
|
+
activeId = rootChildren[rootChildren.length - 1];
|
|
3289
|
+
}
|
|
3290
|
+
let current = activeId;
|
|
3291
|
+
while (current && !visited.has(current)) {
|
|
3292
|
+
if (!this.nodeMap.has(current)) break;
|
|
3293
|
+
visited.add(current);
|
|
3294
|
+
path.push(current);
|
|
3295
|
+
const activeChild = this.activeChildMap.get(current);
|
|
3296
|
+
if (!activeChild || !this.nodeMap.has(activeChild)) break;
|
|
3297
|
+
current = activeChild;
|
|
3298
|
+
}
|
|
3299
|
+
return path;
|
|
3325
3300
|
}
|
|
3326
3301
|
};
|
|
3302
|
+
/** Sentinel key used for root-level messages (parentId === null) */
|
|
3303
|
+
_MessageTree.ROOT_KEY = "__root__";
|
|
3304
|
+
var MessageTree = _MessageTree;
|
|
3327
3305
|
|
|
3328
|
-
// src/chat/
|
|
3329
|
-
var
|
|
3330
|
-
constructor(
|
|
3331
|
-
|
|
3332
|
-
this.
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3306
|
+
// src/chat/ChatWithTools.ts
|
|
3307
|
+
var ChatWithTools = class {
|
|
3308
|
+
constructor(config, callbacks = {}) {
|
|
3309
|
+
this.config = config;
|
|
3310
|
+
this.callbacks = callbacks;
|
|
3311
|
+
this.agentLoop = new AbstractAgentLoop(
|
|
3312
|
+
{
|
|
3313
|
+
maxIterations: config.maxIterations ?? 20,
|
|
3314
|
+
tools: config.tools
|
|
3315
|
+
},
|
|
3316
|
+
{
|
|
3317
|
+
onExecutionsChange: (executions) => {
|
|
3318
|
+
callbacks.onToolExecutionsChange?.(executions);
|
|
3319
|
+
},
|
|
3320
|
+
onApprovalRequired: (execution) => {
|
|
3321
|
+
callbacks.onApprovalRequired?.(execution);
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
);
|
|
3325
|
+
this.chat = new AbstractChat({
|
|
3326
|
+
runtimeUrl: config.runtimeUrl,
|
|
3327
|
+
llm: config.llm,
|
|
3328
|
+
systemPrompt: config.systemPrompt,
|
|
3329
|
+
streaming: config.streaming,
|
|
3330
|
+
headers: config.headers,
|
|
3331
|
+
body: config.body,
|
|
3332
|
+
optimization: config.optimization,
|
|
3333
|
+
threadId: config.threadId,
|
|
3334
|
+
debug: config.debug,
|
|
3335
|
+
initialMessages: config.initialMessages,
|
|
3336
|
+
state: config.state,
|
|
3337
|
+
transport: config.transport,
|
|
3338
|
+
callbacks: {
|
|
3339
|
+
onMessagesChange: callbacks.onMessagesChange,
|
|
3340
|
+
onStatusChange: callbacks.onStatusChange,
|
|
3341
|
+
onError: callbacks.onError,
|
|
3342
|
+
onMessageStart: callbacks.onMessageStart,
|
|
3343
|
+
onMessageDelta: callbacks.onMessageDelta,
|
|
3344
|
+
onMessageFinish: callbacks.onMessageFinish,
|
|
3345
|
+
onToolCalls: callbacks.onToolCalls,
|
|
3346
|
+
onFinish: callbacks.onFinish,
|
|
3347
|
+
onContextUsageChange: callbacks.onContextUsageChange,
|
|
3348
|
+
// Server-side tool callbacks - track in agentLoop for UI display
|
|
3349
|
+
// IMPORTANT: Only track tools that are NOT registered client-side
|
|
3350
|
+
// Client-side tools are tracked via executeToolCalls() path
|
|
3351
|
+
onServerToolStart: (info) => {
|
|
3352
|
+
const existingExecution = this.agentLoop.toolExecutions.find(
|
|
3353
|
+
(e) => e.id === info.id
|
|
3354
|
+
);
|
|
3355
|
+
if (existingExecution) {
|
|
3356
|
+
if (info.hidden !== void 0 && existingExecution.hidden !== info.hidden) {
|
|
3357
|
+
this.debug(
|
|
3358
|
+
"Updating hidden flag for existing execution:",
|
|
3359
|
+
info.name,
|
|
3360
|
+
info.hidden
|
|
3361
|
+
);
|
|
3362
|
+
this.agentLoop.updateToolExecution(info.id, {
|
|
3363
|
+
hidden: info.hidden
|
|
3364
|
+
});
|
|
3365
|
+
}
|
|
3366
|
+
return;
|
|
3367
|
+
}
|
|
3368
|
+
const isClientTool = this.agentLoop.tools.some(
|
|
3369
|
+
(t) => t.name === info.name && t.location === "client"
|
|
3370
|
+
);
|
|
3371
|
+
if (isClientTool) {
|
|
3372
|
+
this.debug("Skipping server tracking for client tool:", info.name);
|
|
3373
|
+
return;
|
|
3374
|
+
}
|
|
3375
|
+
this.debug("Server tool started:", info.name, {
|
|
3376
|
+
hidden: info.hidden,
|
|
3377
|
+
id: info.id
|
|
3378
|
+
});
|
|
3379
|
+
this.agentLoop.addServerToolExecution(info);
|
|
3380
|
+
},
|
|
3381
|
+
onServerToolArgs: (info) => {
|
|
3382
|
+
const isClientTool = this.agentLoop.tools.some(
|
|
3383
|
+
(t) => t.name === info.name && t.location === "client"
|
|
3384
|
+
);
|
|
3385
|
+
if (isClientTool) return;
|
|
3386
|
+
this.debug("Server tool args:", info.name, info.args);
|
|
3387
|
+
this.agentLoop.updateServerToolArgs(info.id, info.args ?? {});
|
|
3388
|
+
},
|
|
3389
|
+
onServerToolEnd: (info) => {
|
|
3390
|
+
const isClientTool = this.agentLoop.tools.some(
|
|
3391
|
+
(t) => t.name === info.name && t.location === "client"
|
|
3392
|
+
);
|
|
3393
|
+
if (isClientTool) return;
|
|
3394
|
+
this.debug("Server tool ended:", info.name, {
|
|
3395
|
+
error: info.error,
|
|
3396
|
+
hasResult: !!info.result
|
|
3397
|
+
});
|
|
3398
|
+
this.agentLoop.completeServerToolExecution(info);
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
});
|
|
3402
|
+
this.wireEvents();
|
|
3344
3403
|
}
|
|
3345
|
-
// ============================================
|
|
3346
|
-
// Static Migration Helpers
|
|
3347
|
-
// ============================================
|
|
3348
3404
|
/**
|
|
3349
|
-
*
|
|
3350
|
-
*
|
|
3351
|
-
* Rules:
|
|
3352
|
-
* - Tool messages get parentId = the owning assistant message's id
|
|
3353
|
-
* (matched via toolCallId → toolCall.id).
|
|
3354
|
-
* - All other messages get parentId of the previous non-tool message
|
|
3355
|
-
* (or null for the first message).
|
|
3356
|
-
*
|
|
3357
|
-
* Returns a new array with parentId/childrenIds filled in.
|
|
3358
|
-
* Does NOT mutate the original messages.
|
|
3405
|
+
* Wire up internal events between chat and agent loop
|
|
3359
3406
|
*/
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
const assistantById = /* @__PURE__ */ new Map();
|
|
3367
|
-
for (const msg of messages) {
|
|
3368
|
-
if (msg.role === "assistant") {
|
|
3369
|
-
assistantById.set(msg.id, msg);
|
|
3407
|
+
wireEvents() {
|
|
3408
|
+
this.debug("Wiring up toolCalls event handler");
|
|
3409
|
+
this.chat.on("toolCalls", async (event) => {
|
|
3410
|
+
const toolCalls = event.toolCalls;
|
|
3411
|
+
if (!toolCalls?.length) {
|
|
3412
|
+
return;
|
|
3370
3413
|
}
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3414
|
+
this.debug("Tool calls received:", toolCalls.length);
|
|
3415
|
+
const toolCallInfos = toolCalls.map((tc) => {
|
|
3416
|
+
const tcAny = tc;
|
|
3417
|
+
const name = tcAny.function?.name ?? tcAny.name ?? "";
|
|
3418
|
+
let args = {};
|
|
3419
|
+
if (tcAny.function?.arguments) {
|
|
3420
|
+
try {
|
|
3421
|
+
args = JSON.parse(tcAny.function.arguments);
|
|
3422
|
+
} catch {
|
|
3423
|
+
args = {};
|
|
3379
3424
|
}
|
|
3425
|
+
} else if (tcAny.args) {
|
|
3426
|
+
args = tcAny.args;
|
|
3380
3427
|
}
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3428
|
+
return { id: tc.id, name, args };
|
|
3429
|
+
});
|
|
3430
|
+
try {
|
|
3431
|
+
const results = await this.agentLoop.executeToolCalls(toolCallInfos);
|
|
3432
|
+
this.debug("Tool results:", results);
|
|
3433
|
+
if (results.length > 0) {
|
|
3434
|
+
const toolResults = results.map((r) => ({
|
|
3435
|
+
toolCallId: r.toolCallId,
|
|
3436
|
+
result: r.success ? r.result : { success: false, error: r.error }
|
|
3437
|
+
}));
|
|
3438
|
+
await this.chat.continueWithToolResults(toolResults);
|
|
3439
|
+
} else if (this.agentLoop.maxIterationsReached && toolCallInfos.length > 0) {
|
|
3440
|
+
this.debug(
|
|
3441
|
+
"Max iterations reached, stopping loop without new LLM request"
|
|
3442
|
+
);
|
|
3443
|
+
const errorMessage = this.config.maxIterationsMessage || "Tool execution paused: iteration limit reached. User can say 'continue' to resume.";
|
|
3444
|
+
const blockedResults = toolCallInfos.map((tc) => ({
|
|
3445
|
+
toolCallId: tc.id,
|
|
3446
|
+
result: {
|
|
3447
|
+
success: false,
|
|
3448
|
+
error: errorMessage
|
|
3449
|
+
}
|
|
3450
|
+
}));
|
|
3451
|
+
await this.chat.addToolResultMessages(blockedResults, errorMessage);
|
|
3452
|
+
}
|
|
3453
|
+
} catch (error) {
|
|
3454
|
+
this.debug("Error executing tools:", error);
|
|
3455
|
+
console.error("[ChatWithTools] Tool execution error:", error);
|
|
3400
3456
|
}
|
|
3401
|
-
|
|
3402
|
-
}
|
|
3403
|
-
return result.map((msg) => ({
|
|
3404
|
-
...msg,
|
|
3405
|
-
childrenIds: childrenMap.get(msg.id) ?? []
|
|
3406
|
-
}));
|
|
3457
|
+
});
|
|
3407
3458
|
}
|
|
3408
3459
|
// ============================================
|
|
3409
|
-
//
|
|
3460
|
+
// Chat Getters
|
|
3410
3461
|
// ============================================
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
* and what gets sent to the API.
|
|
3414
|
-
*
|
|
3415
|
-
* Backward-compat: if NO message has parentId set (all undefined),
|
|
3416
|
-
* falls back to insertion order (legacy linear mode).
|
|
3417
|
-
*/
|
|
3418
|
-
getVisibleMessages() {
|
|
3419
|
-
if (this._visibleCache !== null) return this._visibleCache;
|
|
3420
|
-
if (this.nodeMap.size === 0) {
|
|
3421
|
-
this._visibleCache = [];
|
|
3422
|
-
return this._visibleCache;
|
|
3423
|
-
}
|
|
3424
|
-
const hasTreeStructure = Array.from(this.nodeMap.values()).some(
|
|
3425
|
-
(m) => m.parentId !== void 0
|
|
3426
|
-
);
|
|
3427
|
-
this._visibleCache = hasTreeStructure ? this._getActivePath().map((id) => this.nodeMap.get(id)) : Array.from(this.nodeMap.values());
|
|
3428
|
-
return this._visibleCache;
|
|
3462
|
+
get messages() {
|
|
3463
|
+
return this.chat.messages;
|
|
3429
3464
|
}
|
|
3430
|
-
|
|
3431
|
-
this.
|
|
3465
|
+
get status() {
|
|
3466
|
+
return this.chat.status;
|
|
3467
|
+
}
|
|
3468
|
+
get error() {
|
|
3469
|
+
return this.chat.error;
|
|
3470
|
+
}
|
|
3471
|
+
get isStreaming() {
|
|
3472
|
+
return this.chat.isStreaming;
|
|
3432
3473
|
}
|
|
3433
3474
|
/**
|
|
3434
|
-
*
|
|
3475
|
+
* Whether any operation is in progress (chat or tools)
|
|
3476
|
+
* Use this to show loading indicators and disable send button
|
|
3435
3477
|
*/
|
|
3436
|
-
|
|
3437
|
-
|
|
3478
|
+
get isLoading() {
|
|
3479
|
+
const chatBusy = this.status === "submitted" || this.status === "streaming";
|
|
3480
|
+
const toolsBusy = this.agentLoop.isProcessing;
|
|
3481
|
+
const hasPendingApprovals = this.agentLoop.pendingApprovalExecutions.length > 0;
|
|
3482
|
+
return chatBusy || toolsBusy || hasPendingApprovals;
|
|
3438
3483
|
}
|
|
3439
3484
|
/**
|
|
3440
|
-
*
|
|
3441
|
-
*
|
|
3485
|
+
* Check if a request is currently in progress (excludes pending approvals)
|
|
3486
|
+
* Use this to prevent sending new messages
|
|
3442
3487
|
*/
|
|
3443
|
-
|
|
3444
|
-
const
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
const siblings = this.childrenOf.get(parentKey) ?? [];
|
|
3448
|
-
if (siblings.length <= 1) return null;
|
|
3449
|
-
const siblingIndex = siblings.indexOf(messageId);
|
|
3450
|
-
return {
|
|
3451
|
-
siblingIndex,
|
|
3452
|
-
totalSiblings: siblings.length,
|
|
3453
|
-
siblingIds: [...siblings],
|
|
3454
|
-
hasPrevious: siblingIndex > 0,
|
|
3455
|
-
hasNext: siblingIndex < siblings.length - 1
|
|
3456
|
-
};
|
|
3488
|
+
get isBusy() {
|
|
3489
|
+
const chatBusy = this.status === "submitted" || this.status === "streaming";
|
|
3490
|
+
const toolsBusy = this.agentLoop.isProcessing;
|
|
3491
|
+
return chatBusy || toolsBusy;
|
|
3457
3492
|
}
|
|
3458
|
-
|
|
3459
|
-
|
|
3493
|
+
// ============================================
|
|
3494
|
+
// Tool Execution Getters
|
|
3495
|
+
// ============================================
|
|
3496
|
+
get toolExecutions() {
|
|
3497
|
+
return this.agentLoop.toolExecutions;
|
|
3460
3498
|
}
|
|
3461
|
-
get
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
return
|
|
3499
|
+
get tools() {
|
|
3500
|
+
return this.agentLoop.tools;
|
|
3501
|
+
}
|
|
3502
|
+
get iteration() {
|
|
3503
|
+
return this.agentLoop.iteration;
|
|
3504
|
+
}
|
|
3505
|
+
get maxIterations() {
|
|
3506
|
+
return this.agentLoop.maxIterations;
|
|
3507
|
+
}
|
|
3508
|
+
get isProcessing() {
|
|
3509
|
+
return this.agentLoop.isProcessing;
|
|
3466
3510
|
}
|
|
3467
3511
|
// ============================================
|
|
3468
|
-
//
|
|
3512
|
+
// Chat Actions
|
|
3469
3513
|
// ============================================
|
|
3470
3514
|
/**
|
|
3471
|
-
*
|
|
3472
|
-
*
|
|
3473
|
-
*
|
|
3474
|
-
* -
|
|
3515
|
+
* Send a message
|
|
3516
|
+
* Returns false if a request is already in progress
|
|
3517
|
+
*
|
|
3518
|
+
* @param options.editMessageId - Edit flow: new message branches from the
|
|
3519
|
+
* same parent as this message ID
|
|
3475
3520
|
*/
|
|
3476
|
-
|
|
3477
|
-
this.
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
this.childrenOf.set(parentKey, []);
|
|
3481
|
-
}
|
|
3482
|
-
const siblings = this.childrenOf.get(parentKey);
|
|
3483
|
-
if (!siblings.includes(message.id)) {
|
|
3484
|
-
siblings.push(message.id);
|
|
3521
|
+
async sendMessage(content, attachments, options) {
|
|
3522
|
+
if (this.isLoading) {
|
|
3523
|
+
this.debug("sendMessage blocked - request already in progress");
|
|
3524
|
+
return false;
|
|
3485
3525
|
}
|
|
3486
|
-
this.
|
|
3487
|
-
|
|
3488
|
-
this._invalidateCache();
|
|
3489
|
-
return message;
|
|
3526
|
+
this.agentLoop.resetIterations();
|
|
3527
|
+
return await this.chat.sendMessage(content, attachments, options);
|
|
3490
3528
|
}
|
|
3491
3529
|
/**
|
|
3492
|
-
*
|
|
3493
|
-
* then walk to its leaf and update currentLeafId.
|
|
3530
|
+
* Stop generation and cancel any running tools
|
|
3494
3531
|
*/
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
this.activeChildMap.set(parentKey, messageId);
|
|
3500
|
-
this._currentLeafId = this._walkToLeaf(messageId);
|
|
3501
|
-
this._invalidateCache();
|
|
3532
|
+
stop() {
|
|
3533
|
+
this.agentLoop.cancel();
|
|
3534
|
+
this.chat.stop();
|
|
3535
|
+
this.debug("Stopped - cancelled tools and aborted stream");
|
|
3502
3536
|
}
|
|
3503
3537
|
/**
|
|
3504
|
-
*
|
|
3505
|
-
* No tree structure change.
|
|
3538
|
+
* Clear all messages
|
|
3506
3539
|
*/
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
this.nodeMap.set(id, updater(existing));
|
|
3511
|
-
this._invalidateCache();
|
|
3512
|
-
return true;
|
|
3540
|
+
clearMessages() {
|
|
3541
|
+
this.chat.clearMessages();
|
|
3542
|
+
this.agentLoop.clearToolExecutions();
|
|
3513
3543
|
}
|
|
3514
3544
|
/**
|
|
3515
|
-
* Set
|
|
3516
|
-
* Used by regenerate() to rewind the active path before pushing a new message.
|
|
3545
|
+
* Set messages directly
|
|
3517
3546
|
*/
|
|
3518
|
-
|
|
3519
|
-
this.
|
|
3520
|
-
if (leafId === null) return;
|
|
3521
|
-
const msg = this.nodeMap.get(leafId);
|
|
3522
|
-
if (!msg) return;
|
|
3523
|
-
let current = msg;
|
|
3524
|
-
while (current) {
|
|
3525
|
-
const parentKey = this._parentKey(current.parentId);
|
|
3526
|
-
this.activeChildMap.set(parentKey, current.id);
|
|
3527
|
-
if (current.parentId == null || current.parentId === void 0) break;
|
|
3528
|
-
current = this.nodeMap.get(current.parentId);
|
|
3529
|
-
}
|
|
3530
|
-
this._invalidateCache();
|
|
3547
|
+
setMessages(messages) {
|
|
3548
|
+
this.chat.setMessages(messages);
|
|
3531
3549
|
}
|
|
3532
3550
|
/**
|
|
3533
|
-
*
|
|
3534
|
-
* Used by setMessages().
|
|
3551
|
+
* Regenerate last response
|
|
3535
3552
|
*/
|
|
3536
|
-
|
|
3537
|
-
this.
|
|
3538
|
-
this.childrenOf.clear();
|
|
3539
|
-
this.activeChildMap.clear();
|
|
3540
|
-
this._currentLeafId = null;
|
|
3541
|
-
this._invalidateCache();
|
|
3542
|
-
if (messages.length > 0) {
|
|
3543
|
-
this._buildFromMessages(messages);
|
|
3544
|
-
}
|
|
3553
|
+
async regenerate(messageId) {
|
|
3554
|
+
await this.chat.regenerate(messageId);
|
|
3545
3555
|
}
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
for (const msg of linked) {
|
|
3552
|
-
this.nodeMap.set(msg.id, msg);
|
|
3553
|
-
const parentKey = this._parentKey(msg.parentId);
|
|
3554
|
-
if (!this.childrenOf.has(parentKey)) {
|
|
3555
|
-
this.childrenOf.set(parentKey, []);
|
|
3556
|
-
}
|
|
3557
|
-
const siblings = this.childrenOf.get(parentKey);
|
|
3558
|
-
if (!siblings.includes(msg.id)) {
|
|
3559
|
-
siblings.push(msg.id);
|
|
3560
|
-
}
|
|
3561
|
-
}
|
|
3562
|
-
for (const [parentKey, children] of this.childrenOf) {
|
|
3563
|
-
if (children.length > 0) {
|
|
3564
|
-
this.activeChildMap.set(parentKey, children[children.length - 1]);
|
|
3565
|
-
}
|
|
3566
|
-
}
|
|
3567
|
-
const path = this._getActivePath();
|
|
3568
|
-
this._currentLeafId = path.length > 0 ? path[path.length - 1] : null;
|
|
3556
|
+
/**
|
|
3557
|
+
* Set tools available for the LLM
|
|
3558
|
+
*/
|
|
3559
|
+
setTools(tools) {
|
|
3560
|
+
this.chat.setTools(tools);
|
|
3569
3561
|
}
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3562
|
+
/**
|
|
3563
|
+
* Update prompt/tool optimization controls.
|
|
3564
|
+
*/
|
|
3565
|
+
setOptimizationConfig(config) {
|
|
3566
|
+
this.config.optimization = config;
|
|
3567
|
+
this.chat.setOptimizationConfig(config);
|
|
3575
3568
|
}
|
|
3576
3569
|
/**
|
|
3577
|
-
*
|
|
3570
|
+
* Set the active tool profile used for request-time tool selection.
|
|
3578
3571
|
*/
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
while (true) {
|
|
3582
|
-
const children = this.childrenOf.get(current);
|
|
3583
|
-
if (!children || children.length === 0) break;
|
|
3584
|
-
const activeChild = this.activeChildMap.get(current);
|
|
3585
|
-
if (!activeChild) break;
|
|
3586
|
-
if (!this.nodeMap.has(activeChild)) break;
|
|
3587
|
-
current = activeChild;
|
|
3588
|
-
}
|
|
3589
|
-
return current;
|
|
3572
|
+
setToolProfile(profile) {
|
|
3573
|
+
this.chat.setToolProfile(profile);
|
|
3590
3574
|
}
|
|
3591
3575
|
/**
|
|
3592
|
-
*
|
|
3576
|
+
* Get the most recent prompt context usage snapshot.
|
|
3593
3577
|
*/
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3597
|
-
const rootChildren = this.childrenOf.get(_MessageTree.ROOT_KEY) ?? [];
|
|
3598
|
-
if (rootChildren.length === 0) return path;
|
|
3599
|
-
let activeId = this.activeChildMap.get(_MessageTree.ROOT_KEY);
|
|
3600
|
-
if (!activeId) {
|
|
3601
|
-
activeId = rootChildren[rootChildren.length - 1];
|
|
3602
|
-
}
|
|
3603
|
-
let current = activeId;
|
|
3604
|
-
while (current && !visited.has(current)) {
|
|
3605
|
-
if (!this.nodeMap.has(current)) break;
|
|
3606
|
-
visited.add(current);
|
|
3607
|
-
path.push(current);
|
|
3608
|
-
const activeChild = this.activeChildMap.get(current);
|
|
3609
|
-
if (!activeChild || !this.nodeMap.has(activeChild)) break;
|
|
3610
|
-
current = activeChild;
|
|
3611
|
-
}
|
|
3612
|
-
return path;
|
|
3578
|
+
getContextUsage() {
|
|
3579
|
+
return this.chat.getContextUsage();
|
|
3613
3580
|
}
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
// src/react/internal/ReactChatState.ts
|
|
3620
|
-
var ReactChatState = class {
|
|
3621
|
-
constructor(initialMessages) {
|
|
3622
|
-
this._status = "ready";
|
|
3623
|
-
this._error = void 0;
|
|
3624
|
-
// Callbacks for React subscriptions (useSyncExternalStore)
|
|
3625
|
-
this.subscribers = /* @__PURE__ */ new Set();
|
|
3626
|
-
// ============================================
|
|
3627
|
-
// Subscription (for useSyncExternalStore)
|
|
3628
|
-
// ============================================
|
|
3629
|
-
/**
|
|
3630
|
-
* Subscribe to state changes.
|
|
3631
|
-
* Returns an unsubscribe function.
|
|
3632
|
-
*
|
|
3633
|
-
* @example
|
|
3634
|
-
* ```tsx
|
|
3635
|
-
* const messages = useSyncExternalStore(
|
|
3636
|
-
* state.subscribe,
|
|
3637
|
-
* () => state.messages
|
|
3638
|
-
* );
|
|
3639
|
-
* ```
|
|
3640
|
-
*/
|
|
3641
|
-
this.subscribe = (callback) => {
|
|
3642
|
-
this.subscribers.add(callback);
|
|
3643
|
-
return () => {
|
|
3644
|
-
this.subscribers.delete(callback);
|
|
3645
|
-
};
|
|
3646
|
-
};
|
|
3647
|
-
this.tree = new MessageTree(initialMessages);
|
|
3581
|
+
/**
|
|
3582
|
+
* Set dynamic context (from useAIContext hook)
|
|
3583
|
+
*/
|
|
3584
|
+
setContext(context) {
|
|
3585
|
+
this.chat.setContext(context);
|
|
3648
3586
|
}
|
|
3649
|
-
// ============================================
|
|
3650
|
-
// Getters — visible path only
|
|
3651
|
-
// ============================================
|
|
3652
3587
|
/**
|
|
3653
|
-
*
|
|
3654
|
-
* and what gets sent to the API.
|
|
3655
|
-
*
|
|
3656
|
-
* For all messages across all branches, use getAllMessages().
|
|
3588
|
+
* Set system prompt dynamically
|
|
3657
3589
|
*/
|
|
3658
|
-
|
|
3659
|
-
|
|
3590
|
+
setSystemPrompt(prompt) {
|
|
3591
|
+
this.chat.setSystemPrompt(prompt);
|
|
3660
3592
|
}
|
|
3661
|
-
|
|
3593
|
+
/**
|
|
3594
|
+
* Set headers configuration
|
|
3595
|
+
* Can be static headers or a getter function for dynamic resolution
|
|
3596
|
+
*/
|
|
3597
|
+
setHeaders(headers) {
|
|
3598
|
+
this.chat.setHeaders(headers);
|
|
3599
|
+
}
|
|
3600
|
+
/**
|
|
3601
|
+
* Set URL configuration
|
|
3602
|
+
* Can be static URL or a getter function for dynamic resolution
|
|
3603
|
+
*/
|
|
3604
|
+
setUrl(url) {
|
|
3605
|
+
this.chat.setUrl(url);
|
|
3606
|
+
}
|
|
3607
|
+
/**
|
|
3608
|
+
* Set body configuration
|
|
3609
|
+
* Additional properties merged into every request body
|
|
3610
|
+
*/
|
|
3611
|
+
setBody(body) {
|
|
3612
|
+
this.chat.setBody(body);
|
|
3613
|
+
}
|
|
3614
|
+
setRequestMessageTransform(fn) {
|
|
3615
|
+
this.chat.setRequestMessageTransform(fn);
|
|
3616
|
+
}
|
|
3617
|
+
/**
|
|
3618
|
+
* Set inline skills (forwarded to underlying chat instance)
|
|
3619
|
+
*/
|
|
3620
|
+
setInlineSkills(skills) {
|
|
3621
|
+
this.chat.setInlineSkills(skills);
|
|
3622
|
+
}
|
|
3623
|
+
// ============================================
|
|
3624
|
+
// Tool Registration
|
|
3625
|
+
// ============================================
|
|
3626
|
+
/**
|
|
3627
|
+
* Register a tool
|
|
3628
|
+
*/
|
|
3629
|
+
registerTool(tool) {
|
|
3630
|
+
this.agentLoop.registerTool(tool);
|
|
3631
|
+
this.chat.setTools(this.agentLoop.tools);
|
|
3632
|
+
}
|
|
3633
|
+
/**
|
|
3634
|
+
* Unregister a tool
|
|
3635
|
+
*/
|
|
3636
|
+
unregisterTool(name) {
|
|
3637
|
+
this.agentLoop.unregisterTool(name);
|
|
3638
|
+
this.chat.setTools(this.agentLoop.tools);
|
|
3639
|
+
}
|
|
3640
|
+
// ============================================
|
|
3641
|
+
// Tool Approval
|
|
3642
|
+
// ============================================
|
|
3643
|
+
/**
|
|
3644
|
+
* Approve a tool execution with optional extra data
|
|
3645
|
+
*/
|
|
3646
|
+
approveToolExecution(id, extraData, permissionLevel) {
|
|
3647
|
+
this.agentLoop.approveToolExecution(id, extraData, permissionLevel);
|
|
3648
|
+
}
|
|
3649
|
+
/**
|
|
3650
|
+
* Reject a tool execution
|
|
3651
|
+
*/
|
|
3652
|
+
rejectToolExecution(id, reason, permissionLevel) {
|
|
3653
|
+
this.agentLoop.rejectToolExecution(id, reason, permissionLevel);
|
|
3654
|
+
}
|
|
3655
|
+
/**
|
|
3656
|
+
* Clear tool executions
|
|
3657
|
+
*/
|
|
3658
|
+
clearToolExecutions() {
|
|
3659
|
+
this.agentLoop.clearToolExecutions();
|
|
3660
|
+
}
|
|
3661
|
+
// ============================================
|
|
3662
|
+
// Event Subscriptions (for framework adapters)
|
|
3663
|
+
// ============================================
|
|
3664
|
+
/**
|
|
3665
|
+
* Subscribe to chat events
|
|
3666
|
+
*/
|
|
3667
|
+
on(event, handler) {
|
|
3668
|
+
return this.chat.on(event, handler);
|
|
3669
|
+
}
|
|
3670
|
+
// ============================================
|
|
3671
|
+
// Cleanup
|
|
3672
|
+
// ============================================
|
|
3673
|
+
/**
|
|
3674
|
+
* Whether this instance has been disposed
|
|
3675
|
+
*/
|
|
3676
|
+
get disposed() {
|
|
3677
|
+
return this.chat.disposed;
|
|
3678
|
+
}
|
|
3679
|
+
/**
|
|
3680
|
+
* Revive a disposed instance (for React StrictMode compatibility)
|
|
3681
|
+
* This allows reusing an instance after dispose() was called,
|
|
3682
|
+
* preserving registered tools and state
|
|
3683
|
+
*/
|
|
3684
|
+
revive() {
|
|
3685
|
+
this.chat.revive();
|
|
3686
|
+
this.agentLoop.revive();
|
|
3687
|
+
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Dispose and cleanup
|
|
3690
|
+
*/
|
|
3691
|
+
dispose() {
|
|
3692
|
+
this.chat.dispose();
|
|
3693
|
+
this.agentLoop.dispose();
|
|
3694
|
+
}
|
|
3695
|
+
// ============================================
|
|
3696
|
+
// Private
|
|
3697
|
+
// ============================================
|
|
3698
|
+
debug(message, ...args) {
|
|
3699
|
+
createLogger("tools", () => this.config.debug ?? false)(
|
|
3700
|
+
message,
|
|
3701
|
+
args.length === 1 ? args[0] : args.length > 1 ? args : void 0
|
|
3702
|
+
);
|
|
3703
|
+
}
|
|
3704
|
+
};
|
|
3705
|
+
|
|
3706
|
+
// src/react/internal/ReactChatState.ts
|
|
3707
|
+
var ReactChatState = class {
|
|
3708
|
+
constructor(initialMessages) {
|
|
3709
|
+
this._status = "ready";
|
|
3710
|
+
this._error = void 0;
|
|
3711
|
+
// Callbacks for React subscriptions (useSyncExternalStore)
|
|
3712
|
+
this.subscribers = /* @__PURE__ */ new Set();
|
|
3713
|
+
// ============================================
|
|
3714
|
+
// Subscription (for useSyncExternalStore)
|
|
3715
|
+
// ============================================
|
|
3716
|
+
/**
|
|
3717
|
+
* Subscribe to state changes.
|
|
3718
|
+
* Returns an unsubscribe function.
|
|
3719
|
+
*
|
|
3720
|
+
* @example
|
|
3721
|
+
* ```tsx
|
|
3722
|
+
* const messages = useSyncExternalStore(
|
|
3723
|
+
* state.subscribe,
|
|
3724
|
+
* () => state.messages
|
|
3725
|
+
* );
|
|
3726
|
+
* ```
|
|
3727
|
+
*/
|
|
3728
|
+
this.subscribe = (callback) => {
|
|
3729
|
+
this.subscribers.add(callback);
|
|
3730
|
+
return () => {
|
|
3731
|
+
this.subscribers.delete(callback);
|
|
3732
|
+
};
|
|
3733
|
+
};
|
|
3734
|
+
this.tree = new MessageTree(initialMessages);
|
|
3735
|
+
}
|
|
3736
|
+
// ============================================
|
|
3737
|
+
// Getters — visible path only
|
|
3738
|
+
// ============================================
|
|
3739
|
+
/**
|
|
3740
|
+
* Returns the VISIBLE PATH (active branch) — what the UI renders
|
|
3741
|
+
* and what gets sent to the API.
|
|
3742
|
+
*
|
|
3743
|
+
* For all messages across all branches, use getAllMessages().
|
|
3744
|
+
*/
|
|
3745
|
+
get messages() {
|
|
3746
|
+
return this.tree.getVisibleMessages();
|
|
3747
|
+
}
|
|
3748
|
+
get status() {
|
|
3662
3749
|
return this._status;
|
|
3663
3750
|
}
|
|
3664
3751
|
get error() {
|
|
@@ -3782,149 +3869,21 @@ var ReactChatState = class {
|
|
|
3782
3869
|
function createReactChatState(initialMessages) {
|
|
3783
3870
|
return new ReactChatState(initialMessages);
|
|
3784
3871
|
}
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
// Branching API — pass-throughs to ReactChatState
|
|
3801
|
-
// ============================================
|
|
3802
|
-
/**
|
|
3803
|
-
* Navigate to a sibling branch.
|
|
3804
|
-
*/
|
|
3805
|
-
switchBranch(messageId) {
|
|
3806
|
-
this.reactState.switchBranch(messageId);
|
|
3807
|
-
}
|
|
3808
|
-
/**
|
|
3809
|
-
* Get branch navigation info for a message.
|
|
3810
|
-
*/
|
|
3811
|
-
getBranchInfo(messageId) {
|
|
3812
|
-
return this.reactState.getBranchInfo(messageId);
|
|
3813
|
-
}
|
|
3814
|
-
/**
|
|
3815
|
-
* Get all messages across all branches (for persistence).
|
|
3816
|
-
*/
|
|
3817
|
-
getAllMessages() {
|
|
3818
|
-
return this.reactState.getAllMessages();
|
|
3819
|
-
}
|
|
3820
|
-
/**
|
|
3821
|
-
* Whether any message has siblings (branching has occurred).
|
|
3822
|
-
*/
|
|
3823
|
-
get hasBranches() {
|
|
3824
|
-
return this.reactState.hasBranches;
|
|
3825
|
-
}
|
|
3826
|
-
/**
|
|
3827
|
-
* Dispose and cleanup
|
|
3828
|
-
*/
|
|
3829
|
-
dispose() {
|
|
3830
|
-
super.dispose();
|
|
3831
|
-
this.reactState.dispose();
|
|
3832
|
-
}
|
|
3833
|
-
/**
|
|
3834
|
-
* Revive a disposed instance (for React StrictMode compatibility)
|
|
3835
|
-
*/
|
|
3836
|
-
revive() {
|
|
3837
|
-
super.revive();
|
|
3838
|
-
this.reactState.revive();
|
|
3839
|
-
}
|
|
3840
|
-
};
|
|
3841
|
-
|
|
3842
|
-
// src/react/utils/context-tree.ts
|
|
3843
|
-
function addNode(tree, node, parentId) {
|
|
3844
|
-
const newNode = {
|
|
3845
|
-
...node,
|
|
3846
|
-
children: node.children || []
|
|
3847
|
-
};
|
|
3848
|
-
if (!parentId) {
|
|
3849
|
-
return [...tree, newNode];
|
|
3850
|
-
}
|
|
3851
|
-
return tree.map((n) => {
|
|
3852
|
-
if (n.id === parentId) {
|
|
3853
|
-
return { ...n, children: [...n.children, newNode] };
|
|
3854
|
-
}
|
|
3855
|
-
if (n.children.length > 0) {
|
|
3856
|
-
return { ...n, children: addNode(n.children, node, parentId) };
|
|
3857
|
-
}
|
|
3858
|
-
return n;
|
|
3859
|
-
});
|
|
3860
|
-
}
|
|
3861
|
-
function removeNode(tree, id) {
|
|
3862
|
-
return tree.reduce((result, node) => {
|
|
3863
|
-
if (node.id !== id) {
|
|
3864
|
-
const newNode = { ...node, children: removeNode(node.children, id) };
|
|
3865
|
-
result.push(newNode);
|
|
3866
|
-
}
|
|
3867
|
-
return result;
|
|
3868
|
-
}, []);
|
|
3869
|
-
}
|
|
3870
|
-
function getIndentPrefix(index, level) {
|
|
3871
|
-
if (level === 0) {
|
|
3872
|
-
return `${index + 1}.`;
|
|
3873
|
-
} else if (level === 1) {
|
|
3874
|
-
return `${String.fromCharCode(65 + index)}.`;
|
|
3875
|
-
} else if (level === 2) {
|
|
3876
|
-
return `${String.fromCharCode(97 + index)}.`;
|
|
3877
|
-
} else {
|
|
3878
|
-
return "-";
|
|
3879
|
-
}
|
|
3880
|
-
}
|
|
3881
|
-
function printNode(node, prefix = "", indentLevel = 0) {
|
|
3882
|
-
const indent = " ".repeat(indentLevel);
|
|
3883
|
-
const prefixLength = prefix.length + indent.length;
|
|
3884
|
-
const subsequentIndent = " ".repeat(prefixLength);
|
|
3885
|
-
const lines = node.value.split("\n");
|
|
3886
|
-
const firstLine = `${indent}${prefix}${lines[0]}`;
|
|
3887
|
-
const subsequentLines = lines.slice(1).map((line) => `${subsequentIndent}${line}`).join("\n");
|
|
3888
|
-
let output = `${firstLine}
|
|
3889
|
-
`;
|
|
3890
|
-
if (subsequentLines) {
|
|
3891
|
-
output += `${subsequentLines}
|
|
3892
|
-
`;
|
|
3893
|
-
}
|
|
3894
|
-
node.children.forEach((child, index) => {
|
|
3895
|
-
const childPrefix = `${" ".repeat(prefix.length)}${getIndentPrefix(index, indentLevel + 1)} `;
|
|
3896
|
-
output += printNode(child, childPrefix, indentLevel + 1);
|
|
3897
|
-
});
|
|
3898
|
-
return output;
|
|
3899
|
-
}
|
|
3900
|
-
function printTree(tree) {
|
|
3901
|
-
if (tree.length === 0) {
|
|
3902
|
-
return "";
|
|
3903
|
-
}
|
|
3904
|
-
let output = "";
|
|
3905
|
-
tree.forEach((node, index) => {
|
|
3906
|
-
if (index > 0) {
|
|
3907
|
-
output += "\n";
|
|
3908
|
-
}
|
|
3909
|
-
output += printNode(node, `${getIndentPrefix(index, 0)} `);
|
|
3910
|
-
});
|
|
3911
|
-
return output.trim();
|
|
3912
|
-
}
|
|
3913
|
-
function useMCPClient(config) {
|
|
3914
|
-
const {
|
|
3915
|
-
autoConnect = true,
|
|
3916
|
-
onConnectionStateChange,
|
|
3917
|
-
onToolsChange,
|
|
3918
|
-
onElicitationRequest,
|
|
3919
|
-
onError,
|
|
3920
|
-
onNotification,
|
|
3921
|
-
...clientConfig
|
|
3922
|
-
} = config;
|
|
3923
|
-
const clientRef = useRef(null);
|
|
3924
|
-
const mountedRef = useRef(true);
|
|
3925
|
-
const [state, setState] = useState({
|
|
3926
|
-
connectionState: "disconnected",
|
|
3927
|
-
tools: []
|
|
3872
|
+
function useMCPClient(config) {
|
|
3873
|
+
const {
|
|
3874
|
+
autoConnect = true,
|
|
3875
|
+
onConnectionStateChange,
|
|
3876
|
+
onToolsChange,
|
|
3877
|
+
onElicitationRequest,
|
|
3878
|
+
onError,
|
|
3879
|
+
onNotification,
|
|
3880
|
+
...clientConfig
|
|
3881
|
+
} = config;
|
|
3882
|
+
const clientRef = useRef(null);
|
|
3883
|
+
const mountedRef = useRef(true);
|
|
3884
|
+
const [state, setState] = useState({
|
|
3885
|
+
connectionState: "disconnected",
|
|
3886
|
+
tools: []
|
|
3928
3887
|
});
|
|
3929
3888
|
const getClient = useCallback(() => {
|
|
3930
3889
|
if (!clientRef.current) {
|
|
@@ -4057,78 +4016,132 @@ function useMCPClient(config) {
|
|
|
4057
4016
|
};
|
|
4058
4017
|
}
|
|
4059
4018
|
|
|
4060
|
-
// src/react/
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4019
|
+
// src/react/internal/ReactChatWithTools.ts
|
|
4020
|
+
var ReactChatWithTools = class extends ChatWithTools {
|
|
4021
|
+
constructor(config, callbacks = {}) {
|
|
4022
|
+
const reactState = new ReactChatState(config.initialMessages);
|
|
4023
|
+
super({ ...config, state: reactState }, callbacks);
|
|
4024
|
+
/**
|
|
4025
|
+
* Subscribe to state changes (for useSyncExternalStore)
|
|
4026
|
+
*/
|
|
4027
|
+
this.subscribe = (callback) => {
|
|
4028
|
+
return this.reactState.subscribe(callback);
|
|
4029
|
+
};
|
|
4030
|
+
this.reactState = reactState;
|
|
4031
|
+
}
|
|
4032
|
+
// ============================================
|
|
4033
|
+
// Branching API — pass-throughs to ReactChatState
|
|
4034
|
+
// ============================================
|
|
4035
|
+
/**
|
|
4036
|
+
* Navigate to a sibling branch.
|
|
4037
|
+
*/
|
|
4038
|
+
switchBranch(messageId) {
|
|
4039
|
+
this.reactState.switchBranch(messageId);
|
|
4040
|
+
}
|
|
4041
|
+
/**
|
|
4042
|
+
* Get branch navigation info for a message.
|
|
4043
|
+
*/
|
|
4044
|
+
getBranchInfo(messageId) {
|
|
4045
|
+
return this.reactState.getBranchInfo(messageId);
|
|
4046
|
+
}
|
|
4047
|
+
/**
|
|
4048
|
+
* Get all messages across all branches (for persistence).
|
|
4049
|
+
*/
|
|
4050
|
+
getAllMessages() {
|
|
4051
|
+
return this.reactState.getAllMessages();
|
|
4052
|
+
}
|
|
4053
|
+
/**
|
|
4054
|
+
* Whether any message has siblings (branching has occurred).
|
|
4055
|
+
*/
|
|
4056
|
+
get hasBranches() {
|
|
4057
|
+
return this.reactState.hasBranches;
|
|
4058
|
+
}
|
|
4059
|
+
/**
|
|
4060
|
+
* Dispose and cleanup
|
|
4061
|
+
*/
|
|
4062
|
+
dispose() {
|
|
4063
|
+
super.dispose();
|
|
4064
|
+
this.reactState.dispose();
|
|
4065
|
+
}
|
|
4066
|
+
/**
|
|
4067
|
+
* Revive a disposed instance (for React StrictMode compatibility)
|
|
4068
|
+
*/
|
|
4069
|
+
revive() {
|
|
4070
|
+
super.revive();
|
|
4071
|
+
this.reactState.revive();
|
|
4072
|
+
}
|
|
4073
|
+
};
|
|
4074
|
+
|
|
4075
|
+
// src/react/utils/context-tree.ts
|
|
4076
|
+
function addNode(tree, node, parentId) {
|
|
4077
|
+
const newNode = {
|
|
4078
|
+
...node,
|
|
4079
|
+
children: node.children || []
|
|
4080
|
+
};
|
|
4081
|
+
if (!parentId) {
|
|
4082
|
+
return [...tree, newNode];
|
|
4083
|
+
}
|
|
4084
|
+
return tree.map((n) => {
|
|
4085
|
+
if (n.id === parentId) {
|
|
4086
|
+
return { ...n, children: [...n.children, newNode] };
|
|
4079
4087
|
}
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
prefix: prefixToolNames,
|
|
4083
|
-
asServerTool: true,
|
|
4084
|
-
// MCP tools execute remotely
|
|
4085
|
-
callTool: mcpClient.callTool,
|
|
4086
|
-
hidden,
|
|
4087
|
-
// Hide from chat UI
|
|
4088
|
-
source
|
|
4089
|
-
// Tool source for UI differentiation
|
|
4090
|
-
})
|
|
4091
|
-
);
|
|
4092
|
-
}, [
|
|
4093
|
-
mcpClient.isConnected,
|
|
4094
|
-
mcpClient.state.tools,
|
|
4095
|
-
mcpClient.callTool,
|
|
4096
|
-
toolAdapter,
|
|
4097
|
-
prefixToolNames,
|
|
4098
|
-
hidden,
|
|
4099
|
-
source
|
|
4100
|
-
]);
|
|
4101
|
-
useEffect(() => {
|
|
4102
|
-
if (!autoRegister) {
|
|
4103
|
-
return;
|
|
4088
|
+
if (n.children.length > 0) {
|
|
4089
|
+
return { ...n, children: addNode(n.children, node, parentId) };
|
|
4104
4090
|
}
|
|
4105
|
-
|
|
4106
|
-
|
|
4091
|
+
return n;
|
|
4092
|
+
});
|
|
4093
|
+
}
|
|
4094
|
+
function removeNode(tree, id) {
|
|
4095
|
+
return tree.reduce((result, node) => {
|
|
4096
|
+
if (node.id !== id) {
|
|
4097
|
+
const newNode = { ...node, children: removeNode(node.children, id) };
|
|
4098
|
+
result.push(newNode);
|
|
4107
4099
|
}
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4100
|
+
return result;
|
|
4101
|
+
}, []);
|
|
4102
|
+
}
|
|
4103
|
+
function getIndentPrefix(index, level) {
|
|
4104
|
+
if (level === 0) {
|
|
4105
|
+
return `${index + 1}.`;
|
|
4106
|
+
} else if (level === 1) {
|
|
4107
|
+
return `${String.fromCharCode(65 + index)}.`;
|
|
4108
|
+
} else if (level === 2) {
|
|
4109
|
+
return `${String.fromCharCode(97 + index)}.`;
|
|
4110
|
+
} else {
|
|
4111
|
+
return "-";
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
function printNode(node, prefix = "", indentLevel = 0) {
|
|
4115
|
+
const indent = " ".repeat(indentLevel);
|
|
4116
|
+
const prefixLength = prefix.length + indent.length;
|
|
4117
|
+
const subsequentIndent = " ".repeat(prefixLength);
|
|
4118
|
+
const lines = node.value.split("\n");
|
|
4119
|
+
const firstLine = `${indent}${prefix}${lines[0]}`;
|
|
4120
|
+
const subsequentLines = lines.slice(1).map((line) => `${subsequentIndent}${line}`).join("\n");
|
|
4121
|
+
let output = `${firstLine}
|
|
4122
|
+
`;
|
|
4123
|
+
if (subsequentLines) {
|
|
4124
|
+
output += `${subsequentLines}
|
|
4125
|
+
`;
|
|
4126
|
+
}
|
|
4127
|
+
node.children.forEach((child, index) => {
|
|
4128
|
+
const childPrefix = `${" ".repeat(prefix.length)}${getIndentPrefix(index, indentLevel + 1)} `;
|
|
4129
|
+
output += printNode(child, childPrefix, indentLevel + 1);
|
|
4130
|
+
});
|
|
4131
|
+
return output;
|
|
4132
|
+
}
|
|
4133
|
+
function printTree(tree) {
|
|
4134
|
+
if (tree.length === 0) {
|
|
4135
|
+
return "";
|
|
4136
|
+
}
|
|
4137
|
+
let output = "";
|
|
4138
|
+
tree.forEach((node, index) => {
|
|
4139
|
+
if (index > 0) {
|
|
4140
|
+
output += "\n";
|
|
4114
4141
|
}
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
}
|
|
4119
|
-
registeredToolsRef.current = [];
|
|
4120
|
-
};
|
|
4121
|
-
}, [
|
|
4122
|
-
autoRegister,
|
|
4123
|
-
mcpClient.isConnected,
|
|
4124
|
-
toolDefinitions,
|
|
4125
|
-
registerTool,
|
|
4126
|
-
unregisterTool
|
|
4127
|
-
]);
|
|
4128
|
-
return {
|
|
4129
|
-
...mcpClient,
|
|
4130
|
-
toolDefinitions
|
|
4131
|
-
};
|
|
4142
|
+
output += printNode(node, `${getIndentPrefix(index, 0)} `);
|
|
4143
|
+
});
|
|
4144
|
+
return output.trim();
|
|
4132
4145
|
}
|
|
4133
4146
|
var defaultTokenUsage = {
|
|
4134
4147
|
current: 0,
|
|
@@ -4837,7 +4850,7 @@ function useTool(config, dependencies = []) {
|
|
|
4837
4850
|
return config.inputSchema;
|
|
4838
4851
|
}, [config.inputSchema]);
|
|
4839
4852
|
useEffect(() => {
|
|
4840
|
-
const
|
|
4853
|
+
const tool = {
|
|
4841
4854
|
name: config.name,
|
|
4842
4855
|
description: config.description,
|
|
4843
4856
|
location: "client",
|
|
@@ -4863,7 +4876,7 @@ function useTool(config, dependencies = []) {
|
|
|
4863
4876
|
aiResponseMode: config.aiResponseMode,
|
|
4864
4877
|
aiContext: config.aiContext
|
|
4865
4878
|
};
|
|
4866
|
-
registerTool(
|
|
4879
|
+
registerTool(tool);
|
|
4867
4880
|
return () => {
|
|
4868
4881
|
unregisterTool(config.name);
|
|
4869
4882
|
};
|
|
@@ -5157,6 +5170,35 @@ ${cs.rollingSummary}`,
|
|
|
5157
5170
|
}, []);
|
|
5158
5171
|
return null;
|
|
5159
5172
|
}
|
|
5173
|
+
var _MessageMetaStore = class _MessageMetaStore {
|
|
5174
|
+
constructor() {
|
|
5175
|
+
this.store = /* @__PURE__ */ new Map();
|
|
5176
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
5177
|
+
this.subscribe = (cb) => {
|
|
5178
|
+
this.listeners.add(cb);
|
|
5179
|
+
return () => this.listeners.delete(cb);
|
|
5180
|
+
};
|
|
5181
|
+
this.getSnapshot = () => this.store;
|
|
5182
|
+
this.getMeta = (messageId) => this.store.get(messageId) ?? _MessageMetaStore.EMPTY;
|
|
5183
|
+
this.setMeta = (messageId, meta) => {
|
|
5184
|
+
this.store.set(messageId, meta);
|
|
5185
|
+
this.listeners.forEach((cb) => cb());
|
|
5186
|
+
};
|
|
5187
|
+
this.updateMeta = (messageId, updater) => {
|
|
5188
|
+
const prev = this.store.get(messageId) ?? {};
|
|
5189
|
+
this.store.set(messageId, updater(prev));
|
|
5190
|
+
this.listeners.forEach((cb) => cb());
|
|
5191
|
+
};
|
|
5192
|
+
this.clear = () => {
|
|
5193
|
+
this.store.clear();
|
|
5194
|
+
this.listeners.forEach((cb) => cb());
|
|
5195
|
+
};
|
|
5196
|
+
}
|
|
5197
|
+
};
|
|
5198
|
+
// Stable empty object — returned for unknown messageIds so useSyncExternalStore
|
|
5199
|
+
// sees the same reference and doesn't trigger infinite re-renders.
|
|
5200
|
+
_MessageMetaStore.EMPTY = {};
|
|
5201
|
+
var MessageMetaStore = _MessageMetaStore;
|
|
5160
5202
|
var CopilotContext = createContext(null);
|
|
5161
5203
|
function useCopilot() {
|
|
5162
5204
|
const context = useContext(CopilotContext);
|
|
@@ -5185,6 +5227,15 @@ function CopilotProvider({
|
|
|
5185
5227
|
messageHistory,
|
|
5186
5228
|
skills
|
|
5187
5229
|
}) {
|
|
5230
|
+
const streamListenersRef = useRef(/* @__PURE__ */ new Set());
|
|
5231
|
+
const subscribeToStreamEvents = useCallback(
|
|
5232
|
+
(handler) => {
|
|
5233
|
+
streamListenersRef.current.add(handler);
|
|
5234
|
+
return () => streamListenersRef.current.delete(handler);
|
|
5235
|
+
},
|
|
5236
|
+
[]
|
|
5237
|
+
);
|
|
5238
|
+
const messageMetaStoreRef = useRef(new MessageMetaStore());
|
|
5188
5239
|
const debugLog = useCallback(
|
|
5189
5240
|
(action, data) => {
|
|
5190
5241
|
createLogger("provider", () => debug ?? false)(action, data);
|
|
@@ -5199,6 +5250,7 @@ function CopilotProvider({
|
|
|
5199
5250
|
}
|
|
5200
5251
|
}, [toolsConfig]);
|
|
5201
5252
|
const [toolExecutions, setToolExecutions] = useState([]);
|
|
5253
|
+
const [agentIteration, setAgentIteration] = useState(0);
|
|
5202
5254
|
const chatRef = useRef(null);
|
|
5203
5255
|
if (chatRef.current !== null && chatRef.current.disposed) {
|
|
5204
5256
|
chatRef.current.revive();
|
|
@@ -5213,7 +5265,9 @@ function CopilotProvider({
|
|
|
5213
5265
|
createdAt: m.created_at ?? /* @__PURE__ */ new Date(),
|
|
5214
5266
|
attachments: m.metadata?.attachments,
|
|
5215
5267
|
toolCalls: m.tool_calls,
|
|
5216
|
-
toolCallId: m.tool_call_id
|
|
5268
|
+
toolCallId: m.tool_call_id,
|
|
5269
|
+
parentId: m.parent_id,
|
|
5270
|
+
childrenIds: m.children_ids
|
|
5217
5271
|
})
|
|
5218
5272
|
);
|
|
5219
5273
|
chatRef.current = new ReactChatWithTools(
|
|
@@ -5234,6 +5288,7 @@ function CopilotProvider({
|
|
|
5234
5288
|
onToolExecutionsChange: (executions) => {
|
|
5235
5289
|
debugLog("Tool executions changed:", executions.length);
|
|
5236
5290
|
setToolExecutions(executions);
|
|
5291
|
+
setAgentIteration(chatRef.current?.iteration ?? 0);
|
|
5237
5292
|
},
|
|
5238
5293
|
onApprovalRequired: (execution) => {
|
|
5239
5294
|
debugLog("Tool approval required:", execution.name);
|
|
@@ -5243,6 +5298,13 @@ function CopilotProvider({
|
|
|
5243
5298
|
},
|
|
5244
5299
|
onError: (error2) => {
|
|
5245
5300
|
if (error2) onError?.(error2);
|
|
5301
|
+
},
|
|
5302
|
+
onStreamChunk: (chunk) => {
|
|
5303
|
+
if (streamListenersRef.current.size > 0) {
|
|
5304
|
+
for (const handler of streamListenersRef.current) {
|
|
5305
|
+
handler(chunk);
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5246
5308
|
}
|
|
5247
5309
|
}
|
|
5248
5310
|
);
|
|
@@ -5296,8 +5358,8 @@ function CopilotProvider({
|
|
|
5296
5358
|
);
|
|
5297
5359
|
const error = errorFromChat ?? null;
|
|
5298
5360
|
const isLoading = status === "streaming" || status === "submitted";
|
|
5299
|
-
const registerTool = useCallback((
|
|
5300
|
-
chatRef.current?.registerTool(
|
|
5361
|
+
const registerTool = useCallback((tool) => {
|
|
5362
|
+
chatRef.current?.registerTool(tool);
|
|
5301
5363
|
}, []);
|
|
5302
5364
|
const unregisterTool = useCallback((name) => {
|
|
5303
5365
|
chatRef.current?.unregisterTool(name);
|
|
@@ -5354,2005 +5416,41 @@ function CopilotProvider({
|
|
|
5354
5416
|
chatRef.current?.setContext(contextString);
|
|
5355
5417
|
setContextChars(contextString.length);
|
|
5356
5418
|
debugLog("Context added:", id);
|
|
5357
|
-
return id;
|
|
5358
|
-
},
|
|
5359
|
-
[debugLog]
|
|
5360
|
-
);
|
|
5361
|
-
const removeContext = useCallback(
|
|
5362
|
-
(id) => {
|
|
5363
|
-
contextTreeRef.current = removeNode(contextTreeRef.current, id);
|
|
5364
|
-
const contextString = printTree(contextTreeRef.current);
|
|
5365
|
-
chatRef.current?.setContext(contextString);
|
|
5366
|
-
setContextChars(contextString.length);
|
|
5367
|
-
debugLog("Context removed:", id);
|
|
5368
|
-
},
|
|
5369
|
-
[debugLog]
|
|
5370
|
-
);
|
|
5371
|
-
const setSystemPrompt = useCallback(
|
|
5372
|
-
(prompt) => {
|
|
5373
|
-
chatRef.current?.setSystemPrompt(prompt);
|
|
5374
|
-
debugLog("System prompt updated via function");
|
|
5375
|
-
},
|
|
5376
|
-
[debugLog]
|
|
5377
|
-
);
|
|
5378
|
-
const setInlineSkills = useCallback(
|
|
5379
|
-
(skills2) => {
|
|
5380
|
-
chatRef.current?.setInlineSkills(skills2);
|
|
5381
|
-
debugLog("Inline skills updated", { count: skills2.length });
|
|
5382
|
-
},
|
|
5383
|
-
[debugLog]
|
|
5384
|
-
);
|
|
5385
|
-
const sendMessage = useCallback(
|
|
5386
|
-
async (content, attachments) => {
|
|
5387
|
-
debugLog("Sending message:", content);
|
|
5388
|
-
await chatRef.current?.sendMessage(content, attachments);
|
|
5389
|
-
},
|
|
5390
|
-
[debugLog]
|
|
5391
|
-
);
|
|
5392
|
-
const stop = useCallback(() => {
|
|
5393
|
-
chatRef.current?.stop();
|
|
5394
|
-
}, []);
|
|
5395
|
-
const clearMessages = useCallback(() => {
|
|
5396
|
-
chatRef.current?.clearMessages();
|
|
5397
|
-
}, []);
|
|
5398
|
-
const setMessages = useCallback((messages2) => {
|
|
5399
|
-
chatRef.current?.setMessages(messages2);
|
|
5400
|
-
}, []);
|
|
5401
|
-
const regenerate = useCallback(async (messageId) => {
|
|
5402
|
-
await chatRef.current?.regenerate(messageId);
|
|
5403
|
-
}, []);
|
|
5404
|
-
const switchBranch = useCallback((messageId) => {
|
|
5405
|
-
chatRef.current?.switchBranch(messageId);
|
|
5406
|
-
}, []);
|
|
5407
|
-
const getBranchInfo = useCallback(
|
|
5408
|
-
(messageId) => chatRef.current?.getBranchInfo(messageId) ?? null,
|
|
5409
|
-
[]
|
|
5410
|
-
);
|
|
5411
|
-
const editMessage = useCallback(
|
|
5412
|
-
async (messageId, newContent) => {
|
|
5413
|
-
await chatRef.current?.sendMessage(newContent, void 0, {
|
|
5414
|
-
editMessageId: messageId
|
|
5415
|
-
});
|
|
5416
|
-
},
|
|
5417
|
-
[]
|
|
5418
|
-
);
|
|
5419
|
-
const getHasBranchesSnapshot = useCallback(
|
|
5420
|
-
() => chatRef.current.hasBranches,
|
|
5421
|
-
[]
|
|
5422
|
-
);
|
|
5423
|
-
const hasBranches = useSyncExternalStore(
|
|
5424
|
-
chatRef.current.subscribe,
|
|
5425
|
-
getHasBranchesSnapshot,
|
|
5426
|
-
() => false
|
|
5427
|
-
);
|
|
5428
|
-
const getAllMessages = useCallback(
|
|
5429
|
-
() => chatRef.current?.getAllMessages?.() ?? [],
|
|
5430
|
-
[]
|
|
5431
|
-
);
|
|
5432
|
-
useEffect(() => {
|
|
5433
|
-
if (onMessagesChange && messages.length > 0) {
|
|
5434
|
-
const allUIMessages = chatRef.current?.getAllMessages?.() ?? messages;
|
|
5435
|
-
const coreMessages = allUIMessages.map((m) => ({
|
|
5436
|
-
id: m.id,
|
|
5437
|
-
role: m.role,
|
|
5438
|
-
content: m.content,
|
|
5439
|
-
created_at: m.createdAt,
|
|
5440
|
-
tool_calls: m.toolCalls,
|
|
5441
|
-
tool_call_id: m.toolCallId,
|
|
5442
|
-
parent_id: m.parentId,
|
|
5443
|
-
children_ids: m.childrenIds,
|
|
5444
|
-
metadata: {
|
|
5445
|
-
attachments: m.attachments,
|
|
5446
|
-
thinking: m.thinking
|
|
5447
|
-
}
|
|
5448
|
-
}));
|
|
5449
|
-
onMessagesChange(coreMessages);
|
|
5450
|
-
}
|
|
5451
|
-
}, [messages, onMessagesChange]);
|
|
5452
|
-
useEffect(() => {
|
|
5453
|
-
if (error && onError) {
|
|
5454
|
-
onError(error);
|
|
5455
|
-
}
|
|
5456
|
-
}, [error, onError]);
|
|
5457
|
-
useEffect(() => {
|
|
5458
|
-
return () => {
|
|
5459
|
-
chatRef.current?.dispose();
|
|
5460
|
-
};
|
|
5461
|
-
}, []);
|
|
5462
|
-
const contextValue = useMemo(
|
|
5463
|
-
() => ({
|
|
5464
|
-
// Chat state
|
|
5465
|
-
messages,
|
|
5466
|
-
status,
|
|
5467
|
-
error,
|
|
5468
|
-
isLoading,
|
|
5469
|
-
// Chat actions
|
|
5470
|
-
sendMessage,
|
|
5471
|
-
stop,
|
|
5472
|
-
clearMessages,
|
|
5473
|
-
setMessages,
|
|
5474
|
-
regenerate,
|
|
5475
|
-
// Branching
|
|
5476
|
-
switchBranch,
|
|
5477
|
-
getBranchInfo,
|
|
5478
|
-
editMessage,
|
|
5479
|
-
hasBranches,
|
|
5480
|
-
getAllMessages,
|
|
5481
|
-
// Tool execution
|
|
5482
|
-
registerTool,
|
|
5483
|
-
unregisterTool,
|
|
5484
|
-
registeredTools,
|
|
5485
|
-
toolExecutions,
|
|
5486
|
-
pendingApprovals,
|
|
5487
|
-
approveToolExecution,
|
|
5488
|
-
rejectToolExecution,
|
|
5489
|
-
// Actions
|
|
5490
|
-
registerAction,
|
|
5491
|
-
unregisterAction,
|
|
5492
|
-
registeredActions,
|
|
5493
|
-
// AI Context
|
|
5494
|
-
addContext,
|
|
5495
|
-
removeContext,
|
|
5496
|
-
contextChars,
|
|
5497
|
-
contextUsage,
|
|
5498
|
-
// System Prompt
|
|
5499
|
-
setSystemPrompt,
|
|
5500
|
-
// Skills
|
|
5501
|
-
setInlineSkills,
|
|
5502
|
-
// Config
|
|
5503
|
-
threadId,
|
|
5504
|
-
runtimeUrl,
|
|
5505
|
-
toolsConfig
|
|
5506
|
-
}),
|
|
5507
|
-
[
|
|
5508
|
-
messages,
|
|
5509
|
-
status,
|
|
5510
|
-
error,
|
|
5511
|
-
isLoading,
|
|
5512
|
-
sendMessage,
|
|
5513
|
-
stop,
|
|
5514
|
-
clearMessages,
|
|
5515
|
-
setMessages,
|
|
5516
|
-
regenerate,
|
|
5517
|
-
switchBranch,
|
|
5518
|
-
getBranchInfo,
|
|
5519
|
-
editMessage,
|
|
5520
|
-
hasBranches,
|
|
5521
|
-
getAllMessages,
|
|
5522
|
-
registerTool,
|
|
5523
|
-
unregisterTool,
|
|
5524
|
-
registeredTools,
|
|
5525
|
-
toolExecutions,
|
|
5526
|
-
pendingApprovals,
|
|
5527
|
-
approveToolExecution,
|
|
5528
|
-
rejectToolExecution,
|
|
5529
|
-
registerAction,
|
|
5530
|
-
unregisterAction,
|
|
5531
|
-
registeredActions,
|
|
5532
|
-
addContext,
|
|
5533
|
-
removeContext,
|
|
5534
|
-
contextChars,
|
|
5535
|
-
contextUsage,
|
|
5536
|
-
setSystemPrompt,
|
|
5537
|
-
setInlineSkills,
|
|
5538
|
-
threadId,
|
|
5539
|
-
runtimeUrl,
|
|
5540
|
-
toolsConfig
|
|
5541
|
-
]
|
|
5542
|
-
);
|
|
5543
|
-
const messageHistoryContextValue = React2.useMemo(
|
|
5544
|
-
() => ({
|
|
5545
|
-
config: { ...defaultMessageHistoryConfig, ...messageHistory },
|
|
5546
|
-
tokenUsage: {
|
|
5547
|
-
current: 0,
|
|
5548
|
-
max: messageHistory?.maxContextTokens ?? 128e3,
|
|
5549
|
-
percentage: 0,
|
|
5550
|
-
isApproaching: false
|
|
5551
|
-
},
|
|
5552
|
-
compactionState: {
|
|
5553
|
-
rollingSummary: null,
|
|
5554
|
-
lastCompactionAt: null,
|
|
5555
|
-
compactionCount: 0,
|
|
5556
|
-
totalTokensSaved: 0,
|
|
5557
|
-
workingMemory: [],
|
|
5558
|
-
displayMessageCount: 0,
|
|
5559
|
-
llmMessageCount: 0
|
|
5560
|
-
}
|
|
5561
|
-
}),
|
|
5562
|
-
[messageHistory]
|
|
5563
|
-
);
|
|
5564
|
-
return /* @__PURE__ */ jsx(MessageHistoryContext.Provider, { value: messageHistoryContextValue, children: /* @__PURE__ */ jsxs(CopilotContext.Provider, { value: contextValue, children: [
|
|
5565
|
-
mcpServers?.map((config) => /* @__PURE__ */ jsx(MCPConnection, { config }, config.name)),
|
|
5566
|
-
messageHistory?.strategy && messageHistory.strategy !== "none" && /* @__PURE__ */ jsx(MessageHistoryBridge, { chatRef }),
|
|
5567
|
-
skills ? /* @__PURE__ */ jsx(SkillProvider, { skills, children }) : children
|
|
5568
|
-
] }) });
|
|
5569
|
-
}
|
|
5570
|
-
function useAIActions(actions) {
|
|
5571
|
-
const { registerAction, unregisterAction } = useCopilot();
|
|
5572
|
-
useEffect(() => {
|
|
5573
|
-
for (const action of actions) {
|
|
5574
|
-
registerAction(action);
|
|
5575
|
-
}
|
|
5576
|
-
return () => {
|
|
5577
|
-
for (const action of actions) {
|
|
5578
|
-
unregisterAction(action.name);
|
|
5579
|
-
}
|
|
5580
|
-
};
|
|
5581
|
-
}, [actions, registerAction, unregisterAction]);
|
|
5582
|
-
}
|
|
5583
|
-
function useAIAction(action) {
|
|
5584
|
-
useAIActions([action]);
|
|
5585
|
-
}
|
|
5586
|
-
function useAITools(options = {}) {
|
|
5587
|
-
const {
|
|
5588
|
-
screenshot = false,
|
|
5589
|
-
console: consoleCapture = false,
|
|
5590
|
-
network = false,
|
|
5591
|
-
requireConsent = true,
|
|
5592
|
-
screenshotOptions,
|
|
5593
|
-
consoleOptions,
|
|
5594
|
-
networkOptions,
|
|
5595
|
-
onConsentRequest,
|
|
5596
|
-
autoStart = true
|
|
5597
|
-
} = options;
|
|
5598
|
-
const [isEnabled] = useState(screenshot || consoleCapture || network);
|
|
5599
|
-
const [activeCaptures, setActiveCaptures] = useState({
|
|
5600
|
-
console: false,
|
|
5601
|
-
network: false
|
|
5602
|
-
});
|
|
5603
|
-
const [pendingConsent, setPendingConsent] = useState(null);
|
|
5604
|
-
const consentResolverRef = useRef(null);
|
|
5605
|
-
const rememberedConsentRef = useRef(/* @__PURE__ */ new Set());
|
|
5606
|
-
useEffect(() => {
|
|
5607
|
-
if (!autoStart || !isEnabled) return;
|
|
5608
|
-
if (consoleCapture && !isConsoleCaptureActive()) {
|
|
5609
|
-
startConsoleCapture(consoleOptions);
|
|
5610
|
-
setActiveCaptures((prev) => ({ ...prev, console: true }));
|
|
5611
|
-
}
|
|
5612
|
-
if (network && !isNetworkCaptureActive()) {
|
|
5613
|
-
startNetworkCapture(networkOptions);
|
|
5614
|
-
setActiveCaptures((prev) => ({ ...prev, network: true }));
|
|
5615
|
-
}
|
|
5616
|
-
return () => {
|
|
5617
|
-
stopConsoleCapture();
|
|
5618
|
-
stopNetworkCapture();
|
|
5619
|
-
};
|
|
5620
|
-
}, [
|
|
5621
|
-
autoStart,
|
|
5622
|
-
isEnabled,
|
|
5623
|
-
consoleCapture,
|
|
5624
|
-
network,
|
|
5625
|
-
consoleOptions,
|
|
5626
|
-
networkOptions
|
|
5627
|
-
]);
|
|
5628
|
-
const captureScreenshotFn = useCallback(
|
|
5629
|
-
async (opts) => {
|
|
5630
|
-
if (!screenshot) {
|
|
5631
|
-
throw new Error("Screenshot capture is not enabled");
|
|
5632
|
-
}
|
|
5633
|
-
if (!isScreenshotSupported()) {
|
|
5634
|
-
throw new Error(
|
|
5635
|
-
"Screenshot capture is not supported in this environment"
|
|
5636
|
-
);
|
|
5637
|
-
}
|
|
5638
|
-
return captureScreenshot({ ...screenshotOptions, ...opts });
|
|
5639
|
-
},
|
|
5640
|
-
[screenshot, screenshotOptions]
|
|
5641
|
-
);
|
|
5642
|
-
const getConsoleLogsFn = useCallback(
|
|
5643
|
-
(opts) => {
|
|
5644
|
-
if (!consoleCapture) {
|
|
5645
|
-
return { logs: [], totalCaptured: 0 };
|
|
5646
|
-
}
|
|
5647
|
-
return getConsoleLogs({ ...consoleOptions, ...opts });
|
|
5648
|
-
},
|
|
5649
|
-
[consoleCapture, consoleOptions]
|
|
5650
|
-
);
|
|
5651
|
-
const getNetworkRequestsFn = useCallback(
|
|
5652
|
-
(opts) => {
|
|
5653
|
-
if (!network) {
|
|
5654
|
-
return { requests: [], totalCaptured: 0 };
|
|
5655
|
-
}
|
|
5656
|
-
return getNetworkRequests({ ...networkOptions, ...opts });
|
|
5657
|
-
},
|
|
5658
|
-
[network, networkOptions]
|
|
5659
|
-
);
|
|
5660
|
-
const requestConsent = useCallback(
|
|
5661
|
-
async (tools, reason = "") => {
|
|
5662
|
-
const enabledTools = tools.filter((tool2) => {
|
|
5663
|
-
if (tool2 === "screenshot") return screenshot;
|
|
5664
|
-
if (tool2 === "console") return consoleCapture;
|
|
5665
|
-
if (tool2 === "network") return network;
|
|
5666
|
-
return false;
|
|
5667
|
-
});
|
|
5668
|
-
if (enabledTools.length === 0) {
|
|
5669
|
-
return { approved: [], denied: [] };
|
|
5670
|
-
}
|
|
5671
|
-
if (!requireConsent) {
|
|
5672
|
-
return { approved: enabledTools, denied: [] };
|
|
5673
|
-
}
|
|
5674
|
-
const needsConsent = enabledTools.filter(
|
|
5675
|
-
(tool2) => !rememberedConsentRef.current.has(tool2)
|
|
5676
|
-
);
|
|
5677
|
-
if (needsConsent.length === 0) {
|
|
5678
|
-
return { approved: enabledTools, denied: [] };
|
|
5679
|
-
}
|
|
5680
|
-
const request = {
|
|
5681
|
-
tools: needsConsent,
|
|
5682
|
-
reason,
|
|
5683
|
-
keywords: []
|
|
5684
|
-
};
|
|
5685
|
-
if (onConsentRequest) {
|
|
5686
|
-
const response = await onConsentRequest(request);
|
|
5687
|
-
if (response.remember) {
|
|
5688
|
-
response.approved.forEach(
|
|
5689
|
-
(tool2) => rememberedConsentRef.current.add(tool2)
|
|
5690
|
-
);
|
|
5691
|
-
}
|
|
5692
|
-
return response;
|
|
5693
|
-
}
|
|
5694
|
-
return new Promise((resolve) => {
|
|
5695
|
-
setPendingConsent(request);
|
|
5696
|
-
consentResolverRef.current = (response) => {
|
|
5697
|
-
if (response.remember) {
|
|
5698
|
-
response.approved.forEach(
|
|
5699
|
-
(tool2) => rememberedConsentRef.current.add(tool2)
|
|
5700
|
-
);
|
|
5701
|
-
}
|
|
5702
|
-
resolve(response);
|
|
5703
|
-
};
|
|
5704
|
-
});
|
|
5705
|
-
},
|
|
5706
|
-
[screenshot, consoleCapture, network, requireConsent, onConsentRequest]
|
|
5707
|
-
);
|
|
5708
|
-
const respondToConsent = useCallback((response) => {
|
|
5709
|
-
if (consentResolverRef.current) {
|
|
5710
|
-
consentResolverRef.current(response);
|
|
5711
|
-
consentResolverRef.current = null;
|
|
5712
|
-
}
|
|
5713
|
-
setPendingConsent(null);
|
|
5714
|
-
}, []);
|
|
5715
|
-
const captureContext = useCallback(
|
|
5716
|
-
async (tools) => {
|
|
5717
|
-
const toolsToCapture = tools || ["screenshot", "console", "network"];
|
|
5718
|
-
const context = {
|
|
5719
|
-
timestamp: Date.now()
|
|
5720
|
-
};
|
|
5721
|
-
const captures = [];
|
|
5722
|
-
if (toolsToCapture.includes("screenshot") && screenshot) {
|
|
5723
|
-
captures.push(
|
|
5724
|
-
captureScreenshotFn().then((result) => {
|
|
5725
|
-
context.screenshot = result;
|
|
5726
|
-
}).catch(() => {
|
|
5727
|
-
})
|
|
5728
|
-
);
|
|
5729
|
-
}
|
|
5730
|
-
if (toolsToCapture.includes("console") && consoleCapture) {
|
|
5731
|
-
context.consoleLogs = getConsoleLogsFn();
|
|
5732
|
-
}
|
|
5733
|
-
if (toolsToCapture.includes("network") && network) {
|
|
5734
|
-
context.networkRequests = getNetworkRequestsFn();
|
|
5735
|
-
}
|
|
5736
|
-
await Promise.all(captures);
|
|
5737
|
-
return context;
|
|
5738
|
-
},
|
|
5739
|
-
[
|
|
5740
|
-
screenshot,
|
|
5741
|
-
consoleCapture,
|
|
5742
|
-
network,
|
|
5743
|
-
captureScreenshotFn,
|
|
5744
|
-
getConsoleLogsFn,
|
|
5745
|
-
getNetworkRequestsFn
|
|
5746
|
-
]
|
|
5747
|
-
);
|
|
5748
|
-
const startCapturing = useCallback(() => {
|
|
5749
|
-
if (consoleCapture && !isConsoleCaptureActive()) {
|
|
5750
|
-
startConsoleCapture(consoleOptions);
|
|
5751
|
-
setActiveCaptures((prev) => ({ ...prev, console: true }));
|
|
5752
|
-
}
|
|
5753
|
-
if (network && !isNetworkCaptureActive()) {
|
|
5754
|
-
startNetworkCapture(networkOptions);
|
|
5755
|
-
setActiveCaptures((prev) => ({ ...prev, network: true }));
|
|
5756
|
-
}
|
|
5757
|
-
}, [consoleCapture, network, consoleOptions, networkOptions]);
|
|
5758
|
-
const stopCapturing = useCallback(() => {
|
|
5759
|
-
stopConsoleCapture();
|
|
5760
|
-
stopNetworkCapture();
|
|
5761
|
-
setActiveCaptures({ console: false, network: false });
|
|
5762
|
-
}, []);
|
|
5763
|
-
const clearCaptured = useCallback(() => {
|
|
5764
|
-
clearConsoleLogs();
|
|
5765
|
-
clearNetworkRequests();
|
|
5766
|
-
}, []);
|
|
5767
|
-
const formatForAI = useCallback((context) => {
|
|
5768
|
-
const parts = [];
|
|
5769
|
-
if (context.screenshot) {
|
|
5770
|
-
parts.push(
|
|
5771
|
-
`Screenshot captured (${context.screenshot.width}x${context.screenshot.height}, ${context.screenshot.format})`
|
|
5772
|
-
);
|
|
5773
|
-
}
|
|
5774
|
-
if (context.consoleLogs && context.consoleLogs.logs.length > 0) {
|
|
5775
|
-
parts.push(formatLogsForAI(context.consoleLogs.logs));
|
|
5776
|
-
}
|
|
5777
|
-
if (context.networkRequests && context.networkRequests.requests.length > 0) {
|
|
5778
|
-
parts.push(formatRequestsForAI(context.networkRequests.requests));
|
|
5779
|
-
}
|
|
5780
|
-
return parts.length > 0 ? parts.join("\n\n---\n\n") : "No context captured.";
|
|
5781
|
-
}, []);
|
|
5782
|
-
const detectIntentFn = useCallback(
|
|
5783
|
-
(message) => {
|
|
5784
|
-
const result = detectIntent(message);
|
|
5785
|
-
result.suggestedTools = result.suggestedTools.filter((tool2) => {
|
|
5786
|
-
if (tool2 === "screenshot") return screenshot;
|
|
5787
|
-
if (tool2 === "console") return consoleCapture;
|
|
5788
|
-
if (tool2 === "network") return network;
|
|
5789
|
-
return false;
|
|
5790
|
-
});
|
|
5791
|
-
return result;
|
|
5792
|
-
},
|
|
5793
|
-
[screenshot, consoleCapture, network]
|
|
5794
|
-
);
|
|
5795
|
-
return useMemo(
|
|
5796
|
-
() => ({
|
|
5797
|
-
isEnabled,
|
|
5798
|
-
activeCaptures,
|
|
5799
|
-
captureScreenshot: captureScreenshotFn,
|
|
5800
|
-
getConsoleLogs: getConsoleLogsFn,
|
|
5801
|
-
getNetworkRequests: getNetworkRequestsFn,
|
|
5802
|
-
captureContext,
|
|
5803
|
-
detectIntent: detectIntentFn,
|
|
5804
|
-
requestConsent,
|
|
5805
|
-
startCapturing,
|
|
5806
|
-
stopCapturing,
|
|
5807
|
-
clearCaptured,
|
|
5808
|
-
formatForAI,
|
|
5809
|
-
pendingConsent,
|
|
5810
|
-
respondToConsent
|
|
5811
|
-
}),
|
|
5812
|
-
[
|
|
5813
|
-
isEnabled,
|
|
5814
|
-
activeCaptures,
|
|
5815
|
-
captureScreenshotFn,
|
|
5816
|
-
getConsoleLogsFn,
|
|
5817
|
-
getNetworkRequestsFn,
|
|
5818
|
-
captureContext,
|
|
5819
|
-
detectIntentFn,
|
|
5820
|
-
requestConsent,
|
|
5821
|
-
startCapturing,
|
|
5822
|
-
stopCapturing,
|
|
5823
|
-
clearCaptured,
|
|
5824
|
-
formatForAI,
|
|
5825
|
-
pendingConsent,
|
|
5826
|
-
respondToConsent
|
|
5827
|
-
]
|
|
5828
|
-
);
|
|
5829
|
-
}
|
|
5830
|
-
function convertZodSchema(schema, _toolName) {
|
|
5831
|
-
try {
|
|
5832
|
-
const zodWithJsonSchema = z;
|
|
5833
|
-
if (typeof zodWithJsonSchema.toJSONSchema === "function") {
|
|
5834
|
-
const jsonSchema = zodWithJsonSchema.toJSONSchema(
|
|
5835
|
-
schema
|
|
5836
|
-
);
|
|
5837
|
-
if (jsonSchema.type === "object") {
|
|
5838
|
-
return {
|
|
5839
|
-
type: "object",
|
|
5840
|
-
properties: jsonSchema.properties || {},
|
|
5841
|
-
required: jsonSchema.required
|
|
5842
|
-
};
|
|
5843
|
-
}
|
|
5844
|
-
}
|
|
5845
|
-
} catch {
|
|
5846
|
-
}
|
|
5847
|
-
return zodObjectToInputSchema(schema);
|
|
5848
|
-
}
|
|
5849
|
-
function useToolWithSchema(config, dependencies = []) {
|
|
5850
|
-
const { registerTool, unregisterTool } = useCopilot();
|
|
5851
|
-
const configRef = useRef(config);
|
|
5852
|
-
configRef.current = config;
|
|
5853
|
-
const inputSchema = useMemo(() => {
|
|
5854
|
-
try {
|
|
5855
|
-
return convertZodSchema(config.schema, config.name);
|
|
5856
|
-
} catch (error) {
|
|
5857
|
-
console.warn(
|
|
5858
|
-
`[useToolWithSchema] Failed to convert Zod schema for tool "${config.name}"`,
|
|
5859
|
-
error
|
|
5860
|
-
);
|
|
5861
|
-
return {
|
|
5862
|
-
type: "object",
|
|
5863
|
-
properties: {}
|
|
5864
|
-
};
|
|
5865
|
-
}
|
|
5866
|
-
}, [config.schema, config.name]);
|
|
5867
|
-
useEffect(() => {
|
|
5868
|
-
const tool2 = {
|
|
5869
|
-
name: config.name,
|
|
5870
|
-
description: config.description,
|
|
5871
|
-
location: "client",
|
|
5872
|
-
inputSchema,
|
|
5873
|
-
handler: async (params, context) => {
|
|
5874
|
-
return configRef.current.handler(params, context);
|
|
5875
|
-
},
|
|
5876
|
-
render: config.render,
|
|
5877
|
-
available: config.available ?? true
|
|
5878
|
-
};
|
|
5879
|
-
registerTool(tool2);
|
|
5880
|
-
return () => {
|
|
5881
|
-
unregisterTool(config.name);
|
|
5882
|
-
};
|
|
5883
|
-
}, [config.name, inputSchema, ...dependencies]);
|
|
5884
|
-
}
|
|
5885
|
-
function useToolsWithSchema(tools, dependencies = []) {
|
|
5886
|
-
const { registerTool, unregisterTool } = useCopilot();
|
|
5887
|
-
const toolsRef = useRef(tools);
|
|
5888
|
-
toolsRef.current = tools;
|
|
5889
|
-
useEffect(() => {
|
|
5890
|
-
const toolNames = [];
|
|
5891
|
-
for (const config of tools) {
|
|
5892
|
-
let inputSchema;
|
|
5893
|
-
try {
|
|
5894
|
-
inputSchema = convertZodSchema(config.schema, config.name);
|
|
5895
|
-
} catch (error) {
|
|
5896
|
-
console.warn(
|
|
5897
|
-
`[useToolsWithSchema] Failed to convert Zod schema for tool "${config.name}"`,
|
|
5898
|
-
error
|
|
5899
|
-
);
|
|
5900
|
-
inputSchema = { type: "object", properties: {} };
|
|
5901
|
-
}
|
|
5902
|
-
const tool2 = {
|
|
5903
|
-
name: config.name,
|
|
5904
|
-
description: config.description,
|
|
5905
|
-
location: "client",
|
|
5906
|
-
inputSchema,
|
|
5907
|
-
handler: async (params, context) => {
|
|
5908
|
-
const currentConfig = toolsRef.current.find(
|
|
5909
|
-
(t) => t.name === config.name
|
|
5910
|
-
);
|
|
5911
|
-
if (currentConfig) {
|
|
5912
|
-
return currentConfig.handler(params, context);
|
|
5913
|
-
}
|
|
5914
|
-
return { success: false, error: "Tool handler not found" };
|
|
5915
|
-
},
|
|
5916
|
-
available: config.available ?? true
|
|
5917
|
-
};
|
|
5918
|
-
registerTool(tool2);
|
|
5919
|
-
toolNames.push(config.name);
|
|
5920
|
-
}
|
|
5921
|
-
return () => {
|
|
5922
|
-
for (const name of toolNames) {
|
|
5923
|
-
unregisterTool(name);
|
|
5924
|
-
}
|
|
5925
|
-
};
|
|
5926
|
-
}, [tools.map((t) => t.name).join(","), ...dependencies]);
|
|
5927
|
-
}
|
|
5928
|
-
var CopilotContext2 = createContext(null);
|
|
5929
|
-
function useCopilotContext() {
|
|
5930
|
-
const context = useContext(CopilotContext2);
|
|
5931
|
-
if (!context) {
|
|
5932
|
-
throw new Error("useCopilotContext must be used within a CopilotProvider");
|
|
5933
|
-
}
|
|
5934
|
-
return context;
|
|
5935
|
-
}
|
|
5936
|
-
|
|
5937
|
-
// src/react/hooks/useToolExecutor.ts
|
|
5938
|
-
function useToolExecutor() {
|
|
5939
|
-
const {
|
|
5940
|
-
registeredTools,
|
|
5941
|
-
config,
|
|
5942
|
-
chat,
|
|
5943
|
-
addToolExecution,
|
|
5944
|
-
updateToolExecution
|
|
5945
|
-
} = useCopilotContext();
|
|
5946
|
-
const toolsRef = useRef(registeredTools);
|
|
5947
|
-
toolsRef.current = registeredTools;
|
|
5948
|
-
const executeTool = useCallback(
|
|
5949
|
-
async (toolCall) => {
|
|
5950
|
-
const tool2 = toolsRef.current.find((t) => t.name === toolCall.name);
|
|
5951
|
-
if (!tool2) {
|
|
5952
|
-
return {
|
|
5953
|
-
success: false,
|
|
5954
|
-
error: `Unknown tool: ${toolCall.name}`
|
|
5955
|
-
};
|
|
5956
|
-
}
|
|
5957
|
-
if (!tool2.handler) {
|
|
5958
|
-
return {
|
|
5959
|
-
success: false,
|
|
5960
|
-
error: `Tool "${toolCall.name}" has no handler`
|
|
5961
|
-
};
|
|
5962
|
-
}
|
|
5963
|
-
const execution = {
|
|
5964
|
-
id: toolCall.id,
|
|
5965
|
-
name: toolCall.name,
|
|
5966
|
-
args: toolCall.input,
|
|
5967
|
-
status: "executing",
|
|
5968
|
-
timestamp: Date.now(),
|
|
5969
|
-
approvalStatus: "none",
|
|
5970
|
-
hidden: tool2.hidden
|
|
5971
|
-
};
|
|
5972
|
-
addToolExecution?.(execution);
|
|
5973
|
-
try {
|
|
5974
|
-
const startTime = Date.now();
|
|
5975
|
-
const result = await tool2.handler(toolCall.input);
|
|
5976
|
-
const duration = Date.now() - startTime;
|
|
5977
|
-
updateToolExecution?.(toolCall.id, {
|
|
5978
|
-
status: result.success ? "completed" : "error",
|
|
5979
|
-
result,
|
|
5980
|
-
error: result.error,
|
|
5981
|
-
duration
|
|
5982
|
-
});
|
|
5983
|
-
return result;
|
|
5984
|
-
} catch (error) {
|
|
5985
|
-
const errorMessage = error instanceof Error ? error.message : "Tool execution failed";
|
|
5986
|
-
updateToolExecution?.(toolCall.id, {
|
|
5987
|
-
status: "error",
|
|
5988
|
-
error: errorMessage
|
|
5989
|
-
});
|
|
5990
|
-
return {
|
|
5991
|
-
success: false,
|
|
5992
|
-
error: errorMessage
|
|
5993
|
-
};
|
|
5994
|
-
}
|
|
5995
|
-
},
|
|
5996
|
-
[addToolExecution, updateToolExecution]
|
|
5997
|
-
);
|
|
5998
|
-
const sendToolResult = useCallback(
|
|
5999
|
-
async (toolCallId, result) => {
|
|
6000
|
-
const runtimeUrl = config.runtimeUrl || config.cloud?.endpoint;
|
|
6001
|
-
if (!runtimeUrl) {
|
|
6002
|
-
console.warn(
|
|
6003
|
-
"[useToolExecutor] No runtime URL configured, cannot send tool result"
|
|
6004
|
-
);
|
|
6005
|
-
return;
|
|
6006
|
-
}
|
|
6007
|
-
const baseUrl = runtimeUrl.replace(/\/chat\/?$/, "");
|
|
6008
|
-
try {
|
|
6009
|
-
const response = await fetch(`${baseUrl}/tool-result`, {
|
|
6010
|
-
method: "POST",
|
|
6011
|
-
headers: {
|
|
6012
|
-
"Content-Type": "application/json"
|
|
6013
|
-
},
|
|
6014
|
-
body: JSON.stringify({
|
|
6015
|
-
threadId: chat.threadId || "default",
|
|
6016
|
-
toolCallId,
|
|
6017
|
-
result
|
|
6018
|
-
})
|
|
6019
|
-
});
|
|
6020
|
-
if (!response.ok) {
|
|
6021
|
-
console.error(
|
|
6022
|
-
"[useToolExecutor] Failed to send tool result:",
|
|
6023
|
-
await response.text()
|
|
6024
|
-
);
|
|
6025
|
-
}
|
|
6026
|
-
} catch (error) {
|
|
6027
|
-
console.error("[useToolExecutor] Error sending tool result:", error);
|
|
6028
|
-
}
|
|
6029
|
-
},
|
|
6030
|
-
[config.runtimeUrl, config.cloud?.endpoint, chat.threadId]
|
|
6031
|
-
);
|
|
6032
|
-
const getTool = useCallback((name) => {
|
|
6033
|
-
return toolsRef.current.find((t) => t.name === name);
|
|
6034
|
-
}, []);
|
|
6035
|
-
const hasTool = useCallback((name) => {
|
|
6036
|
-
return toolsRef.current.some((t) => t.name === name);
|
|
6037
|
-
}, []);
|
|
6038
|
-
return {
|
|
6039
|
-
executeTool,
|
|
6040
|
-
sendToolResult,
|
|
6041
|
-
getTool,
|
|
6042
|
-
hasTool
|
|
6043
|
-
};
|
|
6044
|
-
}
|
|
6045
|
-
function useSuggestions(options = {}) {
|
|
6046
|
-
const {
|
|
6047
|
-
count = 3,
|
|
6048
|
-
context,
|
|
6049
|
-
suggestions: staticSuggestions,
|
|
6050
|
-
autoRefresh = true
|
|
6051
|
-
} = options;
|
|
6052
|
-
const { chat, actions, config } = useCopilotContext();
|
|
6053
|
-
const [suggestions, setSuggestions] = useState([]);
|
|
6054
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
6055
|
-
const normalizedStatic = useMemo(
|
|
6056
|
-
() => staticSuggestions?.map((s) => typeof s === "string" ? { text: s } : s),
|
|
6057
|
-
[staticSuggestions]
|
|
6058
|
-
);
|
|
6059
|
-
const refresh = useCallback(async () => {
|
|
6060
|
-
if (normalizedStatic) {
|
|
6061
|
-
setSuggestions(normalizedStatic.slice(0, count));
|
|
6062
|
-
return;
|
|
6063
|
-
}
|
|
6064
|
-
if (!config.cloud) {
|
|
6065
|
-
return;
|
|
6066
|
-
}
|
|
6067
|
-
setIsLoading(true);
|
|
6068
|
-
try {
|
|
6069
|
-
const endpoint = config.cloud.endpoint || "https://api.yourgpt.ai/v1";
|
|
6070
|
-
const response = await fetch(`${endpoint}/suggestions`, {
|
|
6071
|
-
method: "POST",
|
|
6072
|
-
headers: {
|
|
6073
|
-
"Content-Type": "application/json",
|
|
6074
|
-
Authorization: `Bearer ${config.cloud.apiKey}`
|
|
6075
|
-
},
|
|
6076
|
-
body: JSON.stringify({
|
|
6077
|
-
botId: config.cloud.botId,
|
|
6078
|
-
count,
|
|
6079
|
-
context,
|
|
6080
|
-
messages: chat.messages.slice(-5)
|
|
6081
|
-
// Last 5 messages for context
|
|
6082
|
-
})
|
|
6083
|
-
});
|
|
6084
|
-
if (response.ok) {
|
|
6085
|
-
const data = await response.json();
|
|
6086
|
-
setSuggestions(
|
|
6087
|
-
data.suggestions.map(
|
|
6088
|
-
(s) => typeof s === "string" ? { text: s } : s
|
|
6089
|
-
)
|
|
6090
|
-
);
|
|
6091
|
-
}
|
|
6092
|
-
} catch (error) {
|
|
6093
|
-
console.error("Failed to fetch suggestions:", error);
|
|
6094
|
-
} finally {
|
|
6095
|
-
setIsLoading(false);
|
|
6096
|
-
}
|
|
6097
|
-
}, [config.cloud, count, context, chat.messages, normalizedStatic]);
|
|
6098
|
-
const select = useCallback(
|
|
6099
|
-
(suggestion) => {
|
|
6100
|
-
const text = typeof suggestion === "string" ? suggestion : suggestion.text;
|
|
6101
|
-
actions.sendMessage(text);
|
|
6102
|
-
},
|
|
6103
|
-
[actions]
|
|
6104
|
-
);
|
|
6105
|
-
useEffect(() => {
|
|
6106
|
-
if (autoRefresh && chat.messages.length === 0) {
|
|
6107
|
-
refresh();
|
|
6108
|
-
}
|
|
6109
|
-
}, [autoRefresh, chat.messages.length, refresh]);
|
|
6110
|
-
return {
|
|
6111
|
-
suggestions: normalizedStatic?.slice(0, count) || suggestions,
|
|
6112
|
-
isLoading,
|
|
6113
|
-
refresh,
|
|
6114
|
-
select
|
|
6115
|
-
};
|
|
6116
|
-
}
|
|
6117
|
-
function useAgent(options) {
|
|
6118
|
-
const { name, initialState = {}, onStateChange } = options;
|
|
6119
|
-
const { config } = useCopilotContext();
|
|
6120
|
-
const [state, setStateInternal] = useState(initialState);
|
|
6121
|
-
const [isRunning, setIsRunning] = useState(false);
|
|
6122
|
-
const [nodeName, setNodeName] = useState(null);
|
|
6123
|
-
const [error, setError] = useState(null);
|
|
6124
|
-
const abortControllerRef = useRef(null);
|
|
6125
|
-
const getEndpoint = useCallback(() => {
|
|
6126
|
-
if (config.cloud) {
|
|
6127
|
-
return `${config.cloud.endpoint || "https://api.yourgpt.ai/v1"}/agents/${name}`;
|
|
6128
|
-
}
|
|
6129
|
-
return `${config.runtimeUrl || "/api"}/agents/${name}`;
|
|
6130
|
-
}, [config, name]);
|
|
6131
|
-
const start = useCallback(
|
|
6132
|
-
async (input) => {
|
|
6133
|
-
setIsRunning(true);
|
|
6134
|
-
setError(null);
|
|
6135
|
-
abortControllerRef.current = new AbortController();
|
|
6136
|
-
try {
|
|
6137
|
-
const endpoint = getEndpoint();
|
|
6138
|
-
const headers = {
|
|
6139
|
-
"Content-Type": "application/json"
|
|
6140
|
-
};
|
|
6141
|
-
if (config.cloud?.apiKey) {
|
|
6142
|
-
headers["Authorization"] = `Bearer ${config.cloud.apiKey}`;
|
|
6143
|
-
}
|
|
6144
|
-
const response = await fetch(`${endpoint}/start`, {
|
|
6145
|
-
method: "POST",
|
|
6146
|
-
headers,
|
|
6147
|
-
body: JSON.stringify({
|
|
6148
|
-
input: typeof input === "string" ? { message: input } : input,
|
|
6149
|
-
state
|
|
6150
|
-
}),
|
|
6151
|
-
signal: abortControllerRef.current.signal
|
|
6152
|
-
});
|
|
6153
|
-
if (!response.ok) {
|
|
6154
|
-
throw new Error(`Agent error: ${response.status}`);
|
|
6155
|
-
}
|
|
6156
|
-
for await (const event of streamSSE(response)) {
|
|
6157
|
-
handleAgentEvent(event);
|
|
6158
|
-
}
|
|
6159
|
-
} catch (err) {
|
|
6160
|
-
if (err.name !== "AbortError") {
|
|
6161
|
-
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
6162
|
-
}
|
|
6163
|
-
} finally {
|
|
6164
|
-
setIsRunning(false);
|
|
6165
|
-
abortControllerRef.current = null;
|
|
6166
|
-
}
|
|
6167
|
-
},
|
|
6168
|
-
[config, getEndpoint, state]
|
|
6169
|
-
);
|
|
6170
|
-
const handleAgentEvent = useCallback(
|
|
6171
|
-
(event) => {
|
|
6172
|
-
if (event.type === "error") {
|
|
6173
|
-
setError(new Error(event.message));
|
|
6174
|
-
return;
|
|
6175
|
-
}
|
|
6176
|
-
if ("state" in event && event.state) {
|
|
6177
|
-
setStateInternal(event.state);
|
|
6178
|
-
onStateChange?.(event.state);
|
|
6179
|
-
}
|
|
6180
|
-
if ("nodeName" in event && event.nodeName) {
|
|
6181
|
-
setNodeName(event.nodeName);
|
|
6182
|
-
}
|
|
6183
|
-
},
|
|
6184
|
-
[onStateChange]
|
|
6185
|
-
);
|
|
6186
|
-
const stop = useCallback(() => {
|
|
6187
|
-
abortControllerRef.current?.abort();
|
|
6188
|
-
}, []);
|
|
6189
|
-
const setState = useCallback(
|
|
6190
|
-
(partialState) => {
|
|
6191
|
-
setStateInternal((prev) => {
|
|
6192
|
-
const newState = { ...prev, ...partialState };
|
|
6193
|
-
onStateChange?.(newState);
|
|
6194
|
-
return newState;
|
|
6195
|
-
});
|
|
6196
|
-
},
|
|
6197
|
-
[onStateChange]
|
|
6198
|
-
);
|
|
6199
|
-
useEffect(() => {
|
|
6200
|
-
return () => {
|
|
6201
|
-
abortControllerRef.current?.abort();
|
|
6202
|
-
};
|
|
6203
|
-
}, []);
|
|
6204
|
-
return {
|
|
6205
|
-
state,
|
|
6206
|
-
isRunning,
|
|
6207
|
-
nodeName,
|
|
6208
|
-
start,
|
|
6209
|
-
stop,
|
|
6210
|
-
setState,
|
|
6211
|
-
error
|
|
6212
|
-
};
|
|
6213
|
-
}
|
|
6214
|
-
|
|
6215
|
-
// src/react/utils/knowledge-base.ts
|
|
6216
|
-
var KNOWLEDGE_BASE_API = "https://api.yourgpt.ai/chatbot/v1/searchIndexDocument";
|
|
6217
|
-
async function searchKnowledgeBase(query, config) {
|
|
6218
|
-
try {
|
|
6219
|
-
const response = await fetch(KNOWLEDGE_BASE_API, {
|
|
6220
|
-
method: "POST",
|
|
6221
|
-
headers: {
|
|
6222
|
-
accept: "*/*",
|
|
6223
|
-
"content-type": "application/json",
|
|
6224
|
-
authorization: `Bearer ${config.token}`
|
|
6225
|
-
},
|
|
6226
|
-
body: JSON.stringify({
|
|
6227
|
-
project_uid: config.projectUid,
|
|
6228
|
-
query,
|
|
6229
|
-
page: 1,
|
|
6230
|
-
limit: String(config.limit || 10),
|
|
6231
|
-
app_id: config.appId || "1"
|
|
6232
|
-
})
|
|
6233
|
-
});
|
|
6234
|
-
if (!response.ok) {
|
|
6235
|
-
return {
|
|
6236
|
-
success: false,
|
|
6237
|
-
results: [],
|
|
6238
|
-
error: `API error: ${response.status} ${response.statusText}`
|
|
6239
|
-
};
|
|
6240
|
-
}
|
|
6241
|
-
const data = await response.json();
|
|
6242
|
-
const results = (data.data || data.results || []).map((item) => ({
|
|
6243
|
-
id: item.id || item._id || String(Math.random()),
|
|
6244
|
-
title: item.title || item.name || void 0,
|
|
6245
|
-
content: item.content || item.text || item.snippet || "",
|
|
6246
|
-
score: item.score || item.relevance || void 0,
|
|
6247
|
-
url: item.url || item.source_url || void 0,
|
|
6248
|
-
metadata: item.metadata || {}
|
|
6249
|
-
}));
|
|
6250
|
-
return {
|
|
6251
|
-
success: true,
|
|
6252
|
-
results,
|
|
6253
|
-
total: data.total || results.length,
|
|
6254
|
-
page: data.page || 1
|
|
6255
|
-
};
|
|
6256
|
-
} catch (error) {
|
|
6257
|
-
return {
|
|
6258
|
-
success: false,
|
|
6259
|
-
results: [],
|
|
6260
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
6261
|
-
};
|
|
6262
|
-
}
|
|
6263
|
-
}
|
|
6264
|
-
function formatKnowledgeResultsForAI(results) {
|
|
6265
|
-
if (results.length === 0) {
|
|
6266
|
-
return "No relevant documents found in the knowledge base.";
|
|
6267
|
-
}
|
|
6268
|
-
return results.map((result, index) => {
|
|
6269
|
-
const parts = [`[${index + 1}]`];
|
|
6270
|
-
if (result.title) parts.push(`**${result.title}**`);
|
|
6271
|
-
parts.push(result.content);
|
|
6272
|
-
if (result.url) parts.push(`Source: ${result.url}`);
|
|
6273
|
-
return parts.join("\n");
|
|
6274
|
-
}).join("\n\n---\n\n");
|
|
6275
|
-
}
|
|
6276
|
-
|
|
6277
|
-
// src/react/hooks/useKnowledgeBase.ts
|
|
6278
|
-
function useKnowledgeBase(config) {
|
|
6279
|
-
const { registerTool, unregisterTool } = useCopilot();
|
|
6280
|
-
const configRef = useRef(config);
|
|
6281
|
-
configRef.current = config;
|
|
6282
|
-
const handleSearch = useCallback(
|
|
6283
|
-
async (params) => {
|
|
6284
|
-
const query = params.query;
|
|
6285
|
-
if (!query) {
|
|
6286
|
-
return {
|
|
6287
|
-
success: false,
|
|
6288
|
-
error: "Query is required"
|
|
6289
|
-
};
|
|
6290
|
-
}
|
|
6291
|
-
const currentConfig = configRef.current;
|
|
6292
|
-
const kbConfig = {
|
|
6293
|
-
projectUid: currentConfig.projectUid,
|
|
6294
|
-
token: currentConfig.token,
|
|
6295
|
-
appId: currentConfig.appId,
|
|
6296
|
-
limit: currentConfig.limit || 5
|
|
6297
|
-
};
|
|
6298
|
-
const response = await searchKnowledgeBase(
|
|
6299
|
-
query,
|
|
6300
|
-
kbConfig
|
|
6301
|
-
);
|
|
6302
|
-
if (!response.success) {
|
|
6303
|
-
return {
|
|
6304
|
-
success: false,
|
|
6305
|
-
error: response.error || "Knowledge base search failed"
|
|
6306
|
-
};
|
|
6307
|
-
}
|
|
6308
|
-
const formattedResults = formatKnowledgeResultsForAI(response.results);
|
|
6309
|
-
return {
|
|
6310
|
-
success: true,
|
|
6311
|
-
message: formattedResults,
|
|
6312
|
-
data: {
|
|
6313
|
-
resultCount: response.results.length,
|
|
6314
|
-
total: response.total
|
|
6315
|
-
}
|
|
6316
|
-
};
|
|
6317
|
-
},
|
|
6318
|
-
[]
|
|
6319
|
-
);
|
|
6320
|
-
useEffect(() => {
|
|
6321
|
-
if (config.enabled === false) {
|
|
6322
|
-
return;
|
|
6323
|
-
}
|
|
6324
|
-
registerTool({
|
|
6325
|
-
name: "search_knowledge",
|
|
6326
|
-
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.",
|
|
6327
|
-
location: "client",
|
|
6328
|
-
inputSchema: {
|
|
6329
|
-
type: "object",
|
|
6330
|
-
properties: {
|
|
6331
|
-
query: {
|
|
6332
|
-
type: "string",
|
|
6333
|
-
description: "The search query to find relevant information in the knowledge base"
|
|
6334
|
-
}
|
|
6335
|
-
},
|
|
6336
|
-
required: ["query"]
|
|
6337
|
-
},
|
|
6338
|
-
handler: handleSearch
|
|
6339
|
-
});
|
|
6340
|
-
return () => {
|
|
6341
|
-
unregisterTool("search_knowledge");
|
|
6342
|
-
};
|
|
6343
|
-
}, [
|
|
6344
|
-
config.enabled,
|
|
6345
|
-
config.projectUid,
|
|
6346
|
-
config.token,
|
|
6347
|
-
registerTool,
|
|
6348
|
-
unregisterTool,
|
|
6349
|
-
handleSearch
|
|
6350
|
-
]);
|
|
6351
|
-
}
|
|
6352
|
-
var DEFAULT_CAPABILITIES = {
|
|
6353
|
-
supportsVision: false,
|
|
6354
|
-
supportsTools: true,
|
|
6355
|
-
supportsThinking: false,
|
|
6356
|
-
supportsStreaming: true,
|
|
6357
|
-
supportsPDF: false,
|
|
6358
|
-
supportsAudio: false,
|
|
6359
|
-
supportsVideo: false,
|
|
6360
|
-
maxTokens: 8192,
|
|
6361
|
-
supportedImageTypes: [],
|
|
6362
|
-
supportsJsonMode: false,
|
|
6363
|
-
supportsSystemMessages: true
|
|
6364
|
-
};
|
|
6365
|
-
function useCapabilities() {
|
|
6366
|
-
const { config } = useCopilotContext();
|
|
6367
|
-
const [capabilities, setCapabilities] = useState(DEFAULT_CAPABILITIES);
|
|
6368
|
-
const [provider, setProvider] = useState("unknown");
|
|
6369
|
-
const [model, setModel] = useState("unknown");
|
|
6370
|
-
const [supportedModels, setSupportedModels] = useState([]);
|
|
6371
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
6372
|
-
const [error, setError] = useState(null);
|
|
6373
|
-
const capabilitiesUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/chat\/?$/, "/capabilities") : null;
|
|
6374
|
-
const fetchCapabilities = useCallback(async () => {
|
|
6375
|
-
if (!capabilitiesUrl) {
|
|
6376
|
-
setIsLoading(false);
|
|
6377
|
-
return;
|
|
6378
|
-
}
|
|
6379
|
-
try {
|
|
6380
|
-
setIsLoading(true);
|
|
6381
|
-
setError(null);
|
|
6382
|
-
const response = await fetch(capabilitiesUrl);
|
|
6383
|
-
if (!response.ok) {
|
|
6384
|
-
throw new Error(`Failed to fetch capabilities: ${response.status}`);
|
|
6385
|
-
}
|
|
6386
|
-
const data = await response.json();
|
|
6387
|
-
setCapabilities(data.capabilities);
|
|
6388
|
-
setProvider(data.provider);
|
|
6389
|
-
setModel(data.model);
|
|
6390
|
-
setSupportedModels(data.supportedModels);
|
|
6391
|
-
} catch (err) {
|
|
6392
|
-
setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
6393
|
-
} finally {
|
|
6394
|
-
setIsLoading(false);
|
|
6395
|
-
}
|
|
6396
|
-
}, [capabilitiesUrl]);
|
|
6397
|
-
useEffect(() => {
|
|
6398
|
-
fetchCapabilities();
|
|
6399
|
-
}, [fetchCapabilities]);
|
|
6400
|
-
return {
|
|
6401
|
-
/** Current model capabilities */
|
|
6402
|
-
capabilities,
|
|
6403
|
-
/** Current provider name */
|
|
6404
|
-
provider,
|
|
6405
|
-
/** Current model ID */
|
|
6406
|
-
model,
|
|
6407
|
-
/** List of supported models for current provider */
|
|
6408
|
-
supportedModels,
|
|
6409
|
-
/** Whether capabilities are being loaded */
|
|
6410
|
-
isLoading,
|
|
6411
|
-
/** Error if fetch failed */
|
|
6412
|
-
error,
|
|
6413
|
-
/** Refetch capabilities */
|
|
6414
|
-
refetch: fetchCapabilities
|
|
6415
|
-
};
|
|
6416
|
-
}
|
|
6417
|
-
function useFeatureSupport(feature) {
|
|
6418
|
-
const { capabilities } = useCapabilities();
|
|
6419
|
-
return capabilities[feature] ?? false;
|
|
6420
|
-
}
|
|
6421
|
-
function useSupportedMediaTypes() {
|
|
6422
|
-
const { capabilities } = useCapabilities();
|
|
6423
|
-
return {
|
|
6424
|
-
/** Supported image MIME types */
|
|
6425
|
-
imageTypes: capabilities.supportedImageTypes || [],
|
|
6426
|
-
/** Supported audio MIME types */
|
|
6427
|
-
audioTypes: capabilities.supportedAudioTypes || [],
|
|
6428
|
-
/** Supported video MIME types */
|
|
6429
|
-
videoTypes: capabilities.supportedVideoTypes || [],
|
|
6430
|
-
/** Whether any image types are supported */
|
|
6431
|
-
hasImageSupport: (capabilities.supportedImageTypes?.length ?? 0) > 0,
|
|
6432
|
-
/** Whether any audio types are supported */
|
|
6433
|
-
hasAudioSupport: (capabilities.supportedAudioTypes?.length ?? 0) > 0,
|
|
6434
|
-
/** Whether any video types are supported */
|
|
6435
|
-
hasVideoSupport: (capabilities.supportedVideoTypes?.length ?? 0) > 0
|
|
6436
|
-
};
|
|
6437
|
-
}
|
|
6438
|
-
function useDevLogger() {
|
|
6439
|
-
const ctx = useCopilotContext();
|
|
6440
|
-
return useMemo(() => {
|
|
6441
|
-
const toolExecutions = (ctx.agentLoop?.toolExecutions || []).map(
|
|
6442
|
-
(exec) => ({
|
|
6443
|
-
id: exec.id,
|
|
6444
|
-
name: exec.name,
|
|
6445
|
-
status: exec.status,
|
|
6446
|
-
approvalStatus: exec.approvalStatus || "not_required"
|
|
6447
|
-
})
|
|
6448
|
-
);
|
|
6449
|
-
const pendingApprovalsCount = ctx.pendingApprovals?.length || 0;
|
|
6450
|
-
const registeredTools = (ctx.registeredTools || []).map((tool2) => ({
|
|
6451
|
-
name: tool2.name,
|
|
6452
|
-
location: tool2.location || "client"
|
|
6453
|
-
}));
|
|
6454
|
-
const registeredActions = (ctx.registeredActions || []).map((action) => ({
|
|
6455
|
-
name: action.name
|
|
6456
|
-
}));
|
|
6457
|
-
const storedPermissions = (ctx.storedPermissions || []).map((p) => ({
|
|
6458
|
-
toolName: p.toolName,
|
|
6459
|
-
level: p.level
|
|
6460
|
-
}));
|
|
6461
|
-
return {
|
|
6462
|
-
chat: {
|
|
6463
|
-
isLoading: ctx.chat?.isLoading || false,
|
|
6464
|
-
messageCount: ctx.chat?.messages?.length || 0,
|
|
6465
|
-
threadId: ctx.chat?.threadId || "none",
|
|
6466
|
-
error: ctx.chat?.error?.message || null
|
|
6467
|
-
},
|
|
6468
|
-
tools: {
|
|
6469
|
-
isEnabled: !!ctx.toolsConfig,
|
|
6470
|
-
isCapturing: ctx.tools?.isCapturing || false,
|
|
6471
|
-
pendingConsent: !!ctx.tools?.pendingConsent
|
|
6472
|
-
},
|
|
6473
|
-
agentLoop: {
|
|
6474
|
-
toolExecutions,
|
|
6475
|
-
pendingApprovals: pendingApprovalsCount,
|
|
6476
|
-
iteration: ctx.agentLoop?.iteration || 0,
|
|
6477
|
-
maxIterations: ctx.agentLoop?.maxIterations || 10
|
|
6478
|
-
},
|
|
6479
|
-
registered: {
|
|
6480
|
-
tools: registeredTools,
|
|
6481
|
-
actions: registeredActions,
|
|
6482
|
-
contextCount: ctx.contextTree?.length || 0
|
|
6483
|
-
},
|
|
6484
|
-
permissions: {
|
|
6485
|
-
stored: storedPermissions,
|
|
6486
|
-
loaded: ctx.permissionsLoaded || false
|
|
6487
|
-
},
|
|
6488
|
-
config: {
|
|
6489
|
-
runtimeUrl: ctx.config?.runtimeUrl || ctx.config?.cloud?.endpoint || ""
|
|
6490
|
-
}
|
|
6491
|
-
};
|
|
6492
|
-
}, [
|
|
6493
|
-
ctx.chat,
|
|
6494
|
-
ctx.tools,
|
|
6495
|
-
ctx.toolsConfig,
|
|
6496
|
-
ctx.agentLoop,
|
|
6497
|
-
ctx.pendingApprovals,
|
|
6498
|
-
ctx.registeredTools,
|
|
6499
|
-
ctx.registeredActions,
|
|
6500
|
-
ctx.contextTree,
|
|
6501
|
-
ctx.storedPermissions,
|
|
6502
|
-
ctx.permissionsLoaded,
|
|
6503
|
-
ctx.config
|
|
6504
|
-
]);
|
|
6505
|
-
}
|
|
6506
|
-
|
|
6507
|
-
// src/react/internal/ReactThreadManagerState.ts
|
|
6508
|
-
var ReactThreadManagerState = class {
|
|
6509
|
-
constructor(initialThreads) {
|
|
6510
|
-
this._threads = [];
|
|
6511
|
-
this._currentThreadId = null;
|
|
6512
|
-
this._currentThread = null;
|
|
6513
|
-
this._loadStatus = "idle";
|
|
6514
|
-
this._error = void 0;
|
|
6515
|
-
// Callbacks for React subscriptions (useSyncExternalStore)
|
|
6516
|
-
this.subscribers = /* @__PURE__ */ new Set();
|
|
6517
|
-
// ============================================
|
|
6518
|
-
// Subscription (for useSyncExternalStore)
|
|
6519
|
-
// ============================================
|
|
6520
|
-
/**
|
|
6521
|
-
* Subscribe to state changes.
|
|
6522
|
-
* Returns an unsubscribe function.
|
|
6523
|
-
*
|
|
6524
|
-
* @example
|
|
6525
|
-
* ```tsx
|
|
6526
|
-
* const threads = useSyncExternalStore(
|
|
6527
|
-
* state.subscribe,
|
|
6528
|
-
* () => state.threads
|
|
6529
|
-
* );
|
|
6530
|
-
* ```
|
|
6531
|
-
*/
|
|
6532
|
-
this.subscribe = (callback) => {
|
|
6533
|
-
this.subscribers.add(callback);
|
|
6534
|
-
return () => {
|
|
6535
|
-
this.subscribers.delete(callback);
|
|
6536
|
-
};
|
|
6537
|
-
};
|
|
6538
|
-
if (initialThreads) {
|
|
6539
|
-
this._threads = initialThreads;
|
|
6540
|
-
}
|
|
6541
|
-
}
|
|
6542
|
-
// ============================================
|
|
6543
|
-
// Getters
|
|
6544
|
-
// ============================================
|
|
6545
|
-
get threads() {
|
|
6546
|
-
return this._threads;
|
|
6547
|
-
}
|
|
6548
|
-
get currentThreadId() {
|
|
6549
|
-
return this._currentThreadId;
|
|
6550
|
-
}
|
|
6551
|
-
get currentThread() {
|
|
6552
|
-
return this._currentThread;
|
|
6553
|
-
}
|
|
6554
|
-
get loadStatus() {
|
|
6555
|
-
return this._loadStatus;
|
|
6556
|
-
}
|
|
6557
|
-
get error() {
|
|
6558
|
-
return this._error;
|
|
6559
|
-
}
|
|
6560
|
-
// ============================================
|
|
6561
|
-
// Setters (trigger reactivity)
|
|
6562
|
-
// ============================================
|
|
6563
|
-
set threads(value) {
|
|
6564
|
-
this._threads = value;
|
|
6565
|
-
this.notify();
|
|
6566
|
-
}
|
|
6567
|
-
// ============================================
|
|
6568
|
-
// Mutations
|
|
6569
|
-
// ============================================
|
|
6570
|
-
setThreads(threads) {
|
|
6571
|
-
this._threads = threads;
|
|
6572
|
-
this.notify();
|
|
6573
|
-
}
|
|
6574
|
-
setCurrentThread(thread) {
|
|
6575
|
-
this._currentThread = thread;
|
|
6576
|
-
this._currentThreadId = thread?.id ?? null;
|
|
6577
|
-
this.notify();
|
|
6578
|
-
}
|
|
6579
|
-
setCurrentThreadId(id) {
|
|
6580
|
-
this._currentThreadId = id;
|
|
6581
|
-
this.notify();
|
|
6582
|
-
}
|
|
6583
|
-
addThread(thread) {
|
|
6584
|
-
this._threads = [thread, ...this._threads];
|
|
6585
|
-
this.notify();
|
|
6586
|
-
}
|
|
6587
|
-
updateThread(id, updates) {
|
|
6588
|
-
this._threads = this._threads.map(
|
|
6589
|
-
(t) => t.id === id ? { ...t, ...updates } : t
|
|
6590
|
-
);
|
|
6591
|
-
if (updates.updatedAt) {
|
|
6592
|
-
this._threads = [...this._threads].sort(
|
|
6593
|
-
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
|
6594
|
-
);
|
|
6595
|
-
}
|
|
6596
|
-
if (this._currentThread?.id === id) {
|
|
6597
|
-
this._currentThread = { ...this._currentThread, ...updates };
|
|
6598
|
-
}
|
|
6599
|
-
this.notify();
|
|
6600
|
-
}
|
|
6601
|
-
removeThread(id) {
|
|
6602
|
-
this._threads = this._threads.filter((t) => t.id !== id);
|
|
6603
|
-
if (this._currentThreadId === id) {
|
|
6604
|
-
this._currentThreadId = null;
|
|
6605
|
-
this._currentThread = null;
|
|
6606
|
-
}
|
|
6607
|
-
this.notify();
|
|
6608
|
-
}
|
|
6609
|
-
setLoadStatus(status) {
|
|
6610
|
-
this._loadStatus = status;
|
|
6611
|
-
this.notify();
|
|
6612
|
-
}
|
|
6613
|
-
setError(error) {
|
|
6614
|
-
this._error = error;
|
|
6615
|
-
this.notify();
|
|
6616
|
-
}
|
|
6617
|
-
// ============================================
|
|
6618
|
-
// Snapshots (for useSyncExternalStore)
|
|
6619
|
-
// ============================================
|
|
6620
|
-
getThreadsSnapshot() {
|
|
6621
|
-
return this._threads;
|
|
6622
|
-
}
|
|
6623
|
-
getCurrentThreadSnapshot() {
|
|
6624
|
-
return this._currentThread;
|
|
6625
|
-
}
|
|
6626
|
-
getLoadStatusSnapshot() {
|
|
6627
|
-
return this._loadStatus;
|
|
6628
|
-
}
|
|
6629
|
-
getErrorSnapshot() {
|
|
6630
|
-
return this._error;
|
|
6631
|
-
}
|
|
6632
|
-
// ============================================
|
|
6633
|
-
// Private Methods
|
|
6634
|
-
// ============================================
|
|
6635
|
-
notify() {
|
|
6636
|
-
this.subscribers.forEach((cb) => cb());
|
|
6637
|
-
}
|
|
6638
|
-
/**
|
|
6639
|
-
* Cleanup subscriptions
|
|
6640
|
-
*/
|
|
6641
|
-
dispose() {
|
|
6642
|
-
this.subscribers.clear();
|
|
6643
|
-
}
|
|
6644
|
-
};
|
|
6645
|
-
function createReactThreadManagerState(initialThreads) {
|
|
6646
|
-
return new ReactThreadManagerState(initialThreads);
|
|
6647
|
-
}
|
|
6648
|
-
|
|
6649
|
-
// src/react/internal/ReactThreadManager.ts
|
|
6650
|
-
var _ReactThreadManager = class _ReactThreadManager extends ThreadManager {
|
|
6651
|
-
constructor(config = {}, callbacks = {}) {
|
|
6652
|
-
const reactState = new ReactThreadManagerState();
|
|
6653
|
-
super({ ...config, state: reactState }, callbacks);
|
|
6654
|
-
// ============================================
|
|
6655
|
-
// Subscription Methods (for useSyncExternalStore)
|
|
6656
|
-
// ============================================
|
|
6657
|
-
/**
|
|
6658
|
-
* Subscribe to state changes
|
|
6659
|
-
* Use with useSyncExternalStore
|
|
6660
|
-
*/
|
|
6661
|
-
this.subscribe = (callback) => {
|
|
6662
|
-
return this.state.subscribe(callback);
|
|
6663
|
-
};
|
|
6664
|
-
// ============================================
|
|
6665
|
-
// Snapshot Getters (for useSyncExternalStore)
|
|
6666
|
-
// ============================================
|
|
6667
|
-
/**
|
|
6668
|
-
* Get threads snapshot
|
|
6669
|
-
*/
|
|
6670
|
-
this.getThreadsSnapshot = () => {
|
|
6671
|
-
return this.state.getThreadsSnapshot();
|
|
6672
|
-
};
|
|
6673
|
-
/**
|
|
6674
|
-
* Get current thread snapshot
|
|
6675
|
-
*/
|
|
6676
|
-
this.getCurrentThreadSnapshot = () => {
|
|
6677
|
-
return this.state.getCurrentThreadSnapshot();
|
|
6678
|
-
};
|
|
6679
|
-
/**
|
|
6680
|
-
* Get current thread ID snapshot
|
|
6681
|
-
*/
|
|
6682
|
-
this.getCurrentThreadIdSnapshot = () => {
|
|
6683
|
-
return this.state.currentThreadId;
|
|
6684
|
-
};
|
|
6685
|
-
/**
|
|
6686
|
-
* Get load status snapshot
|
|
6687
|
-
*/
|
|
6688
|
-
this.getLoadStatusSnapshot = () => {
|
|
6689
|
-
return this.state.getLoadStatusSnapshot();
|
|
6690
|
-
};
|
|
6691
|
-
/**
|
|
6692
|
-
* Get error snapshot
|
|
6693
|
-
*/
|
|
6694
|
-
this.getErrorSnapshot = () => {
|
|
6695
|
-
return this.state.getErrorSnapshot();
|
|
6696
|
-
};
|
|
6697
|
-
/**
|
|
6698
|
-
* Get isLoading snapshot
|
|
6699
|
-
*/
|
|
6700
|
-
this.getIsLoadingSnapshot = () => {
|
|
6701
|
-
return this.state.getLoadStatusSnapshot() === "loading";
|
|
6702
|
-
};
|
|
6703
|
-
/**
|
|
6704
|
-
* Get threads snapshot for server (always empty for hydration consistency)
|
|
6705
|
-
*/
|
|
6706
|
-
this.getThreadsServerSnapshot = () => {
|
|
6707
|
-
return _ReactThreadManager.EMPTY_THREADS;
|
|
6708
|
-
};
|
|
6709
|
-
/**
|
|
6710
|
-
* Get current thread snapshot for server (always null)
|
|
6711
|
-
*/
|
|
6712
|
-
this.getCurrentThreadServerSnapshot = () => {
|
|
6713
|
-
return null;
|
|
6714
|
-
};
|
|
6715
|
-
/**
|
|
6716
|
-
* Get current thread ID snapshot for server (always null)
|
|
6717
|
-
*/
|
|
6718
|
-
this.getCurrentThreadIdServerSnapshot = () => {
|
|
6719
|
-
return null;
|
|
6720
|
-
};
|
|
6721
|
-
/**
|
|
6722
|
-
* Get load status snapshot for server (always "idle")
|
|
6723
|
-
*/
|
|
6724
|
-
this.getLoadStatusServerSnapshot = () => {
|
|
6725
|
-
return _ReactThreadManager.IDLE_STATUS;
|
|
6726
|
-
};
|
|
6727
|
-
/**
|
|
6728
|
-
* Get error snapshot for server (always undefined)
|
|
6729
|
-
*/
|
|
6730
|
-
this.getErrorServerSnapshot = () => {
|
|
6731
|
-
return void 0;
|
|
6732
|
-
};
|
|
6733
|
-
/**
|
|
6734
|
-
* Get isLoading snapshot for server (always false)
|
|
6735
|
-
*/
|
|
6736
|
-
this.getIsLoadingServerSnapshot = () => {
|
|
6737
|
-
return false;
|
|
6738
|
-
};
|
|
6739
|
-
}
|
|
6740
|
-
// ============================================
|
|
6741
|
-
// Cleanup
|
|
6742
|
-
// ============================================
|
|
6743
|
-
/**
|
|
6744
|
-
* Dispose of the manager
|
|
6745
|
-
*/
|
|
6746
|
-
async dispose() {
|
|
6747
|
-
this.state.dispose();
|
|
6748
|
-
await super.dispose();
|
|
6749
|
-
}
|
|
6750
|
-
};
|
|
6751
|
-
// ============================================
|
|
6752
|
-
// Server Snapshots (for SSR - stable cached values)
|
|
6753
|
-
// ============================================
|
|
6754
|
-
// Cached values for server snapshots (must be stable references)
|
|
6755
|
-
_ReactThreadManager.EMPTY_THREADS = [];
|
|
6756
|
-
_ReactThreadManager.IDLE_STATUS = "idle";
|
|
6757
|
-
var ReactThreadManager = _ReactThreadManager;
|
|
6758
|
-
function createReactThreadManager(config, callbacks) {
|
|
6759
|
-
return new ReactThreadManager(config, callbacks);
|
|
6760
|
-
}
|
|
6761
|
-
|
|
6762
|
-
// src/react/hooks/useThreadManager.ts
|
|
6763
|
-
var defaultManager = null;
|
|
6764
|
-
function getDefaultManager() {
|
|
6765
|
-
if (!defaultManager) {
|
|
6766
|
-
defaultManager = createReactThreadManager();
|
|
6767
|
-
}
|
|
6768
|
-
return defaultManager;
|
|
6769
|
-
}
|
|
6770
|
-
var internalManager = null;
|
|
6771
|
-
function getInternalManager(config) {
|
|
6772
|
-
if (!internalManager) {
|
|
6773
|
-
internalManager = createReactThreadManager(
|
|
6774
|
-
{
|
|
6775
|
-
adapter: config.adapter,
|
|
6776
|
-
saveDebounce: config.saveDebounce,
|
|
6777
|
-
autoLoad: config.autoLoad,
|
|
6778
|
-
autoRestoreLastThread: config.autoRestoreLastThread
|
|
6779
|
-
},
|
|
6780
|
-
config.callbacks
|
|
6781
|
-
);
|
|
6782
|
-
}
|
|
6783
|
-
return internalManager;
|
|
6784
|
-
}
|
|
6785
|
-
function useThreadManager(config) {
|
|
6786
|
-
const manager = useMemo(() => {
|
|
6787
|
-
if (!config) {
|
|
6788
|
-
return getDefaultManager();
|
|
6789
|
-
}
|
|
6790
|
-
if (!config.adapter) {
|
|
6791
|
-
return getInternalManager(config);
|
|
6792
|
-
}
|
|
6793
|
-
return createReactThreadManager(
|
|
6794
|
-
{
|
|
6795
|
-
adapter: config.adapter,
|
|
6796
|
-
saveDebounce: config.saveDebounce,
|
|
6797
|
-
autoLoad: config.autoLoad,
|
|
6798
|
-
autoRestoreLastThread: config.autoRestoreLastThread
|
|
6799
|
-
},
|
|
6800
|
-
config.callbacks
|
|
6801
|
-
);
|
|
6802
|
-
}, [
|
|
6803
|
-
config?.adapter,
|
|
6804
|
-
config?.saveDebounce,
|
|
6805
|
-
config?.autoLoad,
|
|
6806
|
-
config?.autoRestoreLastThread
|
|
6807
|
-
// Note: callbacks are intentionally not in deps to avoid recreating manager
|
|
6808
|
-
]);
|
|
6809
|
-
const threads = useSyncExternalStore(
|
|
6810
|
-
manager.subscribe,
|
|
6811
|
-
manager.getThreadsSnapshot,
|
|
6812
|
-
manager.getThreadsServerSnapshot
|
|
6813
|
-
// SSR - always empty array
|
|
6814
|
-
);
|
|
6815
|
-
const currentThread = useSyncExternalStore(
|
|
6816
|
-
manager.subscribe,
|
|
6817
|
-
manager.getCurrentThreadSnapshot,
|
|
6818
|
-
manager.getCurrentThreadServerSnapshot
|
|
6819
|
-
// SSR - always null
|
|
6820
|
-
);
|
|
6821
|
-
const currentThreadId = useSyncExternalStore(
|
|
6822
|
-
manager.subscribe,
|
|
6823
|
-
manager.getCurrentThreadIdSnapshot,
|
|
6824
|
-
manager.getCurrentThreadIdServerSnapshot
|
|
6825
|
-
// SSR - always null
|
|
6826
|
-
);
|
|
6827
|
-
const loadStatus = useSyncExternalStore(
|
|
6828
|
-
manager.subscribe,
|
|
6829
|
-
manager.getLoadStatusSnapshot,
|
|
6830
|
-
manager.getLoadStatusServerSnapshot
|
|
6831
|
-
// SSR - always "idle"
|
|
6832
|
-
);
|
|
6833
|
-
const error = useSyncExternalStore(
|
|
6834
|
-
manager.subscribe,
|
|
6835
|
-
manager.getErrorSnapshot,
|
|
6836
|
-
manager.getErrorServerSnapshot
|
|
6837
|
-
// SSR - always undefined
|
|
6838
|
-
);
|
|
6839
|
-
const isLoading = useSyncExternalStore(
|
|
6840
|
-
manager.subscribe,
|
|
6841
|
-
manager.getIsLoadingSnapshot,
|
|
6842
|
-
manager.getIsLoadingServerSnapshot
|
|
6843
|
-
// SSR - always false
|
|
6844
|
-
);
|
|
6845
|
-
useEffect(() => {
|
|
6846
|
-
return () => {
|
|
6847
|
-
if (config?.adapter && manager !== defaultManager && manager !== internalManager) {
|
|
6848
|
-
manager.dispose();
|
|
6849
|
-
}
|
|
6850
|
-
};
|
|
6851
|
-
}, [manager, config]);
|
|
6852
|
-
useEffect(() => {
|
|
6853
|
-
const handleBeforeUnload = () => {
|
|
6854
|
-
if (manager.hasPendingChanges) {
|
|
6855
|
-
manager.saveNow().catch(() => {
|
|
6856
|
-
});
|
|
6857
|
-
}
|
|
6858
|
-
};
|
|
6859
|
-
if (typeof window !== "undefined") {
|
|
6860
|
-
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
6861
|
-
return () => {
|
|
6862
|
-
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
6863
|
-
};
|
|
6864
|
-
}
|
|
6865
|
-
}, [manager]);
|
|
6866
|
-
const createThread = useCallback(
|
|
6867
|
-
(options) => manager.createThread(options),
|
|
6868
|
-
[manager]
|
|
6869
|
-
);
|
|
6870
|
-
const switchThread = useCallback(
|
|
6871
|
-
(id) => manager.switchThread(id),
|
|
6872
|
-
[manager]
|
|
6873
|
-
);
|
|
6874
|
-
const updateCurrentThread = useCallback(
|
|
6875
|
-
(updates) => manager.updateCurrentThread(updates),
|
|
6876
|
-
[manager]
|
|
6877
|
-
);
|
|
6878
|
-
const deleteThread = useCallback(
|
|
6879
|
-
(id) => manager.deleteThread(id),
|
|
6880
|
-
[manager]
|
|
6881
|
-
);
|
|
6882
|
-
const clearCurrentThread = useCallback(
|
|
6883
|
-
() => manager.clearCurrentThread(),
|
|
6884
|
-
[manager]
|
|
6885
|
-
);
|
|
6886
|
-
const refreshThreads = useCallback(() => manager.loadThreads(), [manager]);
|
|
6887
|
-
const saveNow = useCallback(() => manager.saveNow(), [manager]);
|
|
6888
|
-
const clearAllThreads = useCallback(
|
|
6889
|
-
() => manager.clearAllThreads(),
|
|
6890
|
-
[manager]
|
|
6891
|
-
);
|
|
6892
|
-
const messages = useMemo(
|
|
6893
|
-
() => currentThread?.messages ?? [],
|
|
6894
|
-
[currentThread]
|
|
6895
|
-
);
|
|
6896
|
-
const setMessages = useCallback(
|
|
6897
|
-
(newMessages) => updateCurrentThread({ messages: newMessages }),
|
|
6898
|
-
[updateCurrentThread]
|
|
6899
|
-
);
|
|
6900
|
-
const hasPendingChanges = manager.hasPendingChanges;
|
|
6901
|
-
return {
|
|
6902
|
-
// State
|
|
6903
|
-
threads,
|
|
6904
|
-
currentThread,
|
|
6905
|
-
currentThreadId,
|
|
6906
|
-
isLoading,
|
|
6907
|
-
loadStatus,
|
|
6908
|
-
error,
|
|
6909
|
-
// Actions
|
|
6910
|
-
createThread,
|
|
6911
|
-
switchThread,
|
|
6912
|
-
updateCurrentThread,
|
|
6913
|
-
deleteThread,
|
|
6914
|
-
clearCurrentThread,
|
|
6915
|
-
refreshThreads,
|
|
6916
|
-
saveNow,
|
|
6917
|
-
clearAllThreads,
|
|
6918
|
-
// Utilities
|
|
6919
|
-
messages,
|
|
6920
|
-
setMessages,
|
|
6921
|
-
hasPendingChanges
|
|
6922
|
-
};
|
|
6923
|
-
}
|
|
6924
|
-
function useMCPUIIntents(config = {}) {
|
|
6925
|
-
const {
|
|
6926
|
-
onToolCall,
|
|
6927
|
-
onIntent,
|
|
6928
|
-
onPrompt,
|
|
6929
|
-
onNotify,
|
|
6930
|
-
onLink,
|
|
6931
|
-
requireConsent = { tool: false, link: true }
|
|
6932
|
-
} = config;
|
|
6933
|
-
const handleIntent = useCallback(
|
|
6934
|
-
async (intent, context) => {
|
|
6935
|
-
switch (intent.type) {
|
|
6936
|
-
case "tool": {
|
|
6937
|
-
if (requireConsent.tool) ;
|
|
6938
|
-
await onToolCall?.(intent.name, intent.arguments, context);
|
|
6939
|
-
break;
|
|
6940
|
-
}
|
|
6941
|
-
case "intent": {
|
|
6942
|
-
await onIntent?.(intent.action, intent.data, context);
|
|
6943
|
-
break;
|
|
6944
|
-
}
|
|
6945
|
-
case "prompt": {
|
|
6946
|
-
onPrompt?.(intent.text, context);
|
|
6947
|
-
break;
|
|
6948
|
-
}
|
|
6949
|
-
case "notify": {
|
|
6950
|
-
onNotify?.(intent.message, intent.level, context);
|
|
6951
|
-
break;
|
|
6952
|
-
}
|
|
6953
|
-
case "link": {
|
|
6954
|
-
const shouldContinue = onLink?.(intent.url, intent.newTab, context);
|
|
6955
|
-
if (shouldContinue === false) {
|
|
6956
|
-
break;
|
|
6957
|
-
}
|
|
6958
|
-
if (requireConsent.link) {
|
|
6959
|
-
const isSafeUrl = intent.url.startsWith("https://") || intent.url.startsWith("http://localhost");
|
|
6960
|
-
if (!isSafeUrl) {
|
|
6961
|
-
console.warn(
|
|
6962
|
-
"[MCP-UI] Blocked potentially unsafe link:",
|
|
6963
|
-
intent.url
|
|
6964
|
-
);
|
|
6965
|
-
break;
|
|
6966
|
-
}
|
|
6967
|
-
}
|
|
6968
|
-
if (typeof window !== "undefined") {
|
|
6969
|
-
if (intent.newTab !== false) {
|
|
6970
|
-
window.open(intent.url, "_blank", "noopener,noreferrer");
|
|
6971
|
-
} else {
|
|
6972
|
-
window.location.href = intent.url;
|
|
6973
|
-
}
|
|
6974
|
-
}
|
|
6975
|
-
break;
|
|
6976
|
-
}
|
|
6977
|
-
default: {
|
|
6978
|
-
console.warn(
|
|
6979
|
-
"[MCP-UI] Unknown intent type:",
|
|
6980
|
-
intent.type
|
|
6981
|
-
);
|
|
6982
|
-
}
|
|
6983
|
-
}
|
|
6984
|
-
},
|
|
6985
|
-
[onToolCall, onIntent, onPrompt, onNotify, onLink, requireConsent]
|
|
6986
|
-
);
|
|
6987
|
-
return useMemo(
|
|
6988
|
-
() => ({
|
|
6989
|
-
handleIntent
|
|
6990
|
-
}),
|
|
6991
|
-
[handleIntent]
|
|
6992
|
-
);
|
|
6993
|
-
}
|
|
6994
|
-
function createMessageIntentHandler(sendMessage) {
|
|
6995
|
-
return {
|
|
6996
|
-
onIntent: async (action, data) => {
|
|
6997
|
-
const dataStr = data ? ` with ${JSON.stringify(data)}` : "";
|
|
6998
|
-
await sendMessage(`User action: ${action}${dataStr}`);
|
|
6999
|
-
},
|
|
7000
|
-
onPrompt: (text) => {
|
|
7001
|
-
sendMessage(text);
|
|
7002
|
-
},
|
|
7003
|
-
onNotify: (message, level) => {
|
|
7004
|
-
if (level === "error") {
|
|
7005
|
-
sendMessage(`Error: ${message}`);
|
|
7006
|
-
}
|
|
7007
|
-
}
|
|
7008
|
-
};
|
|
7009
|
-
}
|
|
7010
|
-
function createToolIntentHandler(callTool) {
|
|
7011
|
-
return {
|
|
7012
|
-
onToolCall: async (name, args) => {
|
|
7013
|
-
await callTool(name, args);
|
|
7014
|
-
}
|
|
7015
|
-
};
|
|
7016
|
-
}
|
|
7017
|
-
function getLastResponseUsage(messages) {
|
|
7018
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
7019
|
-
const msg = messages[i];
|
|
7020
|
-
if (msg.role === "assistant" && msg.metadata?.usage) {
|
|
7021
|
-
const u = msg.metadata.usage;
|
|
7022
|
-
const prompt = u.prompt_tokens ?? 0;
|
|
7023
|
-
const completion = u.completion_tokens ?? 0;
|
|
7024
|
-
return {
|
|
7025
|
-
prompt_tokens: prompt,
|
|
7026
|
-
completion_tokens: completion,
|
|
7027
|
-
total_tokens: u.total_tokens ?? prompt + completion
|
|
7028
|
-
};
|
|
7029
|
-
}
|
|
7030
|
-
}
|
|
7031
|
-
return null;
|
|
7032
|
-
}
|
|
7033
|
-
function useContextStats() {
|
|
7034
|
-
const { contextChars, contextUsage, registeredTools, messages } = useCopilot();
|
|
7035
|
-
const toolCount = useMemo(() => registeredTools.length, [registeredTools]);
|
|
7036
|
-
const messageCount = useMemo(
|
|
7037
|
-
() => messages.filter((m) => m.role !== "system").length,
|
|
7038
|
-
[messages]
|
|
7039
|
-
);
|
|
7040
|
-
const totalTokens = useMemo(() => {
|
|
7041
|
-
if (contextUsage) return contextUsage.total.tokens;
|
|
7042
|
-
return Math.ceil(contextChars / 3.5);
|
|
7043
|
-
}, [contextUsage, contextChars]);
|
|
7044
|
-
const usagePercent = useMemo(() => {
|
|
7045
|
-
if (contextUsage) return contextUsage.total.percent;
|
|
7046
|
-
return 0;
|
|
7047
|
-
}, [contextUsage]);
|
|
7048
|
-
const lastResponseUsage = useMemo(
|
|
7049
|
-
() => getLastResponseUsage(messages),
|
|
7050
|
-
[messages]
|
|
7051
|
-
);
|
|
7052
|
-
return {
|
|
7053
|
-
contextUsage,
|
|
7054
|
-
totalTokens,
|
|
7055
|
-
usagePercent,
|
|
7056
|
-
contextChars,
|
|
7057
|
-
toolCount,
|
|
7058
|
-
messageCount,
|
|
7059
|
-
lastResponseUsage
|
|
7060
|
-
};
|
|
7061
|
-
}
|
|
7062
|
-
var DEV_CONTENT_WARN_THRESHOLD = 2e3;
|
|
7063
|
-
function useSkill(skill) {
|
|
7064
|
-
const { register, unregister } = useSkillContext();
|
|
7065
|
-
if (process.env.NODE_ENV !== "production" && skill.source.type === "inline" && skill.source.content.length > DEV_CONTENT_WARN_THRESHOLD) {
|
|
7066
|
-
console.warn(
|
|
7067
|
-
`[copilot-sdk/skills] Inline skill "${skill.name}" has ${skill.source.content.length} characters. Inline skills are sent on every request \u2014 keep them under ${DEV_CONTENT_WARN_THRESHOLD} characters. Consider using a file or URL skill instead.`
|
|
7068
|
-
);
|
|
7069
|
-
}
|
|
7070
|
-
useEffect(() => {
|
|
7071
|
-
if (skill.source.type !== "inline") {
|
|
7072
|
-
console.warn(
|
|
7073
|
-
`[copilot-sdk/skills] useSkill only supports inline skills client-side. Skill "${skill.name}" has source type "${skill.source.type}" and will be skipped.`
|
|
7074
|
-
);
|
|
7075
|
-
return;
|
|
7076
|
-
}
|
|
7077
|
-
const resolved = {
|
|
7078
|
-
...skill,
|
|
7079
|
-
content: skill.source.content
|
|
7080
|
-
};
|
|
7081
|
-
register(resolved);
|
|
7082
|
-
return () => {
|
|
7083
|
-
unregister(skill.name);
|
|
7084
|
-
};
|
|
7085
|
-
}, [
|
|
7086
|
-
skill.name,
|
|
7087
|
-
skill.source.type === "inline" ? skill.source.content : "",
|
|
7088
|
-
skill.strategy,
|
|
7089
|
-
skill.description
|
|
7090
|
-
]);
|
|
7091
|
-
}
|
|
7092
|
-
function useSkillStatus() {
|
|
7093
|
-
const { skills, registry } = useSkillContext();
|
|
7094
|
-
const has = useCallback(
|
|
7095
|
-
(name) => registry.has(name),
|
|
7096
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7097
|
-
[skills]
|
|
7098
|
-
);
|
|
7099
|
-
return {
|
|
7100
|
-
skills,
|
|
7101
|
-
count: skills.length,
|
|
7102
|
-
has
|
|
7103
|
-
};
|
|
7104
|
-
}
|
|
7105
|
-
|
|
7106
|
-
// src/react/utils/permission-storage.ts
|
|
7107
|
-
var DEFAULT_KEY_PREFIX = "yourgpt-permissions";
|
|
7108
|
-
function createPermissionStorage(config) {
|
|
7109
|
-
switch (config.type) {
|
|
7110
|
-
case "localStorage":
|
|
7111
|
-
return createBrowserStorageAdapter(
|
|
7112
|
-
typeof window !== "undefined" ? localStorage : null,
|
|
7113
|
-
config.keyPrefix
|
|
7114
|
-
);
|
|
7115
|
-
case "sessionStorage":
|
|
7116
|
-
return createBrowserStorageAdapter(
|
|
7117
|
-
typeof window !== "undefined" ? sessionStorage : null,
|
|
7118
|
-
config.keyPrefix
|
|
7119
|
-
);
|
|
7120
|
-
case "memory":
|
|
7121
|
-
default:
|
|
7122
|
-
return createMemoryStorageAdapter();
|
|
7123
|
-
}
|
|
7124
|
-
}
|
|
7125
|
-
function createBrowserStorageAdapter(storage, keyPrefix = DEFAULT_KEY_PREFIX) {
|
|
7126
|
-
const getStorageKey = () => keyPrefix;
|
|
7127
|
-
const loadPermissions = () => {
|
|
7128
|
-
if (!storage) return /* @__PURE__ */ new Map();
|
|
7129
|
-
try {
|
|
7130
|
-
const data = storage.getItem(getStorageKey());
|
|
7131
|
-
if (!data) return /* @__PURE__ */ new Map();
|
|
7132
|
-
const parsed = JSON.parse(data);
|
|
7133
|
-
return new Map(parsed.map((p) => [p.toolName, p]));
|
|
7134
|
-
} catch {
|
|
7135
|
-
return /* @__PURE__ */ new Map();
|
|
7136
|
-
}
|
|
7137
|
-
};
|
|
7138
|
-
const savePermissions = (permissions) => {
|
|
7139
|
-
if (!storage) return;
|
|
7140
|
-
try {
|
|
7141
|
-
storage.setItem(
|
|
7142
|
-
getStorageKey(),
|
|
7143
|
-
JSON.stringify(Array.from(permissions.values()))
|
|
7144
|
-
);
|
|
7145
|
-
} catch (e) {
|
|
7146
|
-
console.warn("[PermissionStorage] Failed to save permissions:", e);
|
|
7147
|
-
}
|
|
7148
|
-
};
|
|
7149
|
-
return {
|
|
7150
|
-
async get(toolName) {
|
|
7151
|
-
const permissions = loadPermissions();
|
|
7152
|
-
return permissions.get(toolName) || null;
|
|
7153
|
-
},
|
|
7154
|
-
async set(permission) {
|
|
7155
|
-
const permissions = loadPermissions();
|
|
7156
|
-
permissions.set(permission.toolName, permission);
|
|
7157
|
-
savePermissions(permissions);
|
|
7158
|
-
},
|
|
7159
|
-
async remove(toolName) {
|
|
7160
|
-
const permissions = loadPermissions();
|
|
7161
|
-
permissions.delete(toolName);
|
|
7162
|
-
savePermissions(permissions);
|
|
7163
|
-
},
|
|
7164
|
-
async getAll() {
|
|
7165
|
-
const permissions = loadPermissions();
|
|
7166
|
-
return Array.from(permissions.values());
|
|
7167
|
-
},
|
|
7168
|
-
async clear() {
|
|
7169
|
-
if (!storage) return;
|
|
7170
|
-
storage.removeItem(getStorageKey());
|
|
7171
|
-
}
|
|
7172
|
-
};
|
|
7173
|
-
}
|
|
7174
|
-
function createMemoryStorageAdapter() {
|
|
7175
|
-
const permissions = /* @__PURE__ */ new Map();
|
|
7176
|
-
return {
|
|
7177
|
-
async get(toolName) {
|
|
7178
|
-
return permissions.get(toolName) || null;
|
|
7179
|
-
},
|
|
7180
|
-
async set(permission) {
|
|
7181
|
-
permissions.set(permission.toolName, permission);
|
|
7182
|
-
},
|
|
7183
|
-
async remove(toolName) {
|
|
7184
|
-
permissions.delete(toolName);
|
|
7185
|
-
},
|
|
7186
|
-
async getAll() {
|
|
7187
|
-
return Array.from(permissions.values());
|
|
7188
|
-
},
|
|
7189
|
-
async clear() {
|
|
7190
|
-
permissions.clear();
|
|
7191
|
-
}
|
|
7192
|
-
};
|
|
7193
|
-
}
|
|
7194
|
-
function createSessionPermissionCache() {
|
|
7195
|
-
return /* @__PURE__ */ new Map();
|
|
7196
|
-
}
|
|
7197
|
-
|
|
7198
|
-
// src/react/internal/ReactChat.ts
|
|
7199
|
-
var ReactChat = class extends AbstractChat {
|
|
7200
|
-
constructor(config) {
|
|
7201
|
-
const reactState = new ReactChatState(config.initialMessages);
|
|
7202
|
-
const init = {
|
|
7203
|
-
runtimeUrl: config.runtimeUrl,
|
|
7204
|
-
systemPrompt: config.systemPrompt,
|
|
7205
|
-
llm: config.llm,
|
|
7206
|
-
threadId: config.threadId,
|
|
7207
|
-
streaming: config.streaming ?? true,
|
|
7208
|
-
headers: config.headers,
|
|
7209
|
-
initialMessages: config.initialMessages,
|
|
7210
|
-
state: reactState,
|
|
7211
|
-
callbacks: config.callbacks,
|
|
7212
|
-
debug: config.debug
|
|
7213
|
-
};
|
|
7214
|
-
super(init);
|
|
7215
|
-
// ============================================
|
|
7216
|
-
// Subscribe (for useSyncExternalStore)
|
|
7217
|
-
// ============================================
|
|
7218
|
-
/**
|
|
7219
|
-
* Subscribe to state changes.
|
|
7220
|
-
* Returns an unsubscribe function.
|
|
7221
|
-
*
|
|
7222
|
-
* @example
|
|
7223
|
-
* ```tsx
|
|
7224
|
-
* const messages = useSyncExternalStore(
|
|
7225
|
-
* chat.subscribe,
|
|
7226
|
-
* () => chat.messages
|
|
7227
|
-
* );
|
|
7228
|
-
* ```
|
|
7229
|
-
*/
|
|
7230
|
-
this.subscribe = (callback) => {
|
|
7231
|
-
return this.reactState.subscribe(callback);
|
|
7232
|
-
};
|
|
7233
|
-
this.reactState = reactState;
|
|
7234
|
-
}
|
|
7235
|
-
// ============================================
|
|
7236
|
-
// Event handling shortcuts
|
|
7237
|
-
// ============================================
|
|
7238
|
-
/**
|
|
7239
|
-
* Subscribe to tool calls events
|
|
7240
|
-
*/
|
|
7241
|
-
onToolCalls(handler) {
|
|
7242
|
-
return this.on("toolCalls", handler);
|
|
7243
|
-
}
|
|
7244
|
-
/**
|
|
7245
|
-
* Subscribe to done events
|
|
7246
|
-
*/
|
|
7247
|
-
onDone(handler) {
|
|
7248
|
-
return this.on("done", handler);
|
|
7249
|
-
}
|
|
7250
|
-
/**
|
|
7251
|
-
* Subscribe to error events
|
|
7252
|
-
*/
|
|
7253
|
-
onError(handler) {
|
|
7254
|
-
return this.on("error", handler);
|
|
7255
|
-
}
|
|
7256
|
-
// ============================================
|
|
7257
|
-
// Branching API — pass-throughs to ReactChatState
|
|
7258
|
-
// ============================================
|
|
7259
|
-
/**
|
|
7260
|
-
* Navigate to a sibling branch (makes it the active path).
|
|
7261
|
-
*/
|
|
7262
|
-
switchBranch(messageId) {
|
|
7263
|
-
this.reactState.switchBranch(messageId);
|
|
7264
|
-
}
|
|
7265
|
-
/**
|
|
7266
|
-
* Get branch navigation info for a message.
|
|
7267
|
-
* Returns null if the message has no siblings.
|
|
7268
|
-
*/
|
|
7269
|
-
getBranchInfo(messageId) {
|
|
7270
|
-
return this.reactState.getBranchInfo(messageId);
|
|
7271
|
-
}
|
|
7272
|
-
/**
|
|
7273
|
-
* Get all messages across all branches (for persistence).
|
|
7274
|
-
*/
|
|
7275
|
-
getAllMessages() {
|
|
7276
|
-
return this.reactState.getAllMessages();
|
|
7277
|
-
}
|
|
7278
|
-
/**
|
|
7279
|
-
* Whether any message has siblings (branching has occurred).
|
|
7280
|
-
*/
|
|
7281
|
-
get hasBranches() {
|
|
7282
|
-
return this.reactState.hasBranches;
|
|
7283
|
-
}
|
|
7284
|
-
// ============================================
|
|
7285
|
-
// Override dispose to clean up state
|
|
7286
|
-
// ============================================
|
|
7287
|
-
dispose() {
|
|
7288
|
-
super.dispose();
|
|
7289
|
-
this.reactState.dispose();
|
|
7290
|
-
}
|
|
7291
|
-
/**
|
|
7292
|
-
* Revive a disposed instance (for React StrictMode compatibility)
|
|
7293
|
-
*/
|
|
7294
|
-
revive() {
|
|
7295
|
-
super.revive();
|
|
7296
|
-
this.reactState.revive();
|
|
7297
|
-
}
|
|
7298
|
-
};
|
|
7299
|
-
function createReactChat(config) {
|
|
7300
|
-
return new ReactChat(config);
|
|
7301
|
-
}
|
|
7302
|
-
function useChat(config) {
|
|
7303
|
-
const chatRef = useRef(null);
|
|
7304
|
-
const [input, setInput] = useState("");
|
|
7305
|
-
if (chatRef.current !== null && chatRef.current.disposed) {
|
|
7306
|
-
chatRef.current.revive();
|
|
7307
|
-
}
|
|
7308
|
-
if (chatRef.current === null) {
|
|
7309
|
-
chatRef.current = createReactChat({
|
|
7310
|
-
runtimeUrl: config.runtimeUrl,
|
|
7311
|
-
systemPrompt: config.systemPrompt,
|
|
7312
|
-
llm: config.llm,
|
|
7313
|
-
threadId: config.threadId,
|
|
7314
|
-
streaming: config.streaming,
|
|
7315
|
-
headers: config.headers,
|
|
7316
|
-
initialMessages: config.initialMessages,
|
|
7317
|
-
debug: config.debug,
|
|
7318
|
-
callbacks: {
|
|
7319
|
-
onMessagesChange: config.onMessagesChange,
|
|
7320
|
-
onError: config.onError,
|
|
7321
|
-
onFinish: config.onFinish,
|
|
7322
|
-
onToolCalls: config.onToolCalls
|
|
7323
|
-
}
|
|
7324
|
-
});
|
|
7325
|
-
}
|
|
7326
|
-
const messages = useSyncExternalStore(
|
|
7327
|
-
chatRef.current.subscribe,
|
|
7328
|
-
() => chatRef.current.messages,
|
|
7329
|
-
() => chatRef.current.messages
|
|
7330
|
-
// Server snapshot
|
|
5419
|
+
return id;
|
|
5420
|
+
},
|
|
5421
|
+
[debugLog]
|
|
7331
5422
|
);
|
|
7332
|
-
const
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
5423
|
+
const removeContext = useCallback(
|
|
5424
|
+
(id) => {
|
|
5425
|
+
contextTreeRef.current = removeNode(contextTreeRef.current, id);
|
|
5426
|
+
const contextString = printTree(contextTreeRef.current);
|
|
5427
|
+
chatRef.current?.setContext(contextString);
|
|
5428
|
+
setContextChars(contextString.length);
|
|
5429
|
+
debugLog("Context removed:", id);
|
|
5430
|
+
},
|
|
5431
|
+
[debugLog]
|
|
7337
5432
|
);
|
|
7338
|
-
const
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
5433
|
+
const setSystemPrompt = useCallback(
|
|
5434
|
+
(prompt) => {
|
|
5435
|
+
chatRef.current?.setSystemPrompt(prompt);
|
|
5436
|
+
debugLog("System prompt updated via function");
|
|
5437
|
+
},
|
|
5438
|
+
[debugLog]
|
|
7343
5439
|
);
|
|
7344
|
-
const
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
5440
|
+
const setInlineSkills = useCallback(
|
|
5441
|
+
(skills2) => {
|
|
5442
|
+
chatRef.current?.setInlineSkills(skills2);
|
|
5443
|
+
debugLog("Inline skills updated", { count: skills2.length });
|
|
5444
|
+
},
|
|
5445
|
+
[debugLog]
|
|
7348
5446
|
);
|
|
7349
|
-
const isLoading = status === "streaming" || status === "submitted";
|
|
7350
5447
|
const sendMessage = useCallback(
|
|
7351
5448
|
async (content, attachments) => {
|
|
5449
|
+
debugLog("Sending message:", content);
|
|
5450
|
+
setAgentIteration(0);
|
|
7352
5451
|
await chatRef.current?.sendMessage(content, attachments);
|
|
7353
|
-
setInput("");
|
|
7354
5452
|
},
|
|
7355
|
-
[]
|
|
5453
|
+
[debugLog]
|
|
7356
5454
|
);
|
|
7357
5455
|
const stop = useCallback(() => {
|
|
7358
5456
|
chatRef.current?.stop();
|
|
@@ -7366,19 +5464,11 @@ function useChat(config) {
|
|
|
7366
5464
|
const regenerate = useCallback(async (messageId) => {
|
|
7367
5465
|
await chatRef.current?.regenerate(messageId);
|
|
7368
5466
|
}, []);
|
|
7369
|
-
const continueWithToolResults = useCallback(
|
|
7370
|
-
async (toolResults) => {
|
|
7371
|
-
await chatRef.current?.continueWithToolResults(toolResults);
|
|
7372
|
-
},
|
|
7373
|
-
[]
|
|
7374
|
-
);
|
|
7375
5467
|
const switchBranch = useCallback((messageId) => {
|
|
7376
5468
|
chatRef.current?.switchBranch(messageId);
|
|
7377
5469
|
}, []);
|
|
7378
5470
|
const getBranchInfo = useCallback(
|
|
7379
|
-
(messageId) =>
|
|
7380
|
-
return chatRef.current?.getBranchInfo(messageId) ?? null;
|
|
7381
|
-
},
|
|
5471
|
+
(messageId) => chatRef.current?.getBranchInfo(messageId) ?? null,
|
|
7382
5472
|
[]
|
|
7383
5473
|
);
|
|
7384
5474
|
const editMessage = useCallback(
|
|
@@ -7386,42 +5476,240 @@ function useChat(config) {
|
|
|
7386
5476
|
await chatRef.current?.sendMessage(newContent, void 0, {
|
|
7387
5477
|
editMessageId: messageId
|
|
7388
5478
|
});
|
|
7389
|
-
setInput("");
|
|
7390
5479
|
},
|
|
7391
5480
|
[]
|
|
7392
5481
|
);
|
|
5482
|
+
const getHasBranchesSnapshot = useCallback(
|
|
5483
|
+
() => chatRef.current.hasBranches,
|
|
5484
|
+
[]
|
|
5485
|
+
);
|
|
5486
|
+
const hasBranches = useSyncExternalStore(
|
|
5487
|
+
chatRef.current.subscribe,
|
|
5488
|
+
getHasBranchesSnapshot,
|
|
5489
|
+
() => false
|
|
5490
|
+
);
|
|
5491
|
+
const getAllMessages = useCallback(
|
|
5492
|
+
() => chatRef.current?.getAllMessages?.() ?? [],
|
|
5493
|
+
[]
|
|
5494
|
+
);
|
|
5495
|
+
useEffect(() => {
|
|
5496
|
+
if (onMessagesChange && messages.length > 0) {
|
|
5497
|
+
const allUIMessages = chatRef.current?.getAllMessages?.() ?? messages;
|
|
5498
|
+
const coreMessages = allUIMessages.map((m) => ({
|
|
5499
|
+
id: m.id,
|
|
5500
|
+
role: m.role,
|
|
5501
|
+
content: m.content,
|
|
5502
|
+
created_at: m.createdAt,
|
|
5503
|
+
tool_calls: m.toolCalls,
|
|
5504
|
+
tool_call_id: m.toolCallId,
|
|
5505
|
+
parent_id: m.parentId,
|
|
5506
|
+
children_ids: m.childrenIds,
|
|
5507
|
+
metadata: {
|
|
5508
|
+
attachments: m.attachments,
|
|
5509
|
+
thinking: m.thinking
|
|
5510
|
+
}
|
|
5511
|
+
}));
|
|
5512
|
+
onMessagesChange(coreMessages);
|
|
5513
|
+
}
|
|
5514
|
+
}, [messages, onMessagesChange]);
|
|
5515
|
+
useEffect(() => {
|
|
5516
|
+
if (error && onError) {
|
|
5517
|
+
onError(error);
|
|
5518
|
+
}
|
|
5519
|
+
}, [error, onError]);
|
|
7393
5520
|
useEffect(() => {
|
|
7394
5521
|
return () => {
|
|
7395
5522
|
chatRef.current?.dispose();
|
|
7396
5523
|
};
|
|
7397
5524
|
}, []);
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
5525
|
+
const contextValue = useMemo(
|
|
5526
|
+
() => ({
|
|
5527
|
+
// Chat state
|
|
5528
|
+
messages,
|
|
5529
|
+
status,
|
|
5530
|
+
error,
|
|
5531
|
+
isLoading,
|
|
5532
|
+
// Chat actions
|
|
5533
|
+
sendMessage,
|
|
5534
|
+
stop,
|
|
5535
|
+
clearMessages,
|
|
5536
|
+
setMessages,
|
|
5537
|
+
regenerate,
|
|
5538
|
+
// Branching
|
|
5539
|
+
switchBranch,
|
|
5540
|
+
getBranchInfo,
|
|
5541
|
+
editMessage,
|
|
5542
|
+
hasBranches,
|
|
5543
|
+
getAllMessages,
|
|
5544
|
+
// Tool execution
|
|
5545
|
+
registerTool,
|
|
5546
|
+
unregisterTool,
|
|
5547
|
+
registeredTools,
|
|
5548
|
+
toolExecutions,
|
|
5549
|
+
pendingApprovals,
|
|
5550
|
+
approveToolExecution,
|
|
5551
|
+
rejectToolExecution,
|
|
5552
|
+
agentIteration,
|
|
5553
|
+
// Actions
|
|
5554
|
+
registerAction,
|
|
5555
|
+
unregisterAction,
|
|
5556
|
+
registeredActions,
|
|
5557
|
+
// AI Context
|
|
5558
|
+
addContext,
|
|
5559
|
+
removeContext,
|
|
5560
|
+
contextChars,
|
|
5561
|
+
contextUsage,
|
|
5562
|
+
// System Prompt
|
|
5563
|
+
setSystemPrompt,
|
|
5564
|
+
// Skills
|
|
5565
|
+
setInlineSkills,
|
|
5566
|
+
// Config
|
|
5567
|
+
threadId,
|
|
5568
|
+
runtimeUrl,
|
|
5569
|
+
toolsConfig,
|
|
5570
|
+
// Headless primitives
|
|
5571
|
+
subscribeToStreamEvents,
|
|
5572
|
+
messageMeta: messageMetaStoreRef.current
|
|
5573
|
+
}),
|
|
5574
|
+
[
|
|
5575
|
+
messages,
|
|
5576
|
+
status,
|
|
5577
|
+
error,
|
|
5578
|
+
isLoading,
|
|
5579
|
+
sendMessage,
|
|
5580
|
+
stop,
|
|
5581
|
+
clearMessages,
|
|
5582
|
+
setMessages,
|
|
5583
|
+
regenerate,
|
|
5584
|
+
switchBranch,
|
|
5585
|
+
getBranchInfo,
|
|
5586
|
+
editMessage,
|
|
5587
|
+
hasBranches,
|
|
5588
|
+
getAllMessages,
|
|
5589
|
+
registerTool,
|
|
5590
|
+
unregisterTool,
|
|
5591
|
+
registeredTools,
|
|
5592
|
+
toolExecutions,
|
|
5593
|
+
pendingApprovals,
|
|
5594
|
+
approveToolExecution,
|
|
5595
|
+
rejectToolExecution,
|
|
5596
|
+
agentIteration,
|
|
5597
|
+
registerAction,
|
|
5598
|
+
unregisterAction,
|
|
5599
|
+
registeredActions,
|
|
5600
|
+
addContext,
|
|
5601
|
+
removeContext,
|
|
5602
|
+
contextChars,
|
|
5603
|
+
contextUsage,
|
|
5604
|
+
setSystemPrompt,
|
|
5605
|
+
setInlineSkills,
|
|
5606
|
+
threadId,
|
|
5607
|
+
runtimeUrl,
|
|
5608
|
+
toolsConfig
|
|
5609
|
+
]
|
|
5610
|
+
);
|
|
5611
|
+
const messageHistoryContextValue = React2.useMemo(
|
|
5612
|
+
() => ({
|
|
5613
|
+
config: { ...defaultMessageHistoryConfig, ...messageHistory },
|
|
5614
|
+
tokenUsage: {
|
|
5615
|
+
current: 0,
|
|
5616
|
+
max: messageHistory?.maxContextTokens ?? 128e3,
|
|
5617
|
+
percentage: 0,
|
|
5618
|
+
isApproaching: false
|
|
5619
|
+
},
|
|
5620
|
+
compactionState: {
|
|
5621
|
+
rollingSummary: null,
|
|
5622
|
+
lastCompactionAt: null,
|
|
5623
|
+
compactionCount: 0,
|
|
5624
|
+
totalTokensSaved: 0,
|
|
5625
|
+
workingMemory: [],
|
|
5626
|
+
displayMessageCount: 0,
|
|
5627
|
+
llmMessageCount: 0
|
|
5628
|
+
}
|
|
5629
|
+
}),
|
|
5630
|
+
[messageHistory]
|
|
5631
|
+
);
|
|
5632
|
+
return /* @__PURE__ */ jsx(MessageHistoryContext.Provider, { value: messageHistoryContextValue, children: /* @__PURE__ */ jsxs(CopilotContext.Provider, { value: contextValue, children: [
|
|
5633
|
+
mcpServers?.map((config) => /* @__PURE__ */ jsx(MCPConnection, { config }, config.name)),
|
|
5634
|
+
messageHistory?.strategy && messageHistory.strategy !== "none" && /* @__PURE__ */ jsx(MessageHistoryBridge, { chatRef }),
|
|
5635
|
+
skills ? /* @__PURE__ */ jsx(SkillProvider, { skills, children }) : children
|
|
5636
|
+
] }) });
|
|
7418
5637
|
}
|
|
7419
5638
|
|
|
7420
|
-
// src/react/
|
|
7421
|
-
function
|
|
7422
|
-
|
|
5639
|
+
// src/react/hooks/useMCPTools.ts
|
|
5640
|
+
function useMCPTools(config) {
|
|
5641
|
+
const {
|
|
5642
|
+
prefixToolNames = true,
|
|
5643
|
+
autoRegister = true,
|
|
5644
|
+
hidden = false,
|
|
5645
|
+
source = "mcp",
|
|
5646
|
+
...clientConfig
|
|
5647
|
+
} = config;
|
|
5648
|
+
const { registerTool, unregisterTool } = useCopilot();
|
|
5649
|
+
const registeredToolsRef = useRef([]);
|
|
5650
|
+
const mcpClient = useMCPClient(clientConfig);
|
|
5651
|
+
const toolAdapter = useMemo(
|
|
5652
|
+
() => new MCPToolAdapter(clientConfig.name),
|
|
5653
|
+
[clientConfig.name]
|
|
5654
|
+
);
|
|
5655
|
+
const toolDefinitions = useMemo(() => {
|
|
5656
|
+
if (!mcpClient.isConnected || mcpClient.state.tools.length === 0) {
|
|
5657
|
+
return [];
|
|
5658
|
+
}
|
|
5659
|
+
return mcpClient.state.tools.map(
|
|
5660
|
+
(tool) => toolAdapter.toToolDefinition(tool, {
|
|
5661
|
+
prefix: prefixToolNames,
|
|
5662
|
+
asServerTool: true,
|
|
5663
|
+
// MCP tools execute remotely
|
|
5664
|
+
callTool: mcpClient.callTool,
|
|
5665
|
+
hidden,
|
|
5666
|
+
// Hide from chat UI
|
|
5667
|
+
source
|
|
5668
|
+
// Tool source for UI differentiation
|
|
5669
|
+
})
|
|
5670
|
+
);
|
|
5671
|
+
}, [
|
|
5672
|
+
mcpClient.isConnected,
|
|
5673
|
+
mcpClient.state.tools,
|
|
5674
|
+
mcpClient.callTool,
|
|
5675
|
+
toolAdapter,
|
|
5676
|
+
prefixToolNames,
|
|
5677
|
+
hidden,
|
|
5678
|
+
source
|
|
5679
|
+
]);
|
|
5680
|
+
useEffect(() => {
|
|
5681
|
+
if (!autoRegister) {
|
|
5682
|
+
return;
|
|
5683
|
+
}
|
|
5684
|
+
for (const toolName of registeredToolsRef.current) {
|
|
5685
|
+
unregisterTool(toolName);
|
|
5686
|
+
}
|
|
5687
|
+
registeredToolsRef.current = [];
|
|
5688
|
+
if (mcpClient.isConnected && toolDefinitions.length > 0) {
|
|
5689
|
+
for (const tool of toolDefinitions) {
|
|
5690
|
+
registerTool(tool);
|
|
5691
|
+
registeredToolsRef.current.push(tool.name);
|
|
5692
|
+
}
|
|
5693
|
+
}
|
|
5694
|
+
return () => {
|
|
5695
|
+
for (const toolName of registeredToolsRef.current) {
|
|
5696
|
+
unregisterTool(toolName);
|
|
5697
|
+
}
|
|
5698
|
+
registeredToolsRef.current = [];
|
|
5699
|
+
};
|
|
5700
|
+
}, [
|
|
5701
|
+
autoRegister,
|
|
5702
|
+
mcpClient.isConnected,
|
|
5703
|
+
toolDefinitions,
|
|
5704
|
+
registerTool,
|
|
5705
|
+
unregisterTool
|
|
5706
|
+
]);
|
|
5707
|
+
return {
|
|
5708
|
+
...mcpClient,
|
|
5709
|
+
toolDefinitions
|
|
5710
|
+
};
|
|
7423
5711
|
}
|
|
7424
5712
|
|
|
7425
|
-
export { AbstractAgentLoop, AbstractChat, CopilotProvider, MessageHistoryContext, MessageTree,
|
|
7426
|
-
//# sourceMappingURL=chunk-
|
|
7427
|
-
//# sourceMappingURL=chunk-
|
|
5713
|
+
export { AbstractAgentLoop, AbstractChat, CopilotProvider, MessageHistoryContext, MessageTree, ReactChatState, SkillProvider, createReactChatState, defaultMessageHistoryConfig, initialAgentLoopState, isCompactionMarker, keepToolPairsAtomic, toDisplayMessage, toLLMMessage, toLLMMessages, useAIContext, useAIContexts, useCopilot, useMCPClient, useMCPTools, useMessageHistory, useMessageHistoryContext, useSkillContext, useTool, useTools };
|
|
5714
|
+
//# sourceMappingURL=chunk-DH6EO6NW.js.map
|
|
5715
|
+
//# sourceMappingURL=chunk-DH6EO6NW.js.map
|