@yourgpt/copilot-sdk 2.1.4 → 2.1.5-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MessageTree-CoIt_4nB.d.cts +161 -0
- package/dist/MessageTree-CzaN9Eul.d.ts +161 -0
- package/dist/{ThreadManager-Dkp_eLty.d.ts → ThreadManager-BEAECB7Y.d.ts} +1 -1
- package/dist/{ThreadManager-LfFRhr4e.d.cts → ThreadManager-Cw5fwyCN.d.cts} +1 -1
- package/dist/{chunk-POZNNKNJ.cjs → chunk-246B6X5D.cjs} +8 -2
- package/dist/chunk-246B6X5D.cjs.map +1 -0
- package/dist/{chunk-QLH6TSCC.js → chunk-4QXY2PBG.js} +8 -2
- package/dist/chunk-4QXY2PBG.js.map +1 -0
- package/dist/{chunk-7PKGRYHY.js → chunk-6BMQZIS3.js} +3094 -355
- package/dist/chunk-6BMQZIS3.js.map +1 -0
- package/dist/{chunk-N6VZ7FOW.cjs → chunk-76RE7AJE.cjs} +3277 -520
- package/dist/chunk-76RE7AJE.cjs.map +1 -0
- package/dist/chunk-BJYA5NDL.cjs +96 -0
- package/dist/chunk-BJYA5NDL.cjs.map +1 -0
- package/dist/{chunk-LZMBBGWH.js → chunk-ENFWM3EY.js} +4 -4
- package/dist/{chunk-LZMBBGWH.js.map → chunk-ENFWM3EY.js.map} +1 -1
- package/dist/{chunk-OQPRIB73.cjs → chunk-I3SQUNTT.cjs} +71 -25
- package/dist/chunk-I3SQUNTT.cjs.map +1 -0
- package/dist/{chunk-WAPGTQDR.cjs → chunk-JKGFQUHJ.cjs} +10 -10
- package/dist/{chunk-WAPGTQDR.cjs.map → chunk-JKGFQUHJ.cjs.map} +1 -1
- package/dist/{chunk-XGITAEXU.js → chunk-LLM7AHMO.js} +2 -2
- package/dist/{chunk-XGITAEXU.js.map → chunk-LLM7AHMO.js.map} +1 -1
- package/dist/{chunk-ASV6JLYG.cjs → chunk-NUXLAZOE.cjs} +2 -2
- package/dist/{chunk-ASV6JLYG.cjs.map → chunk-NUXLAZOE.cjs.map} +1 -1
- package/dist/{chunk-37KEHUCE.js → chunk-UXJ6LIZB.js} +51 -7
- package/dist/chunk-UXJ6LIZB.js.map +1 -0
- package/dist/chunk-VNLLW3ZI.js +94 -0
- package/dist/chunk-VNLLW3ZI.js.map +1 -0
- package/dist/core/index.cjs +99 -91
- 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/{index-BHkRA0mM.d.cts → index-CiExk87c.d.cts} +1 -1
- package/dist/{index-tB0qI8my.d.ts → index-Dwrcf-CP.d.ts} +1 -1
- package/dist/mcp/index.d.cts +3 -3
- package/dist/mcp/index.d.ts +3 -3
- package/dist/react/index.cjs +113 -52
- package/dist/react/index.d.cts +673 -77
- package/dist/react/index.d.ts +673 -77
- package/dist/react/index.js +7 -6
- package/dist/server/index.cjs +339 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +171 -0
- package/dist/server/index.d.ts +171 -0
- package/dist/server/index.js +332 -0
- package/dist/server/index.js.map +1 -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 +3 -3
- 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 +3 -3
- 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-coIcskZ4.d.ts → tools-DHZhF5km.d.cts} +161 -1
- package/dist/{tools-coIcskZ4.d.cts → tools-DHZhF5km.d.ts} +161 -1
- package/dist/{types-rjaSVmEF.d.ts → types-BTyJu0WD.d.ts} +1 -1
- package/dist/types-BckL3hiw.d.cts +93 -0
- package/dist/types-BckL3hiw.d.ts +93 -0
- package/dist/{types-C8t4Ut8f.d.cts → types-BdX7uPj0.d.cts} +1 -1
- package/dist/{types-DG2ya08y.d.ts → types-BeFBBZ5i.d.cts} +64 -1
- package/dist/{types-DG2ya08y.d.cts → types-BeFBBZ5i.d.ts} +64 -1
- package/dist/ui/index.cjs +468 -163
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +81 -4
- package/dist/ui/index.d.ts +81 -4
- package/dist/ui/index.js +416 -112
- package/dist/ui/index.js.map +1 -1
- package/package.json +6 -1
- package/dist/chunk-37KEHUCE.js.map +0 -1
- package/dist/chunk-7PKGRYHY.js.map +0 -1
- package/dist/chunk-N6VZ7FOW.cjs.map +0 -1
- package/dist/chunk-OQPRIB73.cjs.map +0 -1
- package/dist/chunk-POZNNKNJ.cjs.map +0 -1
- package/dist/chunk-QLH6TSCC.js.map +0 -1
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkI3SQUNTT_cjs = require('./chunk-I3SQUNTT.cjs');
|
|
4
4
|
var chunkJGPDQDY4_cjs = require('./chunk-JGPDQDY4.cjs');
|
|
5
|
-
var
|
|
5
|
+
var chunkBJYA5NDL_cjs = require('./chunk-BJYA5NDL.cjs');
|
|
6
|
+
var React2 = require('react');
|
|
6
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
8
|
var z = require('zod');
|
|
8
9
|
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
|
|
9
12
|
function _interopNamespace(e) {
|
|
10
13
|
if (e && e.__esModule) return e;
|
|
11
14
|
var n = Object.create(null);
|
|
@@ -24,6 +27,7 @@ function _interopNamespace(e) {
|
|
|
24
27
|
return Object.freeze(n);
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
27
31
|
var z__namespace = /*#__PURE__*/_interopNamespace(z);
|
|
28
32
|
|
|
29
33
|
// src/chat/types/tool.ts
|
|
@@ -141,15 +145,6 @@ function parseSSELine(line) {
|
|
|
141
145
|
function isStreamDone(chunk) {
|
|
142
146
|
return chunk.type === "done" || chunk.type === "error";
|
|
143
147
|
}
|
|
144
|
-
function requiresToolExecution(chunk) {
|
|
145
|
-
if (chunk.type === "done" && chunk.requiresAction) {
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
if (chunk.type === "tool_calls") {
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
148
|
|
|
154
149
|
// src/chat/functions/stream/processChunk.ts
|
|
155
150
|
function processStreamChunk(chunk, state) {
|
|
@@ -284,13 +279,14 @@ function createStreamState(messageId) {
|
|
|
284
279
|
function generateMessageId() {
|
|
285
280
|
return `msg-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
286
281
|
}
|
|
287
|
-
function createUserMessage(content, attachments) {
|
|
282
|
+
function createUserMessage(content, attachments, options) {
|
|
288
283
|
return {
|
|
289
284
|
id: generateMessageId(),
|
|
290
285
|
role: "user",
|
|
291
286
|
content,
|
|
292
287
|
attachments,
|
|
293
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
288
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
289
|
+
...options?.parentId !== void 0 ? { parentId: options.parentId } : {}
|
|
294
290
|
};
|
|
295
291
|
}
|
|
296
292
|
function streamStateToMessage(state) {
|
|
@@ -329,12 +325,13 @@ function streamStateToMessage(state) {
|
|
|
329
325
|
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
330
326
|
};
|
|
331
327
|
}
|
|
332
|
-
function createEmptyAssistantMessage(id) {
|
|
328
|
+
function createEmptyAssistantMessage(id, options) {
|
|
333
329
|
return {
|
|
334
330
|
id: generateMessageId(),
|
|
335
331
|
role: "assistant",
|
|
336
332
|
content: "",
|
|
337
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
333
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
334
|
+
...options?.parentId !== void 0 ? { parentId: options.parentId } : {}
|
|
338
335
|
};
|
|
339
336
|
}
|
|
340
337
|
|
|
@@ -403,6 +400,7 @@ var HttpTransport = class {
|
|
|
403
400
|
tools: request.tools,
|
|
404
401
|
actions: request.actions,
|
|
405
402
|
streaming: this.config.streaming,
|
|
403
|
+
__skills: request.__skills,
|
|
406
404
|
...resolved.configBody,
|
|
407
405
|
...request.body
|
|
408
406
|
}),
|
|
@@ -543,17 +541,443 @@ var HttpTransport = class {
|
|
|
543
541
|
}
|
|
544
542
|
};
|
|
545
543
|
|
|
546
|
-
// src/chat/
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
544
|
+
// src/chat/optimizations.ts
|
|
545
|
+
var DEFAULT_CHARS_PER_TOKEN = 4;
|
|
546
|
+
var DEFAULT_SAFETY_MARGIN = 1.2;
|
|
547
|
+
var DEFAULT_INPUT_HEADROOM_RATIO = 0.75;
|
|
548
|
+
var DEFAULT_SYSTEM_PROMPT_SHARE = 0.15;
|
|
549
|
+
var DEFAULT_HISTORY_SHARE = 0.5;
|
|
550
|
+
var DEFAULT_TOOL_RESULTS_SHARE = 0.3;
|
|
551
|
+
var DEFAULT_TOOL_DEFINITIONS_SHARE = 0.05;
|
|
552
|
+
var DEFAULT_MAX_TOOL_RESULT_CONTEXT_SHARE = 0.3;
|
|
553
|
+
var DEFAULT_TOOL_RESULT_HARD_MAX_CHARS = 4e5;
|
|
554
|
+
var DEFAULT_TOOL_RESULT_MIN_KEEP_CHARS = 2e3;
|
|
555
|
+
var DEFAULT_TOOL_RESULT_STRATEGY = "head-tail";
|
|
556
|
+
var DEFAULT_RECENT_HISTORY_PRESERVE = 6;
|
|
557
|
+
var TOOL_RESULT_TRUNCATION_NOTICE = "\n\n[tool result truncated to fit prompt budget]";
|
|
558
|
+
var TOOL_RESULT_COMPACTION_NOTICE = "[tool result compacted to preserve context budget]";
|
|
559
|
+
var SYSTEM_PROMPT_TRUNCATION_NOTICE = "\n\n[system prompt truncated to fit prompt budget]";
|
|
560
|
+
var HISTORY_SUMMARY_HEADER = "Conversation summary of earlier context:";
|
|
561
|
+
var HISTORY_SUMMARY_COMPACTION_NOTICE = "\n\n[summary compacted to preserve context continuity]";
|
|
562
|
+
var DEFAULT_SUMMARY_TRIGGER = 12;
|
|
563
|
+
var DEFAULT_SUMMARY_CHUNK_SIZE = 10;
|
|
564
|
+
var DEFAULT_SUMMARY_MAX_CHARS = 1600;
|
|
565
|
+
var SUMMARY_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
566
|
+
"about",
|
|
567
|
+
"after",
|
|
568
|
+
"again",
|
|
569
|
+
"also",
|
|
570
|
+
"because",
|
|
571
|
+
"been",
|
|
572
|
+
"before",
|
|
573
|
+
"being",
|
|
574
|
+
"could",
|
|
575
|
+
"from",
|
|
576
|
+
"have",
|
|
577
|
+
"into",
|
|
578
|
+
"just",
|
|
579
|
+
"more",
|
|
580
|
+
"need",
|
|
581
|
+
"only",
|
|
582
|
+
"over",
|
|
583
|
+
"same",
|
|
584
|
+
"some",
|
|
585
|
+
"than",
|
|
586
|
+
"that",
|
|
587
|
+
"their",
|
|
588
|
+
"them",
|
|
589
|
+
"then",
|
|
590
|
+
"there",
|
|
591
|
+
"these",
|
|
592
|
+
"they",
|
|
593
|
+
"this",
|
|
594
|
+
"those",
|
|
595
|
+
"through",
|
|
596
|
+
"under",
|
|
597
|
+
"very",
|
|
598
|
+
"want",
|
|
599
|
+
"were",
|
|
600
|
+
"what",
|
|
601
|
+
"when",
|
|
602
|
+
"where",
|
|
603
|
+
"which",
|
|
604
|
+
"while",
|
|
605
|
+
"with",
|
|
606
|
+
"would",
|
|
607
|
+
"your"
|
|
608
|
+
]);
|
|
609
|
+
function clampRatio(value, fallback) {
|
|
610
|
+
if (!Number.isFinite(value)) {
|
|
611
|
+
return fallback;
|
|
612
|
+
}
|
|
613
|
+
return Math.min(1, Math.max(0, value));
|
|
614
|
+
}
|
|
615
|
+
function unique(values) {
|
|
616
|
+
return [...new Set(values)];
|
|
617
|
+
}
|
|
618
|
+
function stringifyContent(value) {
|
|
619
|
+
if (typeof value === "string") {
|
|
620
|
+
return value;
|
|
621
|
+
}
|
|
622
|
+
if (value == null) {
|
|
623
|
+
return "";
|
|
624
|
+
}
|
|
625
|
+
try {
|
|
626
|
+
return JSON.stringify(value);
|
|
627
|
+
} catch {
|
|
628
|
+
return String(value);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function normalizeWhitespace(text) {
|
|
632
|
+
return text.replace(/\s+/g, " ").trim();
|
|
633
|
+
}
|
|
634
|
+
function abbreviateText(text, maxChars = 220) {
|
|
635
|
+
const normalized = normalizeWhitespace(text);
|
|
636
|
+
if (!normalized) {
|
|
637
|
+
return "";
|
|
638
|
+
}
|
|
639
|
+
if (normalized.length <= maxChars) {
|
|
640
|
+
return normalized;
|
|
641
|
+
}
|
|
642
|
+
return `${normalized.slice(0, Math.max(1, maxChars - 3)).trimEnd()}...`;
|
|
643
|
+
}
|
|
644
|
+
function tokenize(text) {
|
|
645
|
+
return text.toLowerCase().replace(/[^a-z0-9_\s-]/g, " ").split(/\s+/).filter((token) => token.length > 1);
|
|
646
|
+
}
|
|
647
|
+
function estimateTokens(text, charsPerToken = DEFAULT_CHARS_PER_TOKEN) {
|
|
648
|
+
if (!text) {
|
|
649
|
+
return 0;
|
|
650
|
+
}
|
|
651
|
+
return Math.ceil(text.length / Math.max(1, charsPerToken));
|
|
652
|
+
}
|
|
653
|
+
function estimateMessageTokens(message, charsPerToken = DEFAULT_CHARS_PER_TOKEN) {
|
|
654
|
+
const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content ?? "");
|
|
655
|
+
const toolCalls = message.tool_calls ? JSON.stringify(message.tool_calls) : "";
|
|
656
|
+
const attachments = message.attachments ? JSON.stringify(message.attachments) : "";
|
|
657
|
+
return estimateTokens(
|
|
658
|
+
`${message.role}
|
|
659
|
+
${content}
|
|
660
|
+
${toolCalls}
|
|
661
|
+
${attachments}`,
|
|
662
|
+
charsPerToken
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
function estimateToolTokens(tool2, charsPerToken = DEFAULT_CHARS_PER_TOKEN) {
|
|
666
|
+
return estimateTokens(JSON.stringify(tool2), charsPerToken);
|
|
667
|
+
}
|
|
668
|
+
function buildToolQuery(messages) {
|
|
669
|
+
return messages.filter(
|
|
670
|
+
(message) => message.role === "user" || message.role === "assistant"
|
|
671
|
+
).slice(-3).map((message) => message.content).filter(Boolean).join(" ");
|
|
672
|
+
}
|
|
673
|
+
function matchesSelector(tool2, selector, activeProfile) {
|
|
674
|
+
const normalized = selector.trim().toLowerCase();
|
|
675
|
+
if (!normalized) {
|
|
676
|
+
return false;
|
|
677
|
+
}
|
|
678
|
+
if (normalized === "*" || normalized === "all") {
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
if (normalized === tool2.name.toLowerCase()) {
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
if (normalized.startsWith("group:")) {
|
|
685
|
+
return (tool2.group ?? "").toLowerCase() === normalized.slice(6);
|
|
686
|
+
}
|
|
687
|
+
if (normalized.startsWith("category:")) {
|
|
688
|
+
return (tool2.category ?? "").toLowerCase() === normalized.slice(9);
|
|
689
|
+
}
|
|
690
|
+
if (normalized.startsWith("profile:")) {
|
|
691
|
+
return (tool2.profiles ?? []).map((value) => value.toLowerCase()).includes(normalized.slice(8));
|
|
692
|
+
}
|
|
693
|
+
if (activeProfile && normalized === activeProfile.toLowerCase()) {
|
|
694
|
+
return (tool2.profiles ?? []).map((value) => value.toLowerCase()).includes(normalized);
|
|
695
|
+
}
|
|
696
|
+
return false;
|
|
697
|
+
}
|
|
698
|
+
function scoreTool(tool2, queryTokens, activeProfile) {
|
|
699
|
+
const haystack = [
|
|
700
|
+
tool2.name,
|
|
701
|
+
tool2.description,
|
|
702
|
+
tool2.category,
|
|
703
|
+
tool2.group,
|
|
704
|
+
...tool2.profiles ?? [],
|
|
705
|
+
...tool2.searchKeywords ?? []
|
|
706
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
707
|
+
let score = tool2.deferLoading ? 0 : 2;
|
|
708
|
+
if (activeProfile && tool2.profiles?.includes(activeProfile)) {
|
|
709
|
+
score += 2;
|
|
710
|
+
}
|
|
711
|
+
for (const token of queryTokens) {
|
|
712
|
+
if (tool2.name.toLowerCase() === token) {
|
|
713
|
+
score += 6;
|
|
714
|
+
} else if (tool2.name.toLowerCase().includes(token)) {
|
|
715
|
+
score += 4;
|
|
716
|
+
} else if (haystack.includes(token)) {
|
|
717
|
+
score += 2;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return score;
|
|
721
|
+
}
|
|
722
|
+
function truncateText(text, maxChars, strategy, notice = TOOL_RESULT_TRUNCATION_NOTICE) {
|
|
723
|
+
if (text.length <= maxChars) {
|
|
724
|
+
return text;
|
|
725
|
+
}
|
|
726
|
+
const bodyBudget = Math.max(1, maxChars - notice.length);
|
|
727
|
+
if (strategy === "head") {
|
|
728
|
+
return text.slice(0, bodyBudget) + notice;
|
|
729
|
+
}
|
|
730
|
+
if (strategy === "head-tail" || strategy === "smart") {
|
|
731
|
+
const tailLooksImportant = strategy === "smart" ? /\b(error|exception|failed|traceback|summary|result|done|complete)\b/i.test(
|
|
732
|
+
text.slice(-2e3)
|
|
733
|
+
) : true;
|
|
734
|
+
if (tailLooksImportant && bodyBudget > 32) {
|
|
735
|
+
const tailBudget = Math.min(Math.floor(bodyBudget * 0.3), 4e3);
|
|
736
|
+
const headBudget = Math.max(1, bodyBudget - tailBudget - 32);
|
|
737
|
+
return text.slice(0, headBudget) + "\n\n[... omitted ...]\n\n" + text.slice(-tailBudget) + notice;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return text.slice(0, bodyBudget) + notice;
|
|
741
|
+
}
|
|
742
|
+
function isHistorySummaryMessage(message) {
|
|
743
|
+
return message.role === "system" && typeof message.content === "string" && message.content.startsWith(HISTORY_SUMMARY_HEADER);
|
|
744
|
+
}
|
|
745
|
+
function collectTopKeywords(messages) {
|
|
746
|
+
const counts = /* @__PURE__ */ new Map();
|
|
747
|
+
for (const message of messages) {
|
|
748
|
+
if (message.role !== "user") {
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
for (const token of tokenize(stringifyContent(message.content))) {
|
|
752
|
+
if (token.length < 3 || SUMMARY_STOP_WORDS.has(token)) {
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
return [...counts.entries()].sort((left, right) => {
|
|
759
|
+
const countDiff = right[1] - left[1];
|
|
760
|
+
if (countDiff !== 0) {
|
|
761
|
+
return countDiff;
|
|
762
|
+
}
|
|
763
|
+
return left[0].localeCompare(right[0]);
|
|
764
|
+
}).slice(0, 5).map(([token]) => token);
|
|
765
|
+
}
|
|
766
|
+
function collectToolCallNames(messages) {
|
|
767
|
+
const toolCallNames = /* @__PURE__ */ new Map();
|
|
768
|
+
for (const message of messages) {
|
|
769
|
+
if (message.role !== "assistant" || !message.tool_calls?.length) {
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
for (const toolCall of message.tool_calls) {
|
|
773
|
+
const parsedToolCall = toolCall;
|
|
774
|
+
if (parsedToolCall.id && parsedToolCall.function?.name) {
|
|
775
|
+
toolCallNames.set(parsedToolCall.id, parsedToolCall.function.name);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return toolCallNames;
|
|
780
|
+
}
|
|
781
|
+
function compressSummaryContent(content, maxChars, fallbackBehavior) {
|
|
782
|
+
if (content.length <= maxChars) {
|
|
783
|
+
return content;
|
|
784
|
+
}
|
|
785
|
+
if (fallbackBehavior === "error") {
|
|
786
|
+
throw new Error("History summary exceeded configured continuity budget.");
|
|
787
|
+
}
|
|
788
|
+
if (fallbackBehavior === "statistical") {
|
|
789
|
+
const lines = content.split("\n");
|
|
790
|
+
const retained = lines.filter(
|
|
791
|
+
(line) => /^(Conversation summary|Stats:|- Messages compacted:|- User turns compacted:|- Assistant turns compacted:|- Tool results compacted:|- Latest user request before preserved window:|- Latest assistant response before preserved window:)/.test(
|
|
792
|
+
line
|
|
793
|
+
)
|
|
794
|
+
);
|
|
795
|
+
const statistical = retained.join("\n");
|
|
796
|
+
if (statistical.length <= maxChars) {
|
|
797
|
+
return statistical;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
return truncateText(
|
|
801
|
+
content,
|
|
802
|
+
maxChars,
|
|
803
|
+
"head",
|
|
804
|
+
HISTORY_SUMMARY_COMPACTION_NOTICE
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
function buildHistorySummary(messages, summarization, maxChars = DEFAULT_SUMMARY_MAX_CHARS) {
|
|
808
|
+
if (messages.length === 0) {
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
811
|
+
if (summarization?.enabled === false) {
|
|
812
|
+
return null;
|
|
813
|
+
}
|
|
814
|
+
const previousSummaries = messages.filter(isHistorySummaryMessage);
|
|
815
|
+
const rawMessages = messages.filter(
|
|
816
|
+
(message) => !isHistorySummaryMessage(message)
|
|
817
|
+
);
|
|
818
|
+
const focusWindowSize = Math.max(
|
|
819
|
+
1,
|
|
820
|
+
summarization?.chunkSize ?? DEFAULT_SUMMARY_CHUNK_SIZE
|
|
821
|
+
);
|
|
822
|
+
const detailedThreshold = Math.max(
|
|
823
|
+
1,
|
|
824
|
+
summarization?.triggerAt ?? DEFAULT_SUMMARY_TRIGGER
|
|
825
|
+
);
|
|
826
|
+
const focusMessages = rawMessages.slice(-focusWindowSize);
|
|
827
|
+
const userMessages = rawMessages.filter((message) => message.role === "user");
|
|
828
|
+
const assistantMessages = rawMessages.filter(
|
|
829
|
+
(message) => message.role === "assistant"
|
|
830
|
+
);
|
|
831
|
+
const toolMessages = rawMessages.filter((message) => message.role === "tool");
|
|
832
|
+
const recentUser = abbreviateText(stringifyContent(userMessages.at(-1)?.content), 240) || "n/a";
|
|
833
|
+
const recentAssistant = abbreviateText(stringifyContent(assistantMessages.at(-1)?.content), 240) || "n/a";
|
|
834
|
+
const recentUserGoals = userMessages.slice(-3).map((message) => abbreviateText(stringifyContent(message.content), 180)).filter(Boolean);
|
|
835
|
+
const recentAssistantNotes = assistantMessages.map((message) => abbreviateText(stringifyContent(message.content), 180)).filter(Boolean).slice(-2);
|
|
836
|
+
const toolCallNames = collectToolCallNames(rawMessages);
|
|
837
|
+
const toolActivity = unique([
|
|
838
|
+
...focusMessages.filter((message) => message.role === "assistant").flatMap(
|
|
839
|
+
(message) => (message.tool_calls ?? []).map((toolCall) => {
|
|
840
|
+
const parsedToolCall = toolCall;
|
|
841
|
+
return parsedToolCall.function?.name;
|
|
842
|
+
}).filter((name) => Boolean(name))
|
|
843
|
+
),
|
|
844
|
+
...toolMessages.slice(-2).map((message) => {
|
|
845
|
+
const toolName = message.tool_call_id ? toolCallNames.get(message.tool_call_id) : void 0;
|
|
846
|
+
const snippet = abbreviateText(stringifyContent(message.content), 120);
|
|
847
|
+
return toolName && snippet ? `${toolName}: ${snippet}` : toolName ?? snippet;
|
|
848
|
+
}).filter(Boolean)
|
|
849
|
+
]).slice(-4);
|
|
850
|
+
const priorSummaryCarryForward = previousSummaries.map(
|
|
851
|
+
(message) => abbreviateText(
|
|
852
|
+
stringifyContent(message.content).replace(
|
|
853
|
+
`${HISTORY_SUMMARY_HEADER}
|
|
854
|
+
`,
|
|
855
|
+
""
|
|
856
|
+
),
|
|
857
|
+
180
|
|
858
|
+
)
|
|
859
|
+
).filter(Boolean).slice(-2);
|
|
860
|
+
const topKeywords = rawMessages.length >= detailedThreshold ? collectTopKeywords(
|
|
861
|
+
focusMessages.length > 0 ? focusMessages : rawMessages
|
|
862
|
+
) : [];
|
|
863
|
+
const lines = [
|
|
864
|
+
HISTORY_SUMMARY_HEADER,
|
|
865
|
+
"Stats:",
|
|
866
|
+
`- Messages compacted: ${messages.length}`,
|
|
867
|
+
`- User turns compacted: ${userMessages.length}`,
|
|
868
|
+
`- Assistant turns compacted: ${assistantMessages.length}`,
|
|
869
|
+
`- Tool results compacted: ${toolMessages.length}`
|
|
870
|
+
];
|
|
871
|
+
if (previousSummaries.length > 0) {
|
|
872
|
+
lines.push(`- Previous summaries merged: ${previousSummaries.length}`);
|
|
873
|
+
}
|
|
874
|
+
if (rawMessages.length > focusMessages.length) {
|
|
875
|
+
lines.push(
|
|
876
|
+
`- Older compacted messages outside the detailed window: ${rawMessages.length - focusMessages.length}`
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
if (topKeywords.length > 0) {
|
|
880
|
+
lines.push(`- Recurring user topics: ${topKeywords.join(", ")}`);
|
|
881
|
+
}
|
|
882
|
+
if (recentUser !== "n/a") {
|
|
883
|
+
lines.push(`- Latest user request before preserved window: ${recentUser}`);
|
|
884
|
+
}
|
|
885
|
+
if (recentAssistant !== "n/a") {
|
|
886
|
+
lines.push(
|
|
887
|
+
`- Latest assistant response before preserved window: ${recentAssistant}`
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
if (recentUserGoals.length > 0) {
|
|
891
|
+
lines.push("Carry forward user goals:");
|
|
892
|
+
for (const goal of recentUserGoals) {
|
|
893
|
+
lines.push(`- ${goal}`);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
if (recentAssistantNotes.length > 0) {
|
|
897
|
+
lines.push("Carry forward assistant commitments:");
|
|
898
|
+
for (const note of recentAssistantNotes) {
|
|
899
|
+
lines.push(`- ${note}`);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (toolActivity.length > 0) {
|
|
903
|
+
lines.push("Recent tool activity:");
|
|
904
|
+
for (const item of toolActivity) {
|
|
905
|
+
lines.push(`- ${item}`);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (priorSummaryCarryForward.length > 0) {
|
|
909
|
+
lines.push("Earlier carried-forward context:");
|
|
910
|
+
for (const item of priorSummaryCarryForward) {
|
|
911
|
+
lines.push(`- ${item}`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const content = compressSummaryContent(
|
|
915
|
+
lines.join("\n"),
|
|
916
|
+
maxChars,
|
|
917
|
+
summarization?.fallbackBehavior
|
|
918
|
+
);
|
|
919
|
+
return {
|
|
920
|
+
role: "system",
|
|
921
|
+
content
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
function buildToolDefinitions(selectedTools) {
|
|
925
|
+
if (selectedTools.length === 0) {
|
|
926
|
+
return void 0;
|
|
927
|
+
}
|
|
928
|
+
return selectedTools.map((tool2) => ({
|
|
929
|
+
name: tool2.name,
|
|
930
|
+
description: tool2.description,
|
|
931
|
+
category: tool2.category,
|
|
932
|
+
group: tool2.group,
|
|
933
|
+
deferLoading: tool2.deferLoading,
|
|
934
|
+
profiles: tool2.profiles,
|
|
935
|
+
searchKeywords: tool2.searchKeywords,
|
|
936
|
+
inputSchema: tool2.inputSchema
|
|
937
|
+
}));
|
|
938
|
+
}
|
|
939
|
+
function resolveTruncationConfig(params) {
|
|
940
|
+
const charsPerToken = params.config?.contextManagement?.tokenEstimation?.charsPerToken ?? DEFAULT_CHARS_PER_TOKEN;
|
|
941
|
+
const contextWindowTokens = params.config?.contextBudget?.budget?.contextWindowTokens;
|
|
942
|
+
const globalConfig = params.config?.toolResultConfig?.truncation;
|
|
943
|
+
const perToolConfig = params.tool?.resultConfig?.truncation;
|
|
944
|
+
const merged = { ...globalConfig, ...perToolConfig };
|
|
945
|
+
const hardMaxChars = merged.hardMaxChars ?? (contextWindowTokens ? Math.floor(
|
|
946
|
+
contextWindowTokens * clampRatio(
|
|
947
|
+
merged.maxContextShare,
|
|
948
|
+
DEFAULT_MAX_TOOL_RESULT_CONTEXT_SHARE
|
|
949
|
+
) * charsPerToken
|
|
950
|
+
) : DEFAULT_TOOL_RESULT_HARD_MAX_CHARS);
|
|
951
|
+
return {
|
|
952
|
+
enabled: merged.enabled ?? true,
|
|
953
|
+
maxContextShare: clampRatio(
|
|
954
|
+
merged.maxContextShare,
|
|
955
|
+
DEFAULT_MAX_TOOL_RESULT_CONTEXT_SHARE
|
|
956
|
+
),
|
|
957
|
+
hardMaxChars: Math.max(1, hardMaxChars),
|
|
958
|
+
minKeepChars: Math.max(
|
|
959
|
+
256,
|
|
960
|
+
merged.minKeepChars ?? DEFAULT_TOOL_RESULT_MIN_KEEP_CHARS
|
|
961
|
+
),
|
|
962
|
+
strategy: merged.strategy ?? DEFAULT_TOOL_RESULT_STRATEGY,
|
|
963
|
+
preserveErrors: merged.preserveErrors ?? true
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
function buildToolResultContent(result, tool2, args) {
|
|
967
|
+
if (typeof result === "string") {
|
|
968
|
+
return result;
|
|
969
|
+
}
|
|
970
|
+
const typedResult = result ?? null;
|
|
550
971
|
const responseMode = typedResult?._aiResponseMode ?? tool2?.aiResponseMode ?? "full";
|
|
551
972
|
if (typedResult?._aiContent) {
|
|
552
973
|
return JSON.stringify(typedResult._aiContent);
|
|
553
974
|
}
|
|
554
975
|
let aiContext = typedResult?._aiContext;
|
|
555
976
|
if (!aiContext && tool2?.aiContext) {
|
|
556
|
-
aiContext = typeof tool2.aiContext === "function" ? tool2.aiContext(
|
|
977
|
+
aiContext = typeof tool2.aiContext === "function" ? tool2.aiContext(
|
|
978
|
+
typedResult ?? { success: true },
|
|
979
|
+
args ?? {}
|
|
980
|
+
) : tool2.aiContext;
|
|
557
981
|
}
|
|
558
982
|
switch (responseMode) {
|
|
559
983
|
case "none":
|
|
@@ -561,7 +985,7 @@ function buildToolResultContentForAI(result, tool2, args) {
|
|
|
561
985
|
case "brief":
|
|
562
986
|
return aiContext ?? "[Tool executed successfully]";
|
|
563
987
|
case "full":
|
|
564
|
-
default:
|
|
988
|
+
default: {
|
|
565
989
|
if (aiContext) {
|
|
566
990
|
const {
|
|
567
991
|
_aiResponseMode,
|
|
@@ -579,18 +1003,526 @@ Full data: ${JSON.stringify(dataOnly)}`;
|
|
|
579
1003
|
return JSON.stringify(dataOnly);
|
|
580
1004
|
}
|
|
581
1005
|
return JSON.stringify(result);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
function buildToolResultContentForPrompt(result, tool2, args, config) {
|
|
1010
|
+
const text = buildToolResultContent(result, tool2, args);
|
|
1011
|
+
const truncation = resolveTruncationConfig({ tool: tool2, config });
|
|
1012
|
+
if (!truncation.enabled) {
|
|
1013
|
+
return text;
|
|
1014
|
+
}
|
|
1015
|
+
if (truncation.preserveErrors && typeof result === "object" && result !== null && "error" in result && typeof result.error === "string") {
|
|
1016
|
+
return text;
|
|
1017
|
+
}
|
|
1018
|
+
const maxChars = Math.max(truncation.minKeepChars, truncation.hardMaxChars);
|
|
1019
|
+
return truncateText(text, maxChars, truncation.strategy);
|
|
1020
|
+
}
|
|
1021
|
+
function sliceHistoryToMaxMessages(params) {
|
|
1022
|
+
const { historyMessages, maxMessages, pruneStrategy, summarization } = params;
|
|
1023
|
+
if (!maxMessages || historyMessages.length <= maxMessages) {
|
|
1024
|
+
return historyMessages;
|
|
1025
|
+
}
|
|
1026
|
+
const dropped = historyMessages.slice(
|
|
1027
|
+
0,
|
|
1028
|
+
historyMessages.length - maxMessages
|
|
1029
|
+
);
|
|
1030
|
+
const kept = historyMessages.slice(-maxMessages);
|
|
1031
|
+
if (pruneStrategy === "summarize") {
|
|
1032
|
+
const summary = buildHistorySummary(dropped, summarization);
|
|
1033
|
+
return summary ? [summary, ...kept] : kept;
|
|
1034
|
+
}
|
|
1035
|
+
return kept;
|
|
1036
|
+
}
|
|
1037
|
+
function compactHistoryToTokenBudget(params) {
|
|
1038
|
+
const {
|
|
1039
|
+
maxTokens,
|
|
1040
|
+
preserveRecent,
|
|
1041
|
+
charsPerToken,
|
|
1042
|
+
pruneStrategy,
|
|
1043
|
+
summarization
|
|
1044
|
+
} = params;
|
|
1045
|
+
let historyMessages = params.historyMessages;
|
|
1046
|
+
if (!maxTokens) {
|
|
1047
|
+
return historyMessages;
|
|
1048
|
+
}
|
|
1049
|
+
const getHistoryTokens = () => historyMessages.reduce(
|
|
1050
|
+
(sum, message) => sum + estimateMessageTokens(message, charsPerToken),
|
|
1051
|
+
0
|
|
1052
|
+
);
|
|
1053
|
+
while (historyMessages.length > 1 && getHistoryTokens() > maxTokens) {
|
|
1054
|
+
const prunableCount = Math.max(0, historyMessages.length - preserveRecent);
|
|
1055
|
+
if (prunableCount <= 0) {
|
|
1056
|
+
const firstMessage = historyMessages[0];
|
|
1057
|
+
if (isHistorySummaryMessage(firstMessage) && typeof firstMessage.content === "string") {
|
|
1058
|
+
const compactedSummary = compressSummaryContent(
|
|
1059
|
+
firstMessage.content,
|
|
1060
|
+
Math.max(400, Math.floor(maxTokens * charsPerToken * 0.25)),
|
|
1061
|
+
summarization?.fallbackBehavior
|
|
1062
|
+
);
|
|
1063
|
+
if (compactedSummary !== firstMessage.content) {
|
|
1064
|
+
historyMessages = [
|
|
1065
|
+
{ ...firstMessage, content: compactedSummary },
|
|
1066
|
+
...historyMessages.slice(1)
|
|
1067
|
+
];
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
historyMessages = historyMessages.slice(1);
|
|
1072
|
+
continue;
|
|
1073
|
+
}
|
|
1074
|
+
const pruned = historyMessages.slice(0, prunableCount);
|
|
1075
|
+
const kept = historyMessages.slice(prunableCount);
|
|
1076
|
+
if (pruneStrategy === "summarize") {
|
|
1077
|
+
const summary = buildHistorySummary(
|
|
1078
|
+
pruned,
|
|
1079
|
+
summarization,
|
|
1080
|
+
Math.max(500, Math.floor(maxTokens * charsPerToken * 0.35))
|
|
1081
|
+
);
|
|
1082
|
+
historyMessages = summary ? [summary, ...kept] : kept;
|
|
1083
|
+
} else {
|
|
1084
|
+
historyMessages = kept;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
return historyMessages;
|
|
1088
|
+
}
|
|
1089
|
+
function compactToolResultsToBudget(params) {
|
|
1090
|
+
let toolResultMessages = params.toolResultMessages;
|
|
1091
|
+
if (!params.maxTokens) {
|
|
1092
|
+
return toolResultMessages;
|
|
1093
|
+
}
|
|
1094
|
+
const getToolResultTokens = () => toolResultMessages.reduce(
|
|
1095
|
+
(sum, message) => sum + estimateMessageTokens(message, params.charsPerToken),
|
|
1096
|
+
0
|
|
1097
|
+
);
|
|
1098
|
+
while (toolResultMessages.length > 0 && getToolResultTokens() > params.maxTokens) {
|
|
1099
|
+
const index = toolResultMessages.findIndex(
|
|
1100
|
+
(message) => message.content !== TOOL_RESULT_COMPACTION_NOTICE
|
|
1101
|
+
);
|
|
1102
|
+
if (index === -1) {
|
|
1103
|
+
break;
|
|
1104
|
+
}
|
|
1105
|
+
toolResultMessages = toolResultMessages.map(
|
|
1106
|
+
(message, currentIndex) => currentIndex === index ? { ...message, content: TOOL_RESULT_COMPACTION_NOTICE } : message
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
return toolResultMessages;
|
|
1110
|
+
}
|
|
1111
|
+
function fitToolsToBudget(params) {
|
|
1112
|
+
let tools = params.tools;
|
|
1113
|
+
if (!tools?.length || !params.maxTokens) {
|
|
1114
|
+
return tools;
|
|
582
1115
|
}
|
|
1116
|
+
const getToolTokens = () => tools.reduce(
|
|
1117
|
+
(sum, tool2) => sum + estimateToolTokens(tool2, params.charsPerToken),
|
|
1118
|
+
0
|
|
1119
|
+
);
|
|
1120
|
+
while (tools.length > 0 && getToolTokens() > params.maxTokens) {
|
|
1121
|
+
tools = tools.slice(0, -1);
|
|
1122
|
+
}
|
|
1123
|
+
return tools;
|
|
1124
|
+
}
|
|
1125
|
+
function truncateSystemPromptToBudget(params) {
|
|
1126
|
+
const { systemPrompt, maxTokens, charsPerToken } = params;
|
|
1127
|
+
if (!systemPrompt || !maxTokens) {
|
|
1128
|
+
return systemPrompt;
|
|
1129
|
+
}
|
|
1130
|
+
const maxChars = maxTokens * charsPerToken;
|
|
1131
|
+
if (systemPrompt.length <= maxChars) {
|
|
1132
|
+
return systemPrompt;
|
|
1133
|
+
}
|
|
1134
|
+
return truncateText(
|
|
1135
|
+
systemPrompt,
|
|
1136
|
+
maxChars,
|
|
1137
|
+
"head",
|
|
1138
|
+
SYSTEM_PROMPT_TRUNCATION_NOTICE
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
function calculateBuckets(params) {
|
|
1142
|
+
const systemPromptTokens = estimateTokens(
|
|
1143
|
+
params.systemPrompt ?? "",
|
|
1144
|
+
params.charsPerToken
|
|
1145
|
+
);
|
|
1146
|
+
const historyTokens = params.historyMessages.reduce(
|
|
1147
|
+
(sum, message) => sum + estimateMessageTokens(message, params.charsPerToken),
|
|
1148
|
+
0
|
|
1149
|
+
);
|
|
1150
|
+
const toolResultsTokens = params.toolResultMessages.reduce(
|
|
1151
|
+
(sum, message) => sum + estimateMessageTokens(message, params.charsPerToken),
|
|
1152
|
+
0
|
|
1153
|
+
);
|
|
1154
|
+
const toolDefinitionTokens = (params.requestTools ?? []).reduce(
|
|
1155
|
+
(sum, tool2) => sum + estimateToolTokens(tool2, params.charsPerToken),
|
|
1156
|
+
0
|
|
1157
|
+
);
|
|
1158
|
+
const total = systemPromptTokens + historyTokens + toolResultsTokens + toolDefinitionTokens;
|
|
1159
|
+
const budget = Number.isFinite(params.availableBudget) ? params.availableBudget : total;
|
|
1160
|
+
const toPart = (tokens) => ({
|
|
1161
|
+
tokens,
|
|
1162
|
+
percent: budget > 0 ? Number((tokens / budget * 100).toFixed(2)) : 0
|
|
1163
|
+
});
|
|
1164
|
+
return {
|
|
1165
|
+
total: toPart(total),
|
|
1166
|
+
breakdown: {
|
|
1167
|
+
systemPrompt: toPart(systemPromptTokens),
|
|
1168
|
+
history: toPart(historyTokens),
|
|
1169
|
+
toolResults: toPart(toolResultsTokens),
|
|
1170
|
+
tools: toPart(toolDefinitionTokens)
|
|
1171
|
+
},
|
|
1172
|
+
budget: {
|
|
1173
|
+
available: budget,
|
|
1174
|
+
remaining: Math.max(0, budget - total)
|
|
1175
|
+
},
|
|
1176
|
+
warnings: unique(params.warnings)
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
function mergeBucketsInOriginalOrder(params) {
|
|
1180
|
+
const historyQueue = [...params.historyMessages];
|
|
1181
|
+
const toolQueue = [...params.toolResultMessages];
|
|
1182
|
+
return params.transformedMessages.flatMap((message) => {
|
|
1183
|
+
if (message.role === "tool") {
|
|
1184
|
+
const nextTool = toolQueue.shift();
|
|
1185
|
+
return nextTool ? [nextTool] : [];
|
|
1186
|
+
}
|
|
1187
|
+
const nextHistory = historyQueue.shift();
|
|
1188
|
+
return nextHistory ? [nextHistory] : [];
|
|
1189
|
+
});
|
|
583
1190
|
}
|
|
1191
|
+
var ChatContextOptimizer = class {
|
|
1192
|
+
constructor(config) {
|
|
1193
|
+
this.lastContextUsage = null;
|
|
1194
|
+
this.config = config;
|
|
1195
|
+
this.activeProfile = config?.toolProfiles?.defaultProfile;
|
|
1196
|
+
}
|
|
1197
|
+
updateConfig(config) {
|
|
1198
|
+
this.config = config;
|
|
1199
|
+
if (!this.activeProfile) {
|
|
1200
|
+
this.activeProfile = config?.toolProfiles?.defaultProfile;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
setActiveProfile(profile) {
|
|
1204
|
+
this.activeProfile = profile?.trim() || void 0;
|
|
1205
|
+
}
|
|
1206
|
+
getContextUsage() {
|
|
1207
|
+
return this.lastContextUsage;
|
|
1208
|
+
}
|
|
1209
|
+
prepare(params) {
|
|
1210
|
+
const charsPerToken = this.config?.contextManagement?.tokenEstimation?.charsPerToken ?? DEFAULT_CHARS_PER_TOKEN;
|
|
1211
|
+
const safetyMargin = this.config?.contextManagement?.tokenEstimation?.safetyMargin ?? DEFAULT_SAFETY_MARGIN;
|
|
1212
|
+
const warnings = [];
|
|
1213
|
+
const contextManagement = this.config?.contextManagement;
|
|
1214
|
+
const contextBudget = this.config?.contextBudget;
|
|
1215
|
+
const allTools = params.tools ?? [];
|
|
1216
|
+
const selectedTools = this.selectTools(allTools, params.messages);
|
|
1217
|
+
const transformedMessages = this.transformMessages(
|
|
1218
|
+
params.messages,
|
|
1219
|
+
allTools
|
|
1220
|
+
);
|
|
1221
|
+
const preserveRecent = contextManagement?.summarization?.preserveRecent ?? DEFAULT_RECENT_HISTORY_PRESERVE;
|
|
1222
|
+
let buckets = {
|
|
1223
|
+
systemPrompt: params.systemPrompt,
|
|
1224
|
+
transformedMessages,
|
|
1225
|
+
historyMessages: transformedMessages.filter(
|
|
1226
|
+
(message) => message.role !== "tool"
|
|
1227
|
+
),
|
|
1228
|
+
toolResultMessages: transformedMessages.filter(
|
|
1229
|
+
(message) => message.role === "tool"
|
|
1230
|
+
),
|
|
1231
|
+
requestTools: buildToolDefinitions(selectedTools)
|
|
1232
|
+
};
|
|
1233
|
+
if (contextManagement?.enabled) {
|
|
1234
|
+
buckets.historyMessages = sliceHistoryToMaxMessages({
|
|
1235
|
+
historyMessages: buckets.historyMessages,
|
|
1236
|
+
maxMessages: contextManagement.history?.maxMessages,
|
|
1237
|
+
pruneStrategy: contextManagement.history?.pruneStrategy,
|
|
1238
|
+
summarization: contextManagement?.summarization
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
const budgetConfig = contextBudget?.budget;
|
|
1242
|
+
const contextWindowTokens = budgetConfig?.contextWindowTokens;
|
|
1243
|
+
const inputHeadroomRatio = clampRatio(
|
|
1244
|
+
budgetConfig?.inputHeadroomRatio,
|
|
1245
|
+
DEFAULT_INPUT_HEADROOM_RATIO
|
|
1246
|
+
);
|
|
1247
|
+
const availableBudget = contextWindowTokens ? Math.max(1, Math.floor(contextWindowTokens * inputHeadroomRatio)) : Number.POSITIVE_INFINITY;
|
|
1248
|
+
const sharedBudget = Number.isFinite(availableBudget) ? availableBudget : void 0;
|
|
1249
|
+
const systemPromptBudget = sharedBudget ? Math.max(
|
|
1250
|
+
1,
|
|
1251
|
+
Math.floor(
|
|
1252
|
+
sharedBudget * clampRatio(
|
|
1253
|
+
budgetConfig?.systemPromptShare,
|
|
1254
|
+
DEFAULT_SYSTEM_PROMPT_SHARE
|
|
1255
|
+
)
|
|
1256
|
+
)
|
|
1257
|
+
) : void 0;
|
|
1258
|
+
const historyBudgetByShare = sharedBudget ? Math.max(
|
|
1259
|
+
1,
|
|
1260
|
+
Math.floor(
|
|
1261
|
+
sharedBudget * clampRatio(budgetConfig?.historyShare, DEFAULT_HISTORY_SHARE)
|
|
1262
|
+
)
|
|
1263
|
+
) : void 0;
|
|
1264
|
+
const historyBudgetByConfig = contextManagement?.enabled && contextManagement.history?.maxTokens ? Math.floor(contextManagement.history.maxTokens / safetyMargin) : void 0;
|
|
1265
|
+
const historyBudget = historyBudgetByShare && historyBudgetByConfig ? Math.min(historyBudgetByShare, historyBudgetByConfig) : historyBudgetByShare ?? historyBudgetByConfig;
|
|
1266
|
+
const toolResultsBudget = sharedBudget ? Math.max(
|
|
1267
|
+
1,
|
|
1268
|
+
Math.floor(
|
|
1269
|
+
sharedBudget * clampRatio(
|
|
1270
|
+
budgetConfig?.toolResultsShare,
|
|
1271
|
+
DEFAULT_TOOL_RESULTS_SHARE
|
|
1272
|
+
)
|
|
1273
|
+
)
|
|
1274
|
+
) : void 0;
|
|
1275
|
+
const toolDefinitionsBudget = sharedBudget ? Math.max(
|
|
1276
|
+
1,
|
|
1277
|
+
Math.floor(
|
|
1278
|
+
sharedBudget * clampRatio(
|
|
1279
|
+
budgetConfig?.toolDefinitionsShare,
|
|
1280
|
+
DEFAULT_TOOL_DEFINITIONS_SHARE
|
|
1281
|
+
)
|
|
1282
|
+
)
|
|
1283
|
+
) : void 0;
|
|
1284
|
+
if (contextBudget?.enabled) {
|
|
1285
|
+
buckets.systemPrompt = truncateSystemPromptToBudget({
|
|
1286
|
+
systemPrompt: buckets.systemPrompt,
|
|
1287
|
+
maxTokens: systemPromptBudget,
|
|
1288
|
+
charsPerToken
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
buckets.historyMessages = compactHistoryToTokenBudget({
|
|
1292
|
+
historyMessages: buckets.historyMessages,
|
|
1293
|
+
maxTokens: historyBudget,
|
|
1294
|
+
preserveRecent,
|
|
1295
|
+
charsPerToken,
|
|
1296
|
+
pruneStrategy: contextManagement?.history?.pruneStrategy,
|
|
1297
|
+
summarization: contextManagement?.summarization
|
|
1298
|
+
});
|
|
1299
|
+
buckets.toolResultMessages = compactToolResultsToBudget({
|
|
1300
|
+
toolResultMessages: buckets.toolResultMessages,
|
|
1301
|
+
maxTokens: toolResultsBudget,
|
|
1302
|
+
charsPerToken
|
|
1303
|
+
});
|
|
1304
|
+
buckets.requestTools = fitToolsToBudget({
|
|
1305
|
+
tools: buckets.requestTools,
|
|
1306
|
+
maxTokens: toolDefinitionsBudget,
|
|
1307
|
+
charsPerToken
|
|
1308
|
+
});
|
|
1309
|
+
let usage = calculateBuckets({
|
|
1310
|
+
...buckets,
|
|
1311
|
+
charsPerToken,
|
|
1312
|
+
availableBudget,
|
|
1313
|
+
warnings
|
|
1314
|
+
});
|
|
1315
|
+
if (Number.isFinite(availableBudget) && usage.total.tokens > availableBudget) {
|
|
1316
|
+
buckets.toolResultMessages = compactToolResultsToBudget({
|
|
1317
|
+
toolResultMessages: buckets.toolResultMessages,
|
|
1318
|
+
maxTokens: Math.max(
|
|
1319
|
+
1,
|
|
1320
|
+
usage.breakdown.toolResults.tokens - usage.total.tokens + availableBudget
|
|
1321
|
+
),
|
|
1322
|
+
charsPerToken
|
|
1323
|
+
});
|
|
1324
|
+
usage = calculateBuckets({
|
|
1325
|
+
...buckets,
|
|
1326
|
+
charsPerToken,
|
|
1327
|
+
availableBudget,
|
|
1328
|
+
warnings
|
|
1329
|
+
});
|
|
1330
|
+
if (usage.total.tokens > availableBudget) {
|
|
1331
|
+
const overflow = usage.total.tokens - availableBudget;
|
|
1332
|
+
buckets.historyMessages = compactHistoryToTokenBudget({
|
|
1333
|
+
historyMessages: buckets.historyMessages,
|
|
1334
|
+
maxTokens: Math.max(1, usage.breakdown.history.tokens - overflow),
|
|
1335
|
+
preserveRecent,
|
|
1336
|
+
charsPerToken,
|
|
1337
|
+
pruneStrategy: contextManagement?.history?.pruneStrategy
|
|
1338
|
+
});
|
|
1339
|
+
usage = calculateBuckets({
|
|
1340
|
+
...buckets,
|
|
1341
|
+
charsPerToken,
|
|
1342
|
+
availableBudget,
|
|
1343
|
+
warnings
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
if (usage.total.tokens > availableBudget) {
|
|
1347
|
+
buckets.requestTools = fitToolsToBudget({
|
|
1348
|
+
tools: buckets.requestTools,
|
|
1349
|
+
maxTokens: Math.max(
|
|
1350
|
+
1,
|
|
1351
|
+
usage.breakdown.tools.tokens - (usage.total.tokens - availableBudget)
|
|
1352
|
+
),
|
|
1353
|
+
charsPerToken
|
|
1354
|
+
});
|
|
1355
|
+
usage = calculateBuckets({
|
|
1356
|
+
...buckets,
|
|
1357
|
+
charsPerToken,
|
|
1358
|
+
availableBudget,
|
|
1359
|
+
warnings
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (Number.isFinite(availableBudget) && usage.total.tokens > availableBudget) {
|
|
1364
|
+
warnings.push(
|
|
1365
|
+
`Prompt budget exceeded: using ${usage.total.tokens} tokens of ${availableBudget}.`
|
|
1366
|
+
);
|
|
1367
|
+
usage = {
|
|
1368
|
+
...usage,
|
|
1369
|
+
warnings: unique(warnings)
|
|
1370
|
+
};
|
|
1371
|
+
if (contextBudget?.enforcement?.mode === "error") {
|
|
1372
|
+
throw new Error(warnings[warnings.length - 1]);
|
|
1373
|
+
}
|
|
1374
|
+
contextBudget?.enforcement?.onBudgetExceeded?.(usage);
|
|
1375
|
+
} else {
|
|
1376
|
+
usage = {
|
|
1377
|
+
...usage,
|
|
1378
|
+
warnings: unique(warnings)
|
|
1379
|
+
};
|
|
1380
|
+
}
|
|
1381
|
+
contextBudget?.monitoring?.onUsageUpdate?.(usage);
|
|
1382
|
+
this.lastContextUsage = usage;
|
|
1383
|
+
return {
|
|
1384
|
+
messages: mergeBucketsInOriginalOrder(buckets),
|
|
1385
|
+
tools: buckets.requestTools,
|
|
1386
|
+
contextUsage: usage,
|
|
1387
|
+
warnings: usage.warnings
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
selectTools(tools, messages) {
|
|
1391
|
+
if (!tools.length) {
|
|
1392
|
+
return [];
|
|
1393
|
+
}
|
|
1394
|
+
const available = tools.filter((tool2) => tool2.available !== false);
|
|
1395
|
+
const profileConfig = this.config?.toolProfiles;
|
|
1396
|
+
if (!profileConfig?.enabled) {
|
|
1397
|
+
return available;
|
|
1398
|
+
}
|
|
1399
|
+
const activeProfile = this.activeProfile ?? profileConfig.defaultProfile;
|
|
1400
|
+
const includeUnprofiled = profileConfig.includeUnprofiled ?? true;
|
|
1401
|
+
const profile = activeProfile ? profileConfig.profiles?.[activeProfile] : void 0;
|
|
1402
|
+
let filtered = available;
|
|
1403
|
+
if (profile?.include?.length) {
|
|
1404
|
+
filtered = filtered.filter(
|
|
1405
|
+
(tool2) => profile.include.some(
|
|
1406
|
+
(selector) => matchesSelector(tool2, selector, activeProfile)
|
|
1407
|
+
) || !!activeProfile && tool2.profiles?.includes(activeProfile)
|
|
1408
|
+
);
|
|
1409
|
+
} else if (activeProfile) {
|
|
1410
|
+
filtered = filtered.filter((tool2) => {
|
|
1411
|
+
if (tool2.profiles?.length) {
|
|
1412
|
+
return tool2.profiles.includes(activeProfile);
|
|
1413
|
+
}
|
|
1414
|
+
return includeUnprofiled;
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
if (profile?.exclude?.length) {
|
|
1418
|
+
filtered = filtered.filter(
|
|
1419
|
+
(tool2) => !profile.exclude.some(
|
|
1420
|
+
(selector) => matchesSelector(tool2, selector, activeProfile)
|
|
1421
|
+
)
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
if (!profileConfig.dynamicSelection?.enabled) {
|
|
1425
|
+
return filtered;
|
|
1426
|
+
}
|
|
1427
|
+
const maxTools = Math.max(
|
|
1428
|
+
1,
|
|
1429
|
+
Math.min(
|
|
1430
|
+
profileConfig.dynamicSelection.maxTools ?? filtered.length,
|
|
1431
|
+
filtered.length
|
|
1432
|
+
)
|
|
1433
|
+
);
|
|
1434
|
+
const queryTokens = tokenize(buildToolQuery(messages));
|
|
1435
|
+
return [...filtered].sort((left, right) => {
|
|
1436
|
+
const scoreDiff = scoreTool(right, queryTokens, activeProfile) - scoreTool(left, queryTokens, activeProfile);
|
|
1437
|
+
if (scoreDiff !== 0) {
|
|
1438
|
+
return scoreDiff;
|
|
1439
|
+
}
|
|
1440
|
+
return left.name.localeCompare(right.name);
|
|
1441
|
+
}).slice(0, maxTools);
|
|
1442
|
+
}
|
|
1443
|
+
transformMessages(messages, allTools) {
|
|
1444
|
+
const toolCallMap = /* @__PURE__ */ new Map();
|
|
1445
|
+
for (const message of messages) {
|
|
1446
|
+
if (message.role !== "assistant" || !message.toolCalls?.length) {
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
for (const toolCall of message.toolCalls) {
|
|
1450
|
+
try {
|
|
1451
|
+
toolCallMap.set(toolCall.id, {
|
|
1452
|
+
toolName: toolCall.function.name,
|
|
1453
|
+
args: JSON.parse(toolCall.function.arguments)
|
|
1454
|
+
});
|
|
1455
|
+
} catch {
|
|
1456
|
+
toolCallMap.set(toolCall.id, {
|
|
1457
|
+
toolName: toolCall.function.name,
|
|
1458
|
+
args: {}
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
const toolDefMap = new Map(
|
|
1464
|
+
allTools.map((tool2) => [tool2.name, tool2])
|
|
1465
|
+
);
|
|
1466
|
+
return messages.map((message) => {
|
|
1467
|
+
if (message.role !== "tool") {
|
|
1468
|
+
return {
|
|
1469
|
+
role: message.role,
|
|
1470
|
+
content: message.content,
|
|
1471
|
+
tool_calls: message.toolCalls,
|
|
1472
|
+
tool_call_id: message.toolCallId,
|
|
1473
|
+
attachments: message.attachments
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
const toolCall = message.toolCallId ? toolCallMap.get(message.toolCallId) : void 0;
|
|
1477
|
+
const tool2 = toolCall ? toolDefMap.get(toolCall.toolName) : void 0;
|
|
1478
|
+
let content = message.content;
|
|
1479
|
+
try {
|
|
1480
|
+
const parsed = JSON.parse(message.content);
|
|
1481
|
+
content = buildToolResultContentForPrompt(
|
|
1482
|
+
parsed,
|
|
1483
|
+
tool2,
|
|
1484
|
+
toolCall?.args ?? {},
|
|
1485
|
+
this.config
|
|
1486
|
+
);
|
|
1487
|
+
} catch {
|
|
1488
|
+
content = buildToolResultContentForPrompt(
|
|
1489
|
+
message.content,
|
|
1490
|
+
tool2,
|
|
1491
|
+
toolCall?.args ?? {},
|
|
1492
|
+
this.config
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
return {
|
|
1496
|
+
role: message.role,
|
|
1497
|
+
content,
|
|
1498
|
+
tool_call_id: message.toolCallId
|
|
1499
|
+
};
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
// src/chat/classes/AbstractChat.ts
|
|
584
1505
|
var AbstractChat = class {
|
|
585
1506
|
constructor(init) {
|
|
1507
|
+
this.lastContextUsage = null;
|
|
586
1508
|
// Event handlers
|
|
587
1509
|
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
588
1510
|
// Current streaming state
|
|
589
1511
|
this.streamState = null;
|
|
1512
|
+
/**
|
|
1513
|
+
* Inline skills from the client (sent on every request for server to merge)
|
|
1514
|
+
*/
|
|
1515
|
+
this.inlineSkills = [];
|
|
590
1516
|
/**
|
|
591
1517
|
* Dynamic context from useAIContext hook
|
|
592
1518
|
*/
|
|
593
1519
|
this.dynamicContext = "";
|
|
1520
|
+
/**
|
|
1521
|
+
* Optional transform applied to messages just before building the HTTP request.
|
|
1522
|
+
* Used by the message-history / compaction system to send a pruned message list
|
|
1523
|
+
* without mutating the in-memory store (which keeps the full history for display).
|
|
1524
|
+
*/
|
|
1525
|
+
this.requestMessageTransform = null;
|
|
594
1526
|
this._isDisposed = false;
|
|
595
1527
|
this.config = {
|
|
596
1528
|
runtimeUrl: init.runtimeUrl,
|
|
@@ -600,7 +1532,8 @@ var AbstractChat = class {
|
|
|
600
1532
|
headers: init.headers,
|
|
601
1533
|
body: init.body,
|
|
602
1534
|
threadId: init.threadId,
|
|
603
|
-
debug: init.debug
|
|
1535
|
+
debug: init.debug,
|
|
1536
|
+
optimization: init.optimization
|
|
604
1537
|
};
|
|
605
1538
|
this.state = init.state ?? new SimpleChatState();
|
|
606
1539
|
this.transport = init.transport ?? new HttpTransport({
|
|
@@ -610,6 +1543,7 @@ var AbstractChat = class {
|
|
|
610
1543
|
streaming: init.streaming ?? true
|
|
611
1544
|
});
|
|
612
1545
|
this.callbacks = init.callbacks ?? {};
|
|
1546
|
+
this.optimizer = new ChatContextOptimizer(init.optimization);
|
|
613
1547
|
if (init.initialMessages?.length) {
|
|
614
1548
|
this.state.setMessages(init.initialMessages);
|
|
615
1549
|
}
|
|
@@ -641,20 +1575,42 @@ var AbstractChat = class {
|
|
|
641
1575
|
/**
|
|
642
1576
|
* Send a message
|
|
643
1577
|
* Returns false if a request is already in progress
|
|
1578
|
+
*
|
|
1579
|
+
* @param content - Message content
|
|
1580
|
+
* @param attachments - Optional attachments
|
|
1581
|
+
* @param options - Optional branching options
|
|
1582
|
+
* @param options.editMessageId - Edit flow: new message branches from the
|
|
1583
|
+
* same parent as this message ID, creating a parallel conversation path
|
|
644
1584
|
*/
|
|
645
|
-
async sendMessage(content, attachments) {
|
|
1585
|
+
async sendMessage(content, attachments, options) {
|
|
646
1586
|
if (this.isBusy) {
|
|
647
1587
|
this.debug("sendMessage", "Blocked - request already in progress");
|
|
648
1588
|
return false;
|
|
649
1589
|
}
|
|
650
|
-
this.debug("sendMessage", { content, attachments });
|
|
1590
|
+
this.debug("sendMessage", { content, attachments, options });
|
|
651
1591
|
try {
|
|
652
1592
|
this.resolveUnresolvedToolCalls();
|
|
653
|
-
|
|
1593
|
+
let newParentId;
|
|
1594
|
+
const visibleMessages = this.state.messages;
|
|
1595
|
+
if (options?.editMessageId && this.state.setCurrentLeaf) {
|
|
1596
|
+
const allMessages = this.state.getAllMessages?.() ?? this.state.messages;
|
|
1597
|
+
const target = allMessages.find((m) => m.id === options.editMessageId);
|
|
1598
|
+
if (target && target.parentId !== void 0) {
|
|
1599
|
+
newParentId = target.parentId;
|
|
1600
|
+
this.state.setCurrentLeaf(
|
|
1601
|
+
typeof target.parentId === "string" ? target.parentId : null
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
} else if (visibleMessages.length > 0) {
|
|
1605
|
+
newParentId = visibleMessages[visibleMessages.length - 1].id;
|
|
1606
|
+
}
|
|
1607
|
+
const userMessage = createUserMessage(content, attachments, {
|
|
1608
|
+
parentId: newParentId
|
|
1609
|
+
});
|
|
654
1610
|
this.state.pushMessage(userMessage);
|
|
655
1611
|
this.state.status = "submitted";
|
|
656
1612
|
this.state.error = void 0;
|
|
657
|
-
this.callbacks.onMessagesChange?.(this.
|
|
1613
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
658
1614
|
this.callbacks.onStatusChange?.("submitted");
|
|
659
1615
|
await Promise.resolve();
|
|
660
1616
|
await this.processRequest();
|
|
@@ -704,7 +1660,7 @@ var AbstractChat = class {
|
|
|
704
1660
|
};
|
|
705
1661
|
this.state.pushMessage(toolMessage);
|
|
706
1662
|
}
|
|
707
|
-
this.callbacks.onMessagesChange?.(this.
|
|
1663
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
708
1664
|
}
|
|
709
1665
|
}
|
|
710
1666
|
/**
|
|
@@ -758,9 +1714,9 @@ var AbstractChat = class {
|
|
|
758
1714
|
this.state.pushMessage(userMessage);
|
|
759
1715
|
}
|
|
760
1716
|
this.state.status = "submitted";
|
|
761
|
-
this.callbacks.onMessagesChange?.(this.
|
|
1717
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
762
1718
|
this.callbacks.onStatusChange?.("submitted");
|
|
763
|
-
await Promise
|
|
1719
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
764
1720
|
await this.processRequest();
|
|
765
1721
|
} catch (error) {
|
|
766
1722
|
this.handleError(error);
|
|
@@ -789,30 +1745,58 @@ var AbstractChat = class {
|
|
|
789
1745
|
this.callbacks.onMessagesChange?.(messages);
|
|
790
1746
|
}
|
|
791
1747
|
/**
|
|
792
|
-
* Regenerate last response
|
|
1748
|
+
* Regenerate last response.
|
|
1749
|
+
*
|
|
1750
|
+
* Branch-aware: when the state supports branching (setCurrentLeaf is available),
|
|
1751
|
+
* regenerate creates a new sibling response instead of destroying the original.
|
|
1752
|
+
* The old response is preserved and navigable via switchBranch().
|
|
1753
|
+
*
|
|
1754
|
+
* Legacy fallback: when branching is not available, uses old slice() behavior.
|
|
793
1755
|
*/
|
|
794
1756
|
async regenerate(messageId) {
|
|
1757
|
+
if (this.isBusy) return;
|
|
795
1758
|
const messages = this.state.messages;
|
|
796
|
-
let
|
|
1759
|
+
let targetMessage;
|
|
797
1760
|
if (messageId) {
|
|
798
|
-
|
|
1761
|
+
targetMessage = messages.find((m) => m.id === messageId);
|
|
1762
|
+
if (!targetMessage) {
|
|
1763
|
+
targetMessage = this.state.getAllMessages?.().find((m) => m.id === messageId);
|
|
1764
|
+
}
|
|
799
1765
|
} else {
|
|
800
1766
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
801
1767
|
if (messages[i].role === "assistant") {
|
|
802
|
-
|
|
1768
|
+
targetMessage = messages[i];
|
|
803
1769
|
break;
|
|
804
1770
|
}
|
|
805
1771
|
}
|
|
806
1772
|
}
|
|
1773
|
+
if (!targetMessage) return;
|
|
1774
|
+
if (targetMessage.parentId !== void 0 && this.state.setCurrentLeaf) {
|
|
1775
|
+
this.state.setCurrentLeaf(targetMessage.parentId ?? null);
|
|
1776
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
1777
|
+
this.state.status = "submitted";
|
|
1778
|
+
await Promise.resolve();
|
|
1779
|
+
await this.processRequest();
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
const targetIndex = messages.indexOf(targetMessage);
|
|
807
1783
|
if (targetIndex > 0) {
|
|
808
1784
|
this.state.setMessages(messages.slice(0, targetIndex));
|
|
809
|
-
this.callbacks.onMessagesChange?.(this.
|
|
1785
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
810
1786
|
await this.processRequest();
|
|
811
1787
|
}
|
|
812
1788
|
}
|
|
813
1789
|
// ============================================
|
|
814
1790
|
// Event Handling
|
|
815
1791
|
// ============================================
|
|
1792
|
+
/**
|
|
1793
|
+
* Returns all messages across all branches when the state supports it
|
|
1794
|
+
* (branch-aware), otherwise returns the visible path.
|
|
1795
|
+
* Use this whenever firing onMessagesChange so inactive branches are not lost.
|
|
1796
|
+
*/
|
|
1797
|
+
_allMessages() {
|
|
1798
|
+
return this.state.getAllMessages?.() ?? this.state.messages;
|
|
1799
|
+
}
|
|
816
1800
|
/**
|
|
817
1801
|
* Subscribe to events
|
|
818
1802
|
*/
|
|
@@ -844,10 +1828,32 @@ var AbstractChat = class {
|
|
|
844
1828
|
*/
|
|
845
1829
|
async processRequest() {
|
|
846
1830
|
const request = this.buildRequest();
|
|
1831
|
+
let preCreatedMessageId;
|
|
1832
|
+
if (this.config.streaming !== false) {
|
|
1833
|
+
const visibleMessages = this.state.messages;
|
|
1834
|
+
const currentLeafId = visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1].id : void 0;
|
|
1835
|
+
const preMsg = createEmptyAssistantMessage(void 0, {
|
|
1836
|
+
parentId: currentLeafId
|
|
1837
|
+
});
|
|
1838
|
+
this.state.pushMessage(preMsg);
|
|
1839
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
1840
|
+
preCreatedMessageId = preMsg.id;
|
|
1841
|
+
}
|
|
847
1842
|
const response = await this.transport.send(request);
|
|
848
1843
|
if (this.isAsyncIterable(response)) {
|
|
849
|
-
await this.handleStreamResponse(response);
|
|
1844
|
+
await this.handleStreamResponse(response, preCreatedMessageId);
|
|
850
1845
|
} else {
|
|
1846
|
+
if (preCreatedMessageId) {
|
|
1847
|
+
const id = preCreatedMessageId;
|
|
1848
|
+
const visibleMsgs = this.state.messages;
|
|
1849
|
+
const placeholderIdx = visibleMsgs.findIndex((m) => m.id === id);
|
|
1850
|
+
const intendedLeafId = placeholderIdx > 0 ? visibleMsgs[placeholderIdx - 1].id : null;
|
|
1851
|
+
const allMsgs = this.state.getAllMessages?.() ?? this.state.messages;
|
|
1852
|
+
this.state.setMessages(allMsgs.filter((m) => m.id !== id));
|
|
1853
|
+
if (intendedLeafId && this.state.setCurrentLeaf) {
|
|
1854
|
+
this.state.setCurrentLeaf(intendedLeafId);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
851
1857
|
this.handleJsonResponse(response);
|
|
852
1858
|
}
|
|
853
1859
|
}
|
|
@@ -858,30 +1864,60 @@ var AbstractChat = class {
|
|
|
858
1864
|
this.config.tools = tools;
|
|
859
1865
|
}
|
|
860
1866
|
/**
|
|
861
|
-
*
|
|
1867
|
+
* Update prompt/tool optimization behavior.
|
|
862
1868
|
*/
|
|
863
|
-
|
|
864
|
-
this.
|
|
865
|
-
this.
|
|
1869
|
+
setOptimizationConfig(config) {
|
|
1870
|
+
this.config.optimization = config;
|
|
1871
|
+
this.optimizer.updateConfig(config);
|
|
866
1872
|
}
|
|
867
1873
|
/**
|
|
868
|
-
*
|
|
869
|
-
* This allows updating the system prompt after initialization
|
|
1874
|
+
* Select the active tool profile for future requests.
|
|
870
1875
|
*/
|
|
871
|
-
|
|
872
|
-
this.
|
|
873
|
-
this.debug("System prompt updated", { length: prompt.length });
|
|
1876
|
+
setToolProfile(profile) {
|
|
1877
|
+
this.optimizer.setActiveProfile(profile);
|
|
874
1878
|
}
|
|
875
1879
|
/**
|
|
876
|
-
*
|
|
877
|
-
* Can be static headers or a getter function for dynamic resolution
|
|
1880
|
+
* Get the most recent prompt context usage snapshot.
|
|
878
1881
|
*/
|
|
879
|
-
|
|
880
|
-
this.
|
|
1882
|
+
getContextUsage() {
|
|
1883
|
+
return this.lastContextUsage;
|
|
1884
|
+
}
|
|
1885
|
+
/**
|
|
1886
|
+
* Set inline skills (called by SkillProvider via React layer)
|
|
1887
|
+
*/
|
|
1888
|
+
setInlineSkills(skills) {
|
|
1889
|
+
this.inlineSkills = skills;
|
|
1890
|
+
this.debug("Inline skills updated", { count: skills.length });
|
|
1891
|
+
}
|
|
1892
|
+
/**
|
|
1893
|
+
* Set (or clear) the per-request message transform.
|
|
1894
|
+
* Pass null to disable.
|
|
1895
|
+
*/
|
|
1896
|
+
setRequestMessageTransform(fn) {
|
|
1897
|
+
this.requestMessageTransform = fn;
|
|
1898
|
+
}
|
|
1899
|
+
/**
|
|
1900
|
+
* Set dynamic context (appended to system prompt)
|
|
1901
|
+
*/
|
|
1902
|
+
setContext(context) {
|
|
1903
|
+
this.dynamicContext = context;
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Set system prompt dynamically
|
|
1907
|
+
* This allows updating the system prompt after initialization
|
|
1908
|
+
*/
|
|
1909
|
+
setSystemPrompt(prompt) {
|
|
1910
|
+
this.config.systemPrompt = prompt;
|
|
1911
|
+
}
|
|
1912
|
+
/**
|
|
1913
|
+
* Set headers configuration
|
|
1914
|
+
* Can be static headers or a getter function for dynamic resolution
|
|
1915
|
+
*/
|
|
1916
|
+
setHeaders(headers) {
|
|
1917
|
+
this.config.headers = headers;
|
|
881
1918
|
if (this.transport.setHeaders && headers !== void 0) {
|
|
882
1919
|
this.transport.setHeaders(headers);
|
|
883
1920
|
}
|
|
884
|
-
this.debug("Headers config updated");
|
|
885
1921
|
}
|
|
886
1922
|
/**
|
|
887
1923
|
* Set URL configuration
|
|
@@ -892,7 +1928,6 @@ var AbstractChat = class {
|
|
|
892
1928
|
if (this.transport.setUrl) {
|
|
893
1929
|
this.transport.setUrl(url);
|
|
894
1930
|
}
|
|
895
|
-
this.debug("URL config updated");
|
|
896
1931
|
}
|
|
897
1932
|
/**
|
|
898
1933
|
* Set body configuration
|
|
@@ -903,110 +1938,88 @@ var AbstractChat = class {
|
|
|
903
1938
|
if (this.transport.setBody && body !== void 0) {
|
|
904
1939
|
this.transport.setBody(body);
|
|
905
1940
|
}
|
|
906
|
-
this.debug("Body config updated");
|
|
907
1941
|
}
|
|
908
1942
|
/**
|
|
909
1943
|
* Build the request payload
|
|
910
1944
|
*/
|
|
911
1945
|
buildRequest() {
|
|
912
|
-
const
|
|
913
|
-
name: tool2.name,
|
|
914
|
-
description: tool2.description,
|
|
915
|
-
inputSchema: tool2.inputSchema
|
|
916
|
-
}));
|
|
917
|
-
const toolCallMap = /* @__PURE__ */ new Map();
|
|
918
|
-
for (const msg of this.state.messages) {
|
|
919
|
-
if (msg.role === "assistant" && msg.toolCalls) {
|
|
920
|
-
for (const tc of msg.toolCalls) {
|
|
921
|
-
try {
|
|
922
|
-
const args = tc.function?.arguments ? JSON.parse(tc.function.arguments) : {};
|
|
923
|
-
toolCallMap.set(tc.id, { toolName: tc.function.name, args });
|
|
924
|
-
} catch {
|
|
925
|
-
toolCallMap.set(tc.id, { toolName: tc.function.name, args: {} });
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
const toolDefMap = /* @__PURE__ */ new Map();
|
|
931
|
-
if (this.config.tools) {
|
|
932
|
-
for (const tool2 of this.config.tools) {
|
|
933
|
-
toolDefMap.set(tool2.name, {
|
|
934
|
-
name: tool2.name,
|
|
935
|
-
aiResponseMode: tool2.aiResponseMode,
|
|
936
|
-
aiContext: tool2.aiContext
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
return {
|
|
941
|
-
messages: this.state.messages.map((m) => {
|
|
942
|
-
if (m.role === "tool" && m.content && m.toolCallId) {
|
|
943
|
-
try {
|
|
944
|
-
const fullResult = JSON.parse(m.content);
|
|
945
|
-
const toolCallInfo = toolCallMap.get(m.toolCallId);
|
|
946
|
-
const toolDef = toolCallInfo ? toolDefMap.get(toolCallInfo.toolName) : void 0;
|
|
947
|
-
const toolArgs = toolCallInfo?.args;
|
|
948
|
-
const transformedContent = buildToolResultContentForAI(
|
|
949
|
-
fullResult,
|
|
950
|
-
toolDef,
|
|
951
|
-
toolArgs
|
|
952
|
-
);
|
|
953
|
-
return {
|
|
954
|
-
role: m.role,
|
|
955
|
-
content: transformedContent,
|
|
956
|
-
tool_call_id: m.toolCallId
|
|
957
|
-
};
|
|
958
|
-
} catch (e) {
|
|
959
|
-
this.debug("Failed to parse tool message JSON", {
|
|
960
|
-
content: m.content?.slice(0, 100),
|
|
961
|
-
error: e instanceof Error ? e.message : String(e)
|
|
962
|
-
});
|
|
963
|
-
return {
|
|
964
|
-
role: m.role,
|
|
965
|
-
content: m.content,
|
|
966
|
-
tool_call_id: m.toolCallId
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
return {
|
|
971
|
-
role: m.role,
|
|
972
|
-
content: m.content,
|
|
973
|
-
tool_calls: m.toolCalls,
|
|
974
|
-
tool_call_id: m.toolCallId,
|
|
975
|
-
attachments: m.attachments
|
|
976
|
-
};
|
|
977
|
-
}),
|
|
978
|
-
threadId: this.config.threadId,
|
|
979
|
-
systemPrompt: this.dynamicContext ? `${this.config.systemPrompt || ""}
|
|
1946
|
+
const systemPrompt = this.dynamicContext ? `${this.config.systemPrompt || ""}
|
|
980
1947
|
|
|
981
1948
|
## Current App Context:
|
|
982
|
-
${this.dynamicContext}`.trim() : this.config.systemPrompt
|
|
1949
|
+
${this.dynamicContext}`.trim() : this.config.systemPrompt;
|
|
1950
|
+
const rawMessages = this.requestMessageTransform ? this.requestMessageTransform(
|
|
1951
|
+
this.state.messages
|
|
1952
|
+
) : this.state.messages;
|
|
1953
|
+
const optimized = this.optimizer.prepare({
|
|
1954
|
+
messages: rawMessages,
|
|
1955
|
+
tools: this.config.tools,
|
|
1956
|
+
systemPrompt
|
|
1957
|
+
});
|
|
1958
|
+
this.lastContextUsage = optimized.contextUsage;
|
|
1959
|
+
this.callbacks.onContextUsageChange?.(optimized.contextUsage);
|
|
1960
|
+
return {
|
|
1961
|
+
messages: optimized.messages,
|
|
1962
|
+
threadId: this.config.threadId,
|
|
1963
|
+
systemPrompt,
|
|
983
1964
|
llm: this.config.llm,
|
|
984
|
-
tools: tools?.length ? tools
|
|
1965
|
+
tools: this.config.tools?.length ? this.config.tools.map((tool2) => ({
|
|
1966
|
+
name: tool2.name,
|
|
1967
|
+
description: tool2.description,
|
|
1968
|
+
category: tool2.category,
|
|
1969
|
+
group: tool2.group,
|
|
1970
|
+
deferLoading: tool2.deferLoading,
|
|
1971
|
+
profiles: tool2.profiles,
|
|
1972
|
+
searchKeywords: tool2.searchKeywords,
|
|
1973
|
+
inputSchema: tool2.inputSchema
|
|
1974
|
+
})) : void 0,
|
|
1975
|
+
__skills: this.inlineSkills.length ? this.inlineSkills : void 0
|
|
985
1976
|
};
|
|
986
1977
|
}
|
|
987
1978
|
/**
|
|
988
1979
|
* Handle streaming response
|
|
989
1980
|
*/
|
|
990
|
-
async handleStreamResponse(stream) {
|
|
1981
|
+
async handleStreamResponse(stream, preCreatedMessageId) {
|
|
991
1982
|
this.state.status = "streaming";
|
|
992
1983
|
this.callbacks.onStatusChange?.("streaming");
|
|
993
|
-
|
|
994
|
-
|
|
1984
|
+
let assistantMessage;
|
|
1985
|
+
if (preCreatedMessageId) {
|
|
1986
|
+
const existing = this.state.messages.find(
|
|
1987
|
+
(m) => m.id === preCreatedMessageId
|
|
1988
|
+
);
|
|
1989
|
+
if (existing) {
|
|
1990
|
+
assistantMessage = existing;
|
|
1991
|
+
} else {
|
|
1992
|
+
assistantMessage = createEmptyAssistantMessage();
|
|
1993
|
+
this.state.pushMessage(assistantMessage);
|
|
1994
|
+
}
|
|
1995
|
+
} else {
|
|
1996
|
+
assistantMessage = createEmptyAssistantMessage();
|
|
1997
|
+
this.state.pushMessage(assistantMessage);
|
|
1998
|
+
}
|
|
995
1999
|
this.streamState = createStreamState(assistantMessage.id);
|
|
996
2000
|
this.callbacks.onMessageStart?.(assistantMessage.id);
|
|
997
|
-
this.
|
|
2001
|
+
this.debugGroup("handleStreamResponse");
|
|
2002
|
+
this.debug("Starting to process stream");
|
|
998
2003
|
let chunkCount = 0;
|
|
999
2004
|
let toolCallsEmitted = false;
|
|
2005
|
+
let pendingClientToolCalls;
|
|
1000
2006
|
for await (const chunk of stream) {
|
|
1001
2007
|
chunkCount++;
|
|
1002
|
-
|
|
2008
|
+
if (chunk.type !== "message:delta") {
|
|
2009
|
+
this.debug("chunk", { count: chunkCount, type: chunk.type });
|
|
2010
|
+
}
|
|
1003
2011
|
if (chunk.type === "error") {
|
|
1004
2012
|
const error = new Error(chunk.message || "Stream error");
|
|
1005
2013
|
this.handleError(error);
|
|
1006
2014
|
return;
|
|
1007
2015
|
}
|
|
1008
2016
|
if (chunk.type === "message:end" && this.streamState?.content) {
|
|
1009
|
-
this.debug("message:end mid-stream
|
|
2017
|
+
this.debug("message:end mid-stream", {
|
|
2018
|
+
messageId: this.streamState.messageId,
|
|
2019
|
+
contentLength: this.streamState.content.length,
|
|
2020
|
+
toolCallsInState: this.streamState.toolCalls?.length ?? 0,
|
|
2021
|
+
chunkCount
|
|
2022
|
+
});
|
|
1010
2023
|
const turnMessage = streamStateToMessage(this.streamState);
|
|
1011
2024
|
const toolCallsHidden = {};
|
|
1012
2025
|
for (const [id, result] of this.streamState.toolResults) {
|
|
@@ -1022,7 +2035,11 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1022
2035
|
}
|
|
1023
2036
|
this.state.updateMessageById(
|
|
1024
2037
|
this.streamState.messageId,
|
|
1025
|
-
() =>
|
|
2038
|
+
(existing) => ({
|
|
2039
|
+
...turnMessage,
|
|
2040
|
+
...existing.parentId !== void 0 ? { parentId: existing.parentId } : {},
|
|
2041
|
+
...existing.childrenIds !== void 0 ? { childrenIds: existing.childrenIds } : {}
|
|
2042
|
+
})
|
|
1026
2043
|
);
|
|
1027
2044
|
this.callbacks.onMessageFinish?.(turnMessage);
|
|
1028
2045
|
this.streamState = null;
|
|
@@ -1037,6 +2054,93 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1037
2054
|
continue;
|
|
1038
2055
|
}
|
|
1039
2056
|
if (!this.streamState) {
|
|
2057
|
+
if (chunk.type === "tool_calls") {
|
|
2058
|
+
pendingClientToolCalls = chunk.toolCalls;
|
|
2059
|
+
this.debug("tool_calls (post-message:end, stored as pending)", {
|
|
2060
|
+
count: pendingClientToolCalls?.length,
|
|
2061
|
+
ids: pendingClientToolCalls?.map((tc) => tc.id)
|
|
2062
|
+
});
|
|
2063
|
+
continue;
|
|
2064
|
+
}
|
|
2065
|
+
if (chunk.type === "done") {
|
|
2066
|
+
this.debug("done (post-message:end)", {
|
|
2067
|
+
hasPendingToolCalls: !!pendingClientToolCalls?.length,
|
|
2068
|
+
pendingCount: pendingClientToolCalls?.length ?? 0,
|
|
2069
|
+
doneMessagesCount: chunk.messages?.length ?? 0,
|
|
2070
|
+
requiresAction: chunk.requiresAction,
|
|
2071
|
+
toolCallsEmitted
|
|
2072
|
+
});
|
|
2073
|
+
if (chunk.messages?.length) {
|
|
2074
|
+
const pendingIds = new Set(
|
|
2075
|
+
(pendingClientToolCalls ?? []).filter((tc) => tc?.id).map((tc) => tc.id)
|
|
2076
|
+
);
|
|
2077
|
+
const messagesToInsert = [];
|
|
2078
|
+
let clientAssistantToolCalls;
|
|
2079
|
+
for (const msg of chunk.messages) {
|
|
2080
|
+
if (msg.role === "assistant" && msg.tool_calls?.length && pendingIds.size > 0 && msg.tool_calls.every(
|
|
2081
|
+
(tc) => pendingIds.has(tc?.id ?? "")
|
|
2082
|
+
)) {
|
|
2083
|
+
clientAssistantToolCalls = msg.tool_calls;
|
|
2084
|
+
continue;
|
|
2085
|
+
}
|
|
2086
|
+
if (msg.role === "assistant" && !msg.tool_calls?.length) continue;
|
|
2087
|
+
messagesToInsert.push({
|
|
2088
|
+
id: generateMessageId(),
|
|
2089
|
+
role: msg.role,
|
|
2090
|
+
content: msg.content ?? "",
|
|
2091
|
+
toolCalls: msg.tool_calls,
|
|
2092
|
+
toolCallId: msg.tool_call_id,
|
|
2093
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
if (clientAssistantToolCalls) {
|
|
2097
|
+
const currentMessages = this.state.messages;
|
|
2098
|
+
for (let i = currentMessages.length - 1; i >= 0; i--) {
|
|
2099
|
+
if (currentMessages[i].role === "assistant") {
|
|
2100
|
+
this.state.updateMessageById(
|
|
2101
|
+
currentMessages[i].id,
|
|
2102
|
+
(m) => ({
|
|
2103
|
+
...m,
|
|
2104
|
+
toolCalls: clientAssistantToolCalls
|
|
2105
|
+
})
|
|
2106
|
+
);
|
|
2107
|
+
break;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
if (messagesToInsert.length > 0) {
|
|
2112
|
+
const currentMessages = this.state.messages;
|
|
2113
|
+
let insertIdx = currentMessages.length;
|
|
2114
|
+
for (let i = currentMessages.length - 1; i >= 0; i--) {
|
|
2115
|
+
if (currentMessages[i].role === "assistant") {
|
|
2116
|
+
insertIdx = i;
|
|
2117
|
+
break;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
this.state.setMessages([
|
|
2121
|
+
...currentMessages.slice(0, insertIdx),
|
|
2122
|
+
...messagesToInsert,
|
|
2123
|
+
...currentMessages.slice(insertIdx)
|
|
2124
|
+
]);
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
if (!toolCallsEmitted && pendingClientToolCalls?.length) {
|
|
2128
|
+
toolCallsEmitted = true;
|
|
2129
|
+
this.debug("emit toolCalls (post-message:end path)", {
|
|
2130
|
+
count: pendingClientToolCalls.length,
|
|
2131
|
+
names: pendingClientToolCalls.map(
|
|
2132
|
+
(tc) => tc.function?.name ?? tc.name
|
|
2133
|
+
)
|
|
2134
|
+
});
|
|
2135
|
+
this.emit("toolCalls", { toolCalls: pendingClientToolCalls });
|
|
2136
|
+
} else {
|
|
2137
|
+
this.debug("skip emit toolCalls (post-message:end path)", {
|
|
2138
|
+
toolCallsEmitted,
|
|
2139
|
+
hasPending: !!pendingClientToolCalls?.length
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
continue;
|
|
2143
|
+
}
|
|
1040
2144
|
this.debug("warning", "streamState is null, skipping chunk");
|
|
1041
2145
|
continue;
|
|
1042
2146
|
}
|
|
@@ -1072,22 +2176,38 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1072
2176
|
const updatedMessage = streamStateToMessage(this.streamState);
|
|
1073
2177
|
this.state.updateMessageById(
|
|
1074
2178
|
this.streamState.messageId,
|
|
1075
|
-
|
|
2179
|
+
// Preserve parentId/childrenIds from the existing placeholder so the
|
|
2180
|
+
// branch tree structure (activeChildMap) is not corrupted when
|
|
2181
|
+
// setCurrentLeaf() walks up the chain later.
|
|
2182
|
+
(existing) => ({
|
|
2183
|
+
...updatedMessage,
|
|
2184
|
+
...existing.parentId !== void 0 ? { parentId: existing.parentId } : {},
|
|
2185
|
+
...existing.childrenIds !== void 0 ? { childrenIds: existing.childrenIds } : {}
|
|
2186
|
+
})
|
|
1076
2187
|
);
|
|
1077
2188
|
if (chunk.type === "message:delta") {
|
|
1078
2189
|
this.callbacks.onMessageDelta?.(assistantMessage.id, chunk.content);
|
|
1079
2190
|
}
|
|
1080
|
-
if (requiresToolExecution(chunk) && !toolCallsEmitted) {
|
|
1081
|
-
toolCallsEmitted = true;
|
|
1082
|
-
this.debug("toolCalls", { toolCalls: updatedMessage.toolCalls });
|
|
1083
|
-
this.emit("toolCalls", { toolCalls: updatedMessage.toolCalls });
|
|
1084
|
-
}
|
|
1085
2191
|
if (isStreamDone(chunk)) {
|
|
1086
|
-
this.debug("streamDone", {
|
|
2192
|
+
this.debug("streamDone", {
|
|
2193
|
+
chunkType: chunk.type,
|
|
2194
|
+
requiresAction: chunk.requiresAction,
|
|
2195
|
+
doneMessagesCount: chunk.messages?.length ?? 0,
|
|
2196
|
+
streamToolCallsCount: this.streamState?.toolCalls?.length ?? 0,
|
|
2197
|
+
toolCallsEmitted,
|
|
2198
|
+
chunkCount
|
|
2199
|
+
});
|
|
1087
2200
|
if (chunk.type === "done" && chunk.messages?.length) {
|
|
1088
2201
|
this.debug("processDoneMessages", {
|
|
1089
|
-
count: chunk.messages.length
|
|
2202
|
+
count: chunk.messages.length,
|
|
2203
|
+
roles: chunk.messages.map(
|
|
2204
|
+
(m) => `${m.role}${m.tool_calls?.length ? `[${m.tool_calls.length}tc]` : ""}`
|
|
2205
|
+
)
|
|
1090
2206
|
});
|
|
2207
|
+
const currentStreamToolCallIds = new Set(
|
|
2208
|
+
this.streamState?.toolCalls?.map((toolCall) => toolCall.id) ?? []
|
|
2209
|
+
);
|
|
2210
|
+
const messagesToInsert = [];
|
|
1091
2211
|
const toolCallsHidden = {};
|
|
1092
2212
|
if (this.streamState?.toolResults) {
|
|
1093
2213
|
for (const [id, result] of this.streamState.toolResults) {
|
|
@@ -1097,7 +2217,12 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1097
2217
|
}
|
|
1098
2218
|
}
|
|
1099
2219
|
for (const msg of chunk.messages) {
|
|
1100
|
-
if (msg.role === "assistant") {
|
|
2220
|
+
if (msg.role === "assistant" && !msg.tool_calls?.length) {
|
|
2221
|
+
continue;
|
|
2222
|
+
}
|
|
2223
|
+
if (msg.role === "assistant" && msg.tool_calls?.length && msg.tool_calls.every(
|
|
2224
|
+
(toolCall) => currentStreamToolCallIds.has(toolCall.id)
|
|
2225
|
+
)) {
|
|
1101
2226
|
continue;
|
|
1102
2227
|
}
|
|
1103
2228
|
let metadata;
|
|
@@ -1113,7 +2238,60 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1113
2238
|
createdAt: /* @__PURE__ */ new Date(),
|
|
1114
2239
|
metadata
|
|
1115
2240
|
};
|
|
1116
|
-
|
|
2241
|
+
messagesToInsert.push(message);
|
|
2242
|
+
}
|
|
2243
|
+
if (messagesToInsert.length > 0) {
|
|
2244
|
+
const currentMessages = this.state.messages;
|
|
2245
|
+
const currentStreamIndex = this.streamState ? currentMessages.findIndex(
|
|
2246
|
+
(message) => message.id === this.streamState.messageId
|
|
2247
|
+
) : -1;
|
|
2248
|
+
if (currentStreamIndex === -1) {
|
|
2249
|
+
this.state.setMessages([...currentMessages, ...messagesToInsert]);
|
|
2250
|
+
} else {
|
|
2251
|
+
this.state.setMessages([
|
|
2252
|
+
...currentMessages.slice(0, currentStreamIndex),
|
|
2253
|
+
...messagesToInsert,
|
|
2254
|
+
...currentMessages.slice(currentStreamIndex)
|
|
2255
|
+
]);
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
this.debug("requiresAction check", {
|
|
2259
|
+
requiresAction: chunk.requiresAction,
|
|
2260
|
+
toolCallsEmitted,
|
|
2261
|
+
updatedMessageToolCallsCount: updatedMessage.toolCalls?.length ?? 0,
|
|
2262
|
+
messagesToInsertCount: messagesToInsert.length
|
|
2263
|
+
});
|
|
2264
|
+
if (chunk.requiresAction && !toolCallsEmitted) {
|
|
2265
|
+
let clientToolCalls = updatedMessage.toolCalls;
|
|
2266
|
+
if (!clientToolCalls?.length && messagesToInsert.length > 0) {
|
|
2267
|
+
for (let i = messagesToInsert.length - 1; i >= 0; i--) {
|
|
2268
|
+
const m = messagesToInsert[i];
|
|
2269
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
2270
|
+
clientToolCalls = m.toolCalls;
|
|
2271
|
+
this.debug("clientToolCalls from messagesToInsert", {
|
|
2272
|
+
index: i,
|
|
2273
|
+
count: clientToolCalls?.length
|
|
2274
|
+
});
|
|
2275
|
+
break;
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
if (clientToolCalls?.length) {
|
|
2280
|
+
toolCallsEmitted = true;
|
|
2281
|
+
this.debug("emit toolCalls (normal done path)", {
|
|
2282
|
+
count: clientToolCalls.length,
|
|
2283
|
+
names: clientToolCalls.map((tc) => tc.function?.name ?? tc.name)
|
|
2284
|
+
});
|
|
2285
|
+
this.emit("toolCalls", { toolCalls: clientToolCalls });
|
|
2286
|
+
} else {
|
|
2287
|
+
this.debug("requiresAction=true but no clientToolCalls found", {
|
|
2288
|
+
updatedMessageToolCalls: updatedMessage.toolCalls,
|
|
2289
|
+
messagesToInsert: messagesToInsert.map((m) => ({
|
|
2290
|
+
role: m.role,
|
|
2291
|
+
hasToolCalls: !!m.toolCalls?.length
|
|
2292
|
+
}))
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
1117
2295
|
}
|
|
1118
2296
|
}
|
|
1119
2297
|
break;
|
|
@@ -1138,15 +2316,22 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1138
2316
|
toolCallsHidden
|
|
1139
2317
|
};
|
|
1140
2318
|
}
|
|
1141
|
-
this.state.updateMessageById(
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
2319
|
+
this.state.updateMessageById(this.streamState.messageId, (existing) => ({
|
|
2320
|
+
...finalMessage,
|
|
2321
|
+
...existing.parentId !== void 0 ? { parentId: existing.parentId } : {},
|
|
2322
|
+
...existing.childrenIds !== void 0 ? { childrenIds: existing.childrenIds } : {}
|
|
2323
|
+
}));
|
|
1145
2324
|
if (!finalMessage.content && (!finalMessage.toolCalls || finalMessage.toolCalls.length === 0)) {
|
|
1146
2325
|
this.debug("warning", "Empty response - no content and no tool calls");
|
|
1147
2326
|
}
|
|
1148
2327
|
}
|
|
1149
|
-
this.callbacks.onMessagesChange?.(this.
|
|
2328
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
2329
|
+
this.debugGroupEnd();
|
|
2330
|
+
this.debug("stream end", {
|
|
2331
|
+
toolCallsEmitted,
|
|
2332
|
+
totalChunks: chunkCount,
|
|
2333
|
+
messagesInState: this.state.messages.length
|
|
2334
|
+
});
|
|
1150
2335
|
if (!toolCallsEmitted) {
|
|
1151
2336
|
this.state.status = "ready";
|
|
1152
2337
|
this.callbacks.onStatusChange?.("ready");
|
|
@@ -1167,6 +2352,7 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1167
2352
|
}
|
|
1168
2353
|
}
|
|
1169
2354
|
}
|
|
2355
|
+
let currentParentId = this.state.messages.length > 0 ? this.state.messages[this.state.messages.length - 1].id : void 0;
|
|
1170
2356
|
for (const msg of response.messages ?? []) {
|
|
1171
2357
|
let metadata;
|
|
1172
2358
|
if (msg.role === "assistant" && msg.tool_calls && toolCallHiddenMap.size > 0) {
|
|
@@ -1189,11 +2375,15 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1189
2375
|
// CRITICAL: Preserve toolCallId for tool messages (fixes Anthropic API errors)
|
|
1190
2376
|
toolCallId: msg.tool_call_id,
|
|
1191
2377
|
createdAt: /* @__PURE__ */ new Date(),
|
|
1192
|
-
metadata
|
|
2378
|
+
metadata,
|
|
2379
|
+
// Preserve branch tree structure: each message is a child of the
|
|
2380
|
+
// current leaf so the tree is not corrupted for non-streaming mode.
|
|
2381
|
+
...currentParentId !== void 0 ? { parentId: currentParentId } : {}
|
|
1193
2382
|
};
|
|
1194
2383
|
this.state.pushMessage(message);
|
|
2384
|
+
currentParentId = message.id;
|
|
1195
2385
|
}
|
|
1196
|
-
this.callbacks.onMessagesChange?.(this.
|
|
2386
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
1197
2387
|
const hasToolCalls = response.requiresAction && this.state.messages.length > 0 && this.state.messages[this.state.messages.length - 1]?.toolCalls?.length;
|
|
1198
2388
|
if (hasToolCalls) {
|
|
1199
2389
|
const lastMessage = this.state.messages[this.state.messages.length - 1];
|
|
@@ -1216,14 +2406,25 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1216
2406
|
this.callbacks.onStatusChange?.("error");
|
|
1217
2407
|
this.emit("error", { error });
|
|
1218
2408
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
2409
|
+
get log() {
|
|
2410
|
+
if (!this._log) {
|
|
2411
|
+
this._log = chunkI3SQUNTT_cjs.createLogger("streaming", () => this.config.debug ?? false);
|
|
2412
|
+
}
|
|
2413
|
+
return this._log;
|
|
2414
|
+
}
|
|
1222
2415
|
debug(action, data) {
|
|
1223
|
-
|
|
1224
|
-
|
|
2416
|
+
this.log(action, data);
|
|
2417
|
+
}
|
|
2418
|
+
debugGroup(label, collapsed = true) {
|
|
2419
|
+
if (collapsed) {
|
|
2420
|
+
this.log.groupCollapsed(label);
|
|
2421
|
+
} else {
|
|
2422
|
+
this.log.group(label);
|
|
1225
2423
|
}
|
|
1226
2424
|
}
|
|
2425
|
+
debugGroupEnd() {
|
|
2426
|
+
this.log.groupEnd();
|
|
2427
|
+
}
|
|
1227
2428
|
/**
|
|
1228
2429
|
* Type guard for async iterable
|
|
1229
2430
|
*/
|
|
@@ -1434,19 +2635,18 @@ var AbstractAgentLoop = class {
|
|
|
1434
2635
|
this._isCancelled = false;
|
|
1435
2636
|
this._isProcessing = true;
|
|
1436
2637
|
this.setIteration(this._iteration + 1);
|
|
1437
|
-
const results =
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
2638
|
+
const results = await Promise.all(
|
|
2639
|
+
toolCalls.map((toolCall) => {
|
|
2640
|
+
if (this._isCancelled || this.abortController.signal.aborted) {
|
|
2641
|
+
return Promise.resolve({
|
|
2642
|
+
toolCallId: toolCall.id,
|
|
2643
|
+
success: false,
|
|
2644
|
+
error: "Tool execution cancelled"
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
return this.executeSingleTool(toolCall);
|
|
2648
|
+
})
|
|
2649
|
+
);
|
|
1450
2650
|
this._isProcessing = false;
|
|
1451
2651
|
return results;
|
|
1452
2652
|
}
|
|
@@ -1764,6 +2964,7 @@ var ChatWithTools = class {
|
|
|
1764
2964
|
streaming: config.streaming,
|
|
1765
2965
|
headers: config.headers,
|
|
1766
2966
|
body: config.body,
|
|
2967
|
+
optimization: config.optimization,
|
|
1767
2968
|
threadId: config.threadId,
|
|
1768
2969
|
debug: config.debug,
|
|
1769
2970
|
initialMessages: config.initialMessages,
|
|
@@ -1778,6 +2979,7 @@ var ChatWithTools = class {
|
|
|
1778
2979
|
onMessageFinish: callbacks.onMessageFinish,
|
|
1779
2980
|
onToolCalls: callbacks.onToolCalls,
|
|
1780
2981
|
onFinish: callbacks.onFinish,
|
|
2982
|
+
onContextUsageChange: callbacks.onContextUsageChange,
|
|
1781
2983
|
// Server-side tool callbacks - track in agentLoop for UI display
|
|
1782
2984
|
// IMPORTANT: Only track tools that are NOT registered client-side
|
|
1783
2985
|
// Client-side tools are tracked via executeToolCalls() path
|
|
@@ -1945,14 +3147,17 @@ var ChatWithTools = class {
|
|
|
1945
3147
|
/**
|
|
1946
3148
|
* Send a message
|
|
1947
3149
|
* Returns false if a request is already in progress
|
|
3150
|
+
*
|
|
3151
|
+
* @param options.editMessageId - Edit flow: new message branches from the
|
|
3152
|
+
* same parent as this message ID
|
|
1948
3153
|
*/
|
|
1949
|
-
async sendMessage(content, attachments) {
|
|
3154
|
+
async sendMessage(content, attachments, options) {
|
|
1950
3155
|
if (this.isLoading) {
|
|
1951
3156
|
this.debug("sendMessage blocked - request already in progress");
|
|
1952
3157
|
return false;
|
|
1953
3158
|
}
|
|
1954
3159
|
this.agentLoop.resetIterations();
|
|
1955
|
-
return await this.chat.sendMessage(content, attachments);
|
|
3160
|
+
return await this.chat.sendMessage(content, attachments, options);
|
|
1956
3161
|
}
|
|
1957
3162
|
/**
|
|
1958
3163
|
* Stop generation and cancel any running tools
|
|
@@ -1987,6 +3192,25 @@ var ChatWithTools = class {
|
|
|
1987
3192
|
setTools(tools) {
|
|
1988
3193
|
this.chat.setTools(tools);
|
|
1989
3194
|
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Update prompt/tool optimization controls.
|
|
3197
|
+
*/
|
|
3198
|
+
setOptimizationConfig(config) {
|
|
3199
|
+
this.config.optimization = config;
|
|
3200
|
+
this.chat.setOptimizationConfig(config);
|
|
3201
|
+
}
|
|
3202
|
+
/**
|
|
3203
|
+
* Set the active tool profile used for request-time tool selection.
|
|
3204
|
+
*/
|
|
3205
|
+
setToolProfile(profile) {
|
|
3206
|
+
this.chat.setToolProfile(profile);
|
|
3207
|
+
}
|
|
3208
|
+
/**
|
|
3209
|
+
* Get the most recent prompt context usage snapshot.
|
|
3210
|
+
*/
|
|
3211
|
+
getContextUsage() {
|
|
3212
|
+
return this.chat.getContextUsage();
|
|
3213
|
+
}
|
|
1990
3214
|
/**
|
|
1991
3215
|
* Set dynamic context (from useAIContext hook)
|
|
1992
3216
|
*/
|
|
@@ -2020,6 +3244,15 @@ var ChatWithTools = class {
|
|
|
2020
3244
|
setBody(body) {
|
|
2021
3245
|
this.chat.setBody(body);
|
|
2022
3246
|
}
|
|
3247
|
+
setRequestMessageTransform(fn) {
|
|
3248
|
+
this.chat.setRequestMessageTransform(fn);
|
|
3249
|
+
}
|
|
3250
|
+
/**
|
|
3251
|
+
* Set inline skills (forwarded to underlying chat instance)
|
|
3252
|
+
*/
|
|
3253
|
+
setInlineSkills(skills) {
|
|
3254
|
+
this.chat.setInlineSkills(skills);
|
|
3255
|
+
}
|
|
2023
3256
|
// ============================================
|
|
2024
3257
|
// Tool Registration
|
|
2025
3258
|
// ============================================
|
|
@@ -2096,16 +3329,307 @@ var ChatWithTools = class {
|
|
|
2096
3329
|
// Private
|
|
2097
3330
|
// ============================================
|
|
2098
3331
|
debug(message, ...args) {
|
|
2099
|
-
|
|
2100
|
-
|
|
3332
|
+
chunkI3SQUNTT_cjs.createLogger("tools", () => this.config.debug ?? false)(
|
|
3333
|
+
message,
|
|
3334
|
+
args.length === 1 ? args[0] : args.length > 1 ? args : void 0
|
|
3335
|
+
);
|
|
3336
|
+
}
|
|
3337
|
+
};
|
|
3338
|
+
|
|
3339
|
+
// src/chat/branching/MessageTree.ts
|
|
3340
|
+
var _MessageTree = class _MessageTree {
|
|
3341
|
+
constructor(messages) {
|
|
3342
|
+
/** All messages by ID */
|
|
3343
|
+
this.nodeMap = /* @__PURE__ */ new Map();
|
|
3344
|
+
/** parentKey → ordered list of child IDs (insertion order = oldest-first) */
|
|
3345
|
+
this.childrenOf = /* @__PURE__ */ new Map();
|
|
3346
|
+
/** parentKey → currently-active child ID */
|
|
3347
|
+
this.activeChildMap = /* @__PURE__ */ new Map();
|
|
3348
|
+
/** Current leaf message ID (tip of the active path) */
|
|
3349
|
+
this._currentLeafId = null;
|
|
3350
|
+
/** Cached visible messages — invalidated on every mutation */
|
|
3351
|
+
this._visibleCache = null;
|
|
3352
|
+
if (messages?.length) {
|
|
3353
|
+
this._buildFromMessages(messages);
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
// ============================================
|
|
3357
|
+
// Static Migration Helpers
|
|
3358
|
+
// ============================================
|
|
3359
|
+
/**
|
|
3360
|
+
* Convert a legacy flat array (no parentId) to a tree-linked array.
|
|
3361
|
+
*
|
|
3362
|
+
* Rules:
|
|
3363
|
+
* - Tool messages get parentId = the owning assistant message's id
|
|
3364
|
+
* (matched via toolCallId → toolCall.id).
|
|
3365
|
+
* - All other messages get parentId of the previous non-tool message
|
|
3366
|
+
* (or null for the first message).
|
|
3367
|
+
*
|
|
3368
|
+
* Returns a new array with parentId/childrenIds filled in.
|
|
3369
|
+
* Does NOT mutate the original messages.
|
|
3370
|
+
*/
|
|
3371
|
+
static fromFlatArray(messages) {
|
|
3372
|
+
if (messages.length === 0) return messages;
|
|
3373
|
+
const alreadyLinked = messages.some((m) => m.parentId !== void 0);
|
|
3374
|
+
if (alreadyLinked) return messages;
|
|
3375
|
+
const result = [];
|
|
3376
|
+
let prevNonToolId = null;
|
|
3377
|
+
const assistantById = /* @__PURE__ */ new Map();
|
|
3378
|
+
for (const msg of messages) {
|
|
3379
|
+
if (msg.role === "assistant") {
|
|
3380
|
+
assistantById.set(msg.id, msg);
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
for (const msg of messages) {
|
|
3384
|
+
if (msg.role === "tool" && msg.toolCallId) {
|
|
3385
|
+
let ownerAssistantId = null;
|
|
3386
|
+
for (const [, assistant] of assistantById) {
|
|
3387
|
+
if (assistant.toolCalls?.some((tc) => tc.id === msg.toolCallId)) {
|
|
3388
|
+
ownerAssistantId = assistant.id;
|
|
3389
|
+
break;
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
result.push({
|
|
3393
|
+
...msg,
|
|
3394
|
+
parentId: ownerAssistantId ?? prevNonToolId,
|
|
3395
|
+
childrenIds: []
|
|
3396
|
+
});
|
|
3397
|
+
} else {
|
|
3398
|
+
result.push({
|
|
3399
|
+
...msg,
|
|
3400
|
+
parentId: prevNonToolId,
|
|
3401
|
+
childrenIds: []
|
|
3402
|
+
});
|
|
3403
|
+
prevNonToolId = msg.id;
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
const childrenMap = /* @__PURE__ */ new Map();
|
|
3407
|
+
for (const msg of result) {
|
|
3408
|
+
const parentKey = msg.parentId == null ? _MessageTree.ROOT_KEY : msg.parentId;
|
|
3409
|
+
if (!childrenMap.has(parentKey)) {
|
|
3410
|
+
childrenMap.set(parentKey, []);
|
|
3411
|
+
}
|
|
3412
|
+
childrenMap.get(parentKey).push(msg.id);
|
|
3413
|
+
}
|
|
3414
|
+
return result.map((msg) => ({
|
|
3415
|
+
...msg,
|
|
3416
|
+
childrenIds: childrenMap.get(msg.id) ?? []
|
|
3417
|
+
}));
|
|
3418
|
+
}
|
|
3419
|
+
// ============================================
|
|
3420
|
+
// Core Queries
|
|
3421
|
+
// ============================================
|
|
3422
|
+
/**
|
|
3423
|
+
* Returns the visible path (root → current leaf) — what the UI renders
|
|
3424
|
+
* and what gets sent to the API.
|
|
3425
|
+
*
|
|
3426
|
+
* Backward-compat: if NO message has parentId set (all undefined),
|
|
3427
|
+
* falls back to insertion order (legacy linear mode).
|
|
3428
|
+
*/
|
|
3429
|
+
getVisibleMessages() {
|
|
3430
|
+
if (this._visibleCache !== null) return this._visibleCache;
|
|
3431
|
+
if (this.nodeMap.size === 0) {
|
|
3432
|
+
this._visibleCache = [];
|
|
3433
|
+
return this._visibleCache;
|
|
3434
|
+
}
|
|
3435
|
+
const hasTreeStructure = Array.from(this.nodeMap.values()).some(
|
|
3436
|
+
(m) => m.parentId !== void 0
|
|
3437
|
+
);
|
|
3438
|
+
this._visibleCache = hasTreeStructure ? this._getActivePath().map((id) => this.nodeMap.get(id)) : Array.from(this.nodeMap.values());
|
|
3439
|
+
return this._visibleCache;
|
|
3440
|
+
}
|
|
3441
|
+
_invalidateCache() {
|
|
3442
|
+
this._visibleCache = null;
|
|
3443
|
+
}
|
|
3444
|
+
/**
|
|
3445
|
+
* Returns ALL messages across every branch (for persistence / ThreadManager).
|
|
3446
|
+
*/
|
|
3447
|
+
getAllMessages() {
|
|
3448
|
+
return Array.from(this.nodeMap.values());
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Branch navigation info for the UI navigator.
|
|
3452
|
+
* Returns null if the message has no siblings (only child).
|
|
3453
|
+
*/
|
|
3454
|
+
getBranchInfo(messageId) {
|
|
3455
|
+
const msg = this.nodeMap.get(messageId);
|
|
3456
|
+
if (!msg) return null;
|
|
3457
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3458
|
+
const siblings = this.childrenOf.get(parentKey) ?? [];
|
|
3459
|
+
if (siblings.length <= 1) return null;
|
|
3460
|
+
const siblingIndex = siblings.indexOf(messageId);
|
|
3461
|
+
return {
|
|
3462
|
+
siblingIndex,
|
|
3463
|
+
totalSiblings: siblings.length,
|
|
3464
|
+
siblingIds: [...siblings],
|
|
3465
|
+
hasPrevious: siblingIndex > 0,
|
|
3466
|
+
hasNext: siblingIndex < siblings.length - 1
|
|
3467
|
+
};
|
|
3468
|
+
}
|
|
3469
|
+
get currentLeafId() {
|
|
3470
|
+
return this._currentLeafId;
|
|
3471
|
+
}
|
|
3472
|
+
get hasBranches() {
|
|
3473
|
+
for (const children of this.childrenOf.values()) {
|
|
3474
|
+
if (children.length > 1) return true;
|
|
3475
|
+
}
|
|
3476
|
+
return false;
|
|
3477
|
+
}
|
|
3478
|
+
// ============================================
|
|
3479
|
+
// Mutations
|
|
3480
|
+
// ============================================
|
|
3481
|
+
/**
|
|
3482
|
+
* Insert a new message.
|
|
3483
|
+
* - Updates childrenOf and nodeMap.
|
|
3484
|
+
* - New branch becomes active (activeChildMap updated).
|
|
3485
|
+
* - Updates current leaf.
|
|
3486
|
+
*/
|
|
3487
|
+
addMessage(message) {
|
|
3488
|
+
this.nodeMap.set(message.id, message);
|
|
3489
|
+
const parentKey = this._parentKey(message.parentId);
|
|
3490
|
+
if (!this.childrenOf.has(parentKey)) {
|
|
3491
|
+
this.childrenOf.set(parentKey, []);
|
|
3492
|
+
}
|
|
3493
|
+
const siblings = this.childrenOf.get(parentKey);
|
|
3494
|
+
if (!siblings.includes(message.id)) {
|
|
3495
|
+
siblings.push(message.id);
|
|
3496
|
+
}
|
|
3497
|
+
this.activeChildMap.set(parentKey, message.id);
|
|
3498
|
+
this._currentLeafId = this._walkToLeaf(message.id);
|
|
3499
|
+
this._invalidateCache();
|
|
3500
|
+
return message;
|
|
3501
|
+
}
|
|
3502
|
+
/**
|
|
3503
|
+
* Navigate: make messageId the active child at its parent fork,
|
|
3504
|
+
* then walk to its leaf and update currentLeafId.
|
|
3505
|
+
*/
|
|
3506
|
+
switchBranch(messageId) {
|
|
3507
|
+
const msg = this.nodeMap.get(messageId);
|
|
3508
|
+
if (!msg) return;
|
|
3509
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3510
|
+
this.activeChildMap.set(parentKey, messageId);
|
|
3511
|
+
this._currentLeafId = this._walkToLeaf(messageId);
|
|
3512
|
+
this._invalidateCache();
|
|
3513
|
+
}
|
|
3514
|
+
/**
|
|
3515
|
+
* Update message content in-place (streaming updates).
|
|
3516
|
+
* No tree structure change.
|
|
3517
|
+
*/
|
|
3518
|
+
updateMessage(id, updater) {
|
|
3519
|
+
const existing = this.nodeMap.get(id);
|
|
3520
|
+
if (!existing) return false;
|
|
3521
|
+
this.nodeMap.set(id, updater(existing));
|
|
3522
|
+
this._invalidateCache();
|
|
3523
|
+
return true;
|
|
3524
|
+
}
|
|
3525
|
+
/**
|
|
3526
|
+
* Set current leaf explicitly.
|
|
3527
|
+
* Used by regenerate() to rewind the active path before pushing a new message.
|
|
3528
|
+
*/
|
|
3529
|
+
setCurrentLeaf(leafId) {
|
|
3530
|
+
this._currentLeafId = leafId;
|
|
3531
|
+
if (leafId === null) return;
|
|
3532
|
+
const msg = this.nodeMap.get(leafId);
|
|
3533
|
+
if (!msg) return;
|
|
3534
|
+
let current = msg;
|
|
3535
|
+
while (current) {
|
|
3536
|
+
const parentKey = this._parentKey(current.parentId);
|
|
3537
|
+
this.activeChildMap.set(parentKey, current.id);
|
|
3538
|
+
if (current.parentId == null || current.parentId === void 0) break;
|
|
3539
|
+
current = this.nodeMap.get(current.parentId);
|
|
3540
|
+
}
|
|
3541
|
+
this._invalidateCache();
|
|
3542
|
+
}
|
|
3543
|
+
/**
|
|
3544
|
+
* Rebuild entire tree from a message array.
|
|
3545
|
+
* Used by setMessages().
|
|
3546
|
+
*/
|
|
3547
|
+
reset(messages) {
|
|
3548
|
+
this.nodeMap.clear();
|
|
3549
|
+
this.childrenOf.clear();
|
|
3550
|
+
this.activeChildMap.clear();
|
|
3551
|
+
this._currentLeafId = null;
|
|
3552
|
+
this._invalidateCache();
|
|
3553
|
+
if (messages.length > 0) {
|
|
3554
|
+
this._buildFromMessages(messages);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
// ============================================
|
|
3558
|
+
// Private Helpers
|
|
3559
|
+
// ============================================
|
|
3560
|
+
_buildFromMessages(messages) {
|
|
3561
|
+
const linked = messages.some((m) => m.parentId !== void 0) ? messages : _MessageTree.fromFlatArray(messages);
|
|
3562
|
+
for (const msg of linked) {
|
|
3563
|
+
this.nodeMap.set(msg.id, msg);
|
|
3564
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3565
|
+
if (!this.childrenOf.has(parentKey)) {
|
|
3566
|
+
this.childrenOf.set(parentKey, []);
|
|
3567
|
+
}
|
|
3568
|
+
const siblings = this.childrenOf.get(parentKey);
|
|
3569
|
+
if (!siblings.includes(msg.id)) {
|
|
3570
|
+
siblings.push(msg.id);
|
|
3571
|
+
}
|
|
3572
|
+
}
|
|
3573
|
+
for (const [parentKey, children] of this.childrenOf) {
|
|
3574
|
+
if (children.length > 0) {
|
|
3575
|
+
this.activeChildMap.set(parentKey, children[children.length - 1]);
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
const path = this._getActivePath();
|
|
3579
|
+
this._currentLeafId = path.length > 0 ? path[path.length - 1] : null;
|
|
3580
|
+
}
|
|
3581
|
+
_parentKey(parentId) {
|
|
3582
|
+
if (parentId == null || parentId === void 0) {
|
|
3583
|
+
return _MessageTree.ROOT_KEY;
|
|
3584
|
+
}
|
|
3585
|
+
return parentId;
|
|
3586
|
+
}
|
|
3587
|
+
/**
|
|
3588
|
+
* Walk forward from a message along active children to find the leaf.
|
|
3589
|
+
*/
|
|
3590
|
+
_walkToLeaf(fromId) {
|
|
3591
|
+
let current = fromId;
|
|
3592
|
+
while (true) {
|
|
3593
|
+
const children = this.childrenOf.get(current);
|
|
3594
|
+
if (!children || children.length === 0) break;
|
|
3595
|
+
const activeChild = this.activeChildMap.get(current);
|
|
3596
|
+
if (!activeChild) break;
|
|
3597
|
+
if (!this.nodeMap.has(activeChild)) break;
|
|
3598
|
+
current = activeChild;
|
|
3599
|
+
}
|
|
3600
|
+
return current;
|
|
3601
|
+
}
|
|
3602
|
+
/**
|
|
3603
|
+
* Walk the active path from root to the current leaf.
|
|
3604
|
+
*/
|
|
3605
|
+
_getActivePath() {
|
|
3606
|
+
const path = [];
|
|
3607
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3608
|
+
const rootChildren = this.childrenOf.get(_MessageTree.ROOT_KEY) ?? [];
|
|
3609
|
+
if (rootChildren.length === 0) return path;
|
|
3610
|
+
let activeId = this.activeChildMap.get(_MessageTree.ROOT_KEY);
|
|
3611
|
+
if (!activeId) {
|
|
3612
|
+
activeId = rootChildren[rootChildren.length - 1];
|
|
2101
3613
|
}
|
|
3614
|
+
let current = activeId;
|
|
3615
|
+
while (current && !visited.has(current)) {
|
|
3616
|
+
if (!this.nodeMap.has(current)) break;
|
|
3617
|
+
visited.add(current);
|
|
3618
|
+
path.push(current);
|
|
3619
|
+
const activeChild = this.activeChildMap.get(current);
|
|
3620
|
+
if (!activeChild || !this.nodeMap.has(activeChild)) break;
|
|
3621
|
+
current = activeChild;
|
|
3622
|
+
}
|
|
3623
|
+
return path;
|
|
2102
3624
|
}
|
|
2103
3625
|
};
|
|
3626
|
+
/** Sentinel key used for root-level messages (parentId === null) */
|
|
3627
|
+
_MessageTree.ROOT_KEY = "__root__";
|
|
3628
|
+
var MessageTree = _MessageTree;
|
|
2104
3629
|
|
|
2105
3630
|
// src/react/internal/ReactChatState.ts
|
|
2106
3631
|
var ReactChatState = class {
|
|
2107
3632
|
constructor(initialMessages) {
|
|
2108
|
-
this._messages = [];
|
|
2109
3633
|
this._status = "ready";
|
|
2110
3634
|
this._error = void 0;
|
|
2111
3635
|
// Callbacks for React subscriptions (useSyncExternalStore)
|
|
@@ -2131,15 +3655,19 @@ var ReactChatState = class {
|
|
|
2131
3655
|
this.subscribers.delete(callback);
|
|
2132
3656
|
};
|
|
2133
3657
|
};
|
|
2134
|
-
|
|
2135
|
-
this._messages = initialMessages;
|
|
2136
|
-
}
|
|
3658
|
+
this.tree = new MessageTree(initialMessages);
|
|
2137
3659
|
}
|
|
2138
3660
|
// ============================================
|
|
2139
|
-
// Getters
|
|
3661
|
+
// Getters — visible path only
|
|
2140
3662
|
// ============================================
|
|
3663
|
+
/**
|
|
3664
|
+
* Returns the VISIBLE PATH (active branch) — what the UI renders
|
|
3665
|
+
* and what gets sent to the API.
|
|
3666
|
+
*
|
|
3667
|
+
* For all messages across all branches, use getAllMessages().
|
|
3668
|
+
*/
|
|
2141
3669
|
get messages() {
|
|
2142
|
-
return this.
|
|
3670
|
+
return this.tree.getVisibleMessages();
|
|
2143
3671
|
}
|
|
2144
3672
|
get status() {
|
|
2145
3673
|
return this._status;
|
|
@@ -2151,7 +3679,7 @@ var ReactChatState = class {
|
|
|
2151
3679
|
// Setters (trigger reactivity)
|
|
2152
3680
|
// ============================================
|
|
2153
3681
|
set messages(value) {
|
|
2154
|
-
this.
|
|
3682
|
+
this.tree.reset(value);
|
|
2155
3683
|
this.notify();
|
|
2156
3684
|
}
|
|
2157
3685
|
set status(value) {
|
|
@@ -2166,63 +3694,102 @@ var ReactChatState = class {
|
|
|
2166
3694
|
// Mutations
|
|
2167
3695
|
// ============================================
|
|
2168
3696
|
pushMessage(message) {
|
|
2169
|
-
this.
|
|
3697
|
+
this.tree.addMessage(message);
|
|
2170
3698
|
this.notify();
|
|
2171
3699
|
}
|
|
2172
3700
|
popMessage() {
|
|
2173
|
-
|
|
3701
|
+
const leafId = this.tree.currentLeafId;
|
|
3702
|
+
if (!leafId) return;
|
|
3703
|
+
const allMessages = this.tree.getAllMessages().filter((m) => m.id !== leafId);
|
|
3704
|
+
const leaf = this.tree.getAllMessages().find((m) => m.id === leafId);
|
|
3705
|
+
const newLeafId = leaf && leaf.parentId !== void 0 && leaf.parentId !== null ? leaf.parentId : null;
|
|
3706
|
+
this.tree.reset(allMessages);
|
|
3707
|
+
if (newLeafId) {
|
|
3708
|
+
this.tree.setCurrentLeaf(newLeafId);
|
|
3709
|
+
}
|
|
2174
3710
|
this.notify();
|
|
2175
3711
|
}
|
|
2176
3712
|
replaceMessage(index, message) {
|
|
2177
|
-
|
|
3713
|
+
const visible = this.tree.getVisibleMessages();
|
|
3714
|
+
const target = visible[index];
|
|
3715
|
+
if (!target) return;
|
|
3716
|
+
this.tree.updateMessage(target.id, () => message);
|
|
2178
3717
|
this.notify();
|
|
2179
3718
|
}
|
|
2180
3719
|
updateLastMessage(updater) {
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
this._messages = [
|
|
2185
|
-
...this._messages.slice(0, lastIndex),
|
|
2186
|
-
updater(lastMessage)
|
|
2187
|
-
];
|
|
3720
|
+
const leafId = this.tree.currentLeafId;
|
|
3721
|
+
if (!leafId) return;
|
|
3722
|
+
this.tree.updateMessage(leafId, updater);
|
|
2188
3723
|
this.notify();
|
|
2189
3724
|
}
|
|
2190
3725
|
updateMessageById(id, updater) {
|
|
2191
|
-
const
|
|
2192
|
-
if (
|
|
2193
|
-
|
|
2194
|
-
(m, i) => i === index ? updater(m) : m
|
|
2195
|
-
);
|
|
2196
|
-
this.notify();
|
|
2197
|
-
return true;
|
|
3726
|
+
const updated = this.tree.updateMessage(id, updater);
|
|
3727
|
+
if (updated) this.notify();
|
|
3728
|
+
return updated;
|
|
2198
3729
|
}
|
|
2199
3730
|
setMessages(messages) {
|
|
2200
|
-
this.
|
|
3731
|
+
this.tree.reset(messages);
|
|
2201
3732
|
this.notify();
|
|
2202
3733
|
}
|
|
2203
3734
|
clearMessages() {
|
|
2204
|
-
this.
|
|
3735
|
+
this.tree.reset([]);
|
|
2205
3736
|
this.notify();
|
|
2206
3737
|
}
|
|
2207
3738
|
// ============================================
|
|
2208
|
-
//
|
|
3739
|
+
// Branching API
|
|
2209
3740
|
// ============================================
|
|
2210
|
-
notify() {
|
|
2211
|
-
this.subscribers.forEach((cb) => cb());
|
|
2212
|
-
}
|
|
2213
3741
|
/**
|
|
2214
|
-
*
|
|
3742
|
+
* Returns ALL messages across all branches.
|
|
3743
|
+
* Use this for persistence (ThreadManager save).
|
|
2215
3744
|
*/
|
|
2216
|
-
|
|
2217
|
-
this.
|
|
3745
|
+
getAllMessages() {
|
|
3746
|
+
return this.tree.getAllMessages();
|
|
2218
3747
|
}
|
|
2219
3748
|
/**
|
|
2220
|
-
*
|
|
2221
|
-
*
|
|
3749
|
+
* Get branch navigation info for a message.
|
|
3750
|
+
* Returns null if the message has no siblings.
|
|
2222
3751
|
*/
|
|
2223
|
-
|
|
3752
|
+
getBranchInfo(messageId) {
|
|
3753
|
+
return this.tree.getBranchInfo(messageId);
|
|
2224
3754
|
}
|
|
2225
|
-
|
|
3755
|
+
/**
|
|
3756
|
+
* Navigate to a sibling branch.
|
|
3757
|
+
* Triggers re-render via notify().
|
|
3758
|
+
*/
|
|
3759
|
+
switchBranch(messageId) {
|
|
3760
|
+
this.tree.switchBranch(messageId);
|
|
3761
|
+
this.notify();
|
|
3762
|
+
}
|
|
3763
|
+
/**
|
|
3764
|
+
* Set the current leaf (used by regenerate() to rewind active path).
|
|
3765
|
+
* Triggers re-render via notify().
|
|
3766
|
+
*/
|
|
3767
|
+
setCurrentLeaf(leafId) {
|
|
3768
|
+
this.tree.setCurrentLeaf(leafId);
|
|
3769
|
+
this.notify();
|
|
3770
|
+
}
|
|
3771
|
+
get hasBranches() {
|
|
3772
|
+
return this.tree.hasBranches;
|
|
3773
|
+
}
|
|
3774
|
+
// ============================================
|
|
3775
|
+
// Private Methods
|
|
3776
|
+
// ============================================
|
|
3777
|
+
notify() {
|
|
3778
|
+
this.subscribers.forEach((cb) => cb());
|
|
3779
|
+
}
|
|
3780
|
+
/**
|
|
3781
|
+
* Cleanup subscriptions
|
|
3782
|
+
*/
|
|
3783
|
+
dispose() {
|
|
3784
|
+
this.subscribers.clear();
|
|
3785
|
+
}
|
|
3786
|
+
/**
|
|
3787
|
+
* Revive after dispose (for React StrictMode compatibility)
|
|
3788
|
+
* Subscribers will be re-added automatically via useSyncExternalStore
|
|
3789
|
+
*/
|
|
3790
|
+
revive() {
|
|
3791
|
+
}
|
|
3792
|
+
};
|
|
2226
3793
|
function createReactChatState(initialMessages) {
|
|
2227
3794
|
return new ReactChatState(initialMessages);
|
|
2228
3795
|
}
|
|
@@ -2240,6 +3807,33 @@ var ReactChatWithTools = class extends ChatWithTools {
|
|
|
2240
3807
|
};
|
|
2241
3808
|
this.reactState = reactState;
|
|
2242
3809
|
}
|
|
3810
|
+
// ============================================
|
|
3811
|
+
// Branching API — pass-throughs to ReactChatState
|
|
3812
|
+
// ============================================
|
|
3813
|
+
/**
|
|
3814
|
+
* Navigate to a sibling branch.
|
|
3815
|
+
*/
|
|
3816
|
+
switchBranch(messageId) {
|
|
3817
|
+
this.reactState.switchBranch(messageId);
|
|
3818
|
+
}
|
|
3819
|
+
/**
|
|
3820
|
+
* Get branch navigation info for a message.
|
|
3821
|
+
*/
|
|
3822
|
+
getBranchInfo(messageId) {
|
|
3823
|
+
return this.reactState.getBranchInfo(messageId);
|
|
3824
|
+
}
|
|
3825
|
+
/**
|
|
3826
|
+
* Get all messages across all branches (for persistence).
|
|
3827
|
+
*/
|
|
3828
|
+
getAllMessages() {
|
|
3829
|
+
return this.reactState.getAllMessages();
|
|
3830
|
+
}
|
|
3831
|
+
/**
|
|
3832
|
+
* Whether any message has siblings (branching has occurred).
|
|
3833
|
+
*/
|
|
3834
|
+
get hasBranches() {
|
|
3835
|
+
return this.reactState.hasBranches;
|
|
3836
|
+
}
|
|
2243
3837
|
/**
|
|
2244
3838
|
* Dispose and cleanup
|
|
2245
3839
|
*/
|
|
@@ -2337,13 +3931,13 @@ function useMCPClient(config) {
|
|
|
2337
3931
|
onNotification,
|
|
2338
3932
|
...clientConfig
|
|
2339
3933
|
} = config;
|
|
2340
|
-
const clientRef =
|
|
2341
|
-
const mountedRef =
|
|
2342
|
-
const [state, setState] =
|
|
3934
|
+
const clientRef = React2.useRef(null);
|
|
3935
|
+
const mountedRef = React2.useRef(true);
|
|
3936
|
+
const [state, setState] = React2.useState({
|
|
2343
3937
|
connectionState: "disconnected",
|
|
2344
3938
|
tools: []
|
|
2345
3939
|
});
|
|
2346
|
-
const getClient =
|
|
3940
|
+
const getClient = React2.useCallback(() => {
|
|
2347
3941
|
if (!clientRef.current) {
|
|
2348
3942
|
clientRef.current = chunkJGPDQDY4_cjs.createMCPClient(clientConfig, {
|
|
2349
3943
|
onConnectionStateChange: (newState) => {
|
|
@@ -2377,7 +3971,7 @@ function useMCPClient(config) {
|
|
|
2377
3971
|
onError,
|
|
2378
3972
|
onNotification
|
|
2379
3973
|
]);
|
|
2380
|
-
const connect =
|
|
3974
|
+
const connect = React2.useCallback(async () => {
|
|
2381
3975
|
const client = getClient();
|
|
2382
3976
|
try {
|
|
2383
3977
|
setState((prev) => ({
|
|
@@ -2408,7 +4002,7 @@ function useMCPClient(config) {
|
|
|
2408
4002
|
throw error;
|
|
2409
4003
|
}
|
|
2410
4004
|
}, [getClient]);
|
|
2411
|
-
const disconnect =
|
|
4005
|
+
const disconnect = React2.useCallback(async () => {
|
|
2412
4006
|
const client = clientRef.current;
|
|
2413
4007
|
if (client) {
|
|
2414
4008
|
await client.disconnect();
|
|
@@ -2420,7 +4014,7 @@ function useMCPClient(config) {
|
|
|
2420
4014
|
}
|
|
2421
4015
|
}
|
|
2422
4016
|
}, []);
|
|
2423
|
-
const callTool =
|
|
4017
|
+
const callTool = React2.useCallback(
|
|
2424
4018
|
async (name, args) => {
|
|
2425
4019
|
const client = clientRef.current;
|
|
2426
4020
|
if (!client || !client.isConnected()) {
|
|
@@ -2434,7 +4028,7 @@ function useMCPClient(config) {
|
|
|
2434
4028
|
},
|
|
2435
4029
|
[]
|
|
2436
4030
|
);
|
|
2437
|
-
const refreshTools =
|
|
4031
|
+
const refreshTools = React2.useCallback(async () => {
|
|
2438
4032
|
const client = clientRef.current;
|
|
2439
4033
|
if (!client || !client.isConnected()) {
|
|
2440
4034
|
throw new Error("MCP client not connected");
|
|
@@ -2445,7 +4039,7 @@ function useMCPClient(config) {
|
|
|
2445
4039
|
}
|
|
2446
4040
|
return tools;
|
|
2447
4041
|
}, []);
|
|
2448
|
-
|
|
4042
|
+
React2.useEffect(() => {
|
|
2449
4043
|
mountedRef.current = true;
|
|
2450
4044
|
if (autoConnect) {
|
|
2451
4045
|
connect().catch(() => {
|
|
@@ -2484,13 +4078,13 @@ function useMCPTools(config) {
|
|
|
2484
4078
|
...clientConfig
|
|
2485
4079
|
} = config;
|
|
2486
4080
|
const { registerTool, unregisterTool } = useCopilot();
|
|
2487
|
-
const registeredToolsRef =
|
|
4081
|
+
const registeredToolsRef = React2.useRef([]);
|
|
2488
4082
|
const mcpClient = useMCPClient(clientConfig);
|
|
2489
|
-
const toolAdapter =
|
|
4083
|
+
const toolAdapter = React2.useMemo(
|
|
2490
4084
|
() => new chunkJGPDQDY4_cjs.MCPToolAdapter(clientConfig.name),
|
|
2491
4085
|
[clientConfig.name]
|
|
2492
4086
|
);
|
|
2493
|
-
const toolDefinitions =
|
|
4087
|
+
const toolDefinitions = React2.useMemo(() => {
|
|
2494
4088
|
if (!mcpClient.isConnected || mcpClient.state.tools.length === 0) {
|
|
2495
4089
|
return [];
|
|
2496
4090
|
}
|
|
@@ -2515,7 +4109,7 @@ function useMCPTools(config) {
|
|
|
2515
4109
|
hidden,
|
|
2516
4110
|
source
|
|
2517
4111
|
]);
|
|
2518
|
-
|
|
4112
|
+
React2.useEffect(() => {
|
|
2519
4113
|
if (!autoRegister) {
|
|
2520
4114
|
return;
|
|
2521
4115
|
}
|
|
@@ -2530,22 +4124,919 @@ function useMCPTools(config) {
|
|
|
2530
4124
|
}
|
|
2531
4125
|
}
|
|
2532
4126
|
return () => {
|
|
2533
|
-
for (const toolName of registeredToolsRef.current) {
|
|
2534
|
-
unregisterTool(toolName);
|
|
4127
|
+
for (const toolName of registeredToolsRef.current) {
|
|
4128
|
+
unregisterTool(toolName);
|
|
4129
|
+
}
|
|
4130
|
+
registeredToolsRef.current = [];
|
|
4131
|
+
};
|
|
4132
|
+
}, [
|
|
4133
|
+
autoRegister,
|
|
4134
|
+
mcpClient.isConnected,
|
|
4135
|
+
toolDefinitions,
|
|
4136
|
+
registerTool,
|
|
4137
|
+
unregisterTool
|
|
4138
|
+
]);
|
|
4139
|
+
return {
|
|
4140
|
+
...mcpClient,
|
|
4141
|
+
toolDefinitions
|
|
4142
|
+
};
|
|
4143
|
+
}
|
|
4144
|
+
var defaultTokenUsage = {
|
|
4145
|
+
current: 0,
|
|
4146
|
+
max: 128e3,
|
|
4147
|
+
percentage: 0,
|
|
4148
|
+
isApproaching: false
|
|
4149
|
+
};
|
|
4150
|
+
var defaultCompactionState = {
|
|
4151
|
+
rollingSummary: null,
|
|
4152
|
+
lastCompactionAt: null,
|
|
4153
|
+
compactionCount: 0,
|
|
4154
|
+
totalTokensSaved: 0,
|
|
4155
|
+
workingMemory: [],
|
|
4156
|
+
displayMessageCount: 0,
|
|
4157
|
+
llmMessageCount: 0
|
|
4158
|
+
};
|
|
4159
|
+
var defaultMessageHistoryConfig = {
|
|
4160
|
+
strategy: "none",
|
|
4161
|
+
maxContextTokens: 128e3,
|
|
4162
|
+
reserveForResponse: 4096,
|
|
4163
|
+
compactionThreshold: 0.75,
|
|
4164
|
+
recentBuffer: 10,
|
|
4165
|
+
toolResultMaxChars: 1e4,
|
|
4166
|
+
persistSession: false,
|
|
4167
|
+
storageKey: "copilot-session"
|
|
4168
|
+
};
|
|
4169
|
+
var MessageHistoryContext = React2.createContext({
|
|
4170
|
+
config: defaultMessageHistoryConfig,
|
|
4171
|
+
tokenUsage: defaultTokenUsage,
|
|
4172
|
+
compactionState: defaultCompactionState
|
|
4173
|
+
});
|
|
4174
|
+
function useMessageHistoryContext() {
|
|
4175
|
+
return React2.useContext(MessageHistoryContext);
|
|
4176
|
+
}
|
|
4177
|
+
|
|
4178
|
+
// src/react/message-history/message-utils.ts
|
|
4179
|
+
function toDisplayMessage(msg) {
|
|
4180
|
+
return {
|
|
4181
|
+
...msg,
|
|
4182
|
+
timestamp: msg.createdAt instanceof Date ? msg.createdAt.getTime() : Date.now()
|
|
4183
|
+
};
|
|
4184
|
+
}
|
|
4185
|
+
function toLLMMessage(msg) {
|
|
4186
|
+
if (isCompactionMarker(msg)) {
|
|
4187
|
+
return {
|
|
4188
|
+
role: "system",
|
|
4189
|
+
content: `[Previous conversation summary]
|
|
4190
|
+
${msg.content}`
|
|
4191
|
+
};
|
|
4192
|
+
}
|
|
4193
|
+
const base = {
|
|
4194
|
+
role: msg.role,
|
|
4195
|
+
content: msg.content
|
|
4196
|
+
};
|
|
4197
|
+
if (msg.toolCalls?.length) {
|
|
4198
|
+
base.tool_calls = msg.toolCalls;
|
|
4199
|
+
}
|
|
4200
|
+
if (msg.toolCallId) {
|
|
4201
|
+
base.tool_call_id = msg.toolCallId;
|
|
4202
|
+
}
|
|
4203
|
+
return base;
|
|
4204
|
+
}
|
|
4205
|
+
function toLLMMessages(messages) {
|
|
4206
|
+
return messages.map(toLLMMessage);
|
|
4207
|
+
}
|
|
4208
|
+
function keepToolPairsAtomic(messages) {
|
|
4209
|
+
if (messages.length === 0) return messages;
|
|
4210
|
+
const firstIdx = messages.findIndex((msg, i) => {
|
|
4211
|
+
if (msg.role !== "assistant" || !msg.toolCalls?.length) return false;
|
|
4212
|
+
const toolCallIds = new Set(msg.toolCalls.map((tc) => tc.id));
|
|
4213
|
+
const resultIds = new Set(
|
|
4214
|
+
messages.slice(i + 1).filter((m) => m.role === "tool" && m.toolCallId).map((m) => m.toolCallId)
|
|
4215
|
+
);
|
|
4216
|
+
return [...toolCallIds].some((id) => !resultIds.has(id));
|
|
4217
|
+
});
|
|
4218
|
+
if (firstIdx === -1) return messages;
|
|
4219
|
+
return messages.slice(firstIdx);
|
|
4220
|
+
}
|
|
4221
|
+
function findSafeWindowStart(messages, desiredStart) {
|
|
4222
|
+
for (let i = desiredStart; i < messages.length; i++) {
|
|
4223
|
+
const msg = messages[i];
|
|
4224
|
+
if (msg.role === "user" || msg.role === "assistant" && !msg.toolCalls?.length) {
|
|
4225
|
+
return i;
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
return desiredStart;
|
|
4229
|
+
}
|
|
4230
|
+
function isCompactionMarker(msg) {
|
|
4231
|
+
return msg.role === "system" && msg.type === "compaction-marker";
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4234
|
+
// src/react/message-history/token-counter.ts
|
|
4235
|
+
function estimateMessageTokens2(msg) {
|
|
4236
|
+
let chars = msg.content?.length ?? 0;
|
|
4237
|
+
if (msg.tool_calls?.length) {
|
|
4238
|
+
for (const tc of msg.tool_calls) {
|
|
4239
|
+
chars += JSON.stringify(tc).length;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
return Math.ceil(chars / 3.5) + 4;
|
|
4243
|
+
}
|
|
4244
|
+
function estimateMessagesTokens(messages) {
|
|
4245
|
+
return messages.reduce((sum, msg) => sum + estimateMessageTokens2(msg), 0);
|
|
4246
|
+
}
|
|
4247
|
+
function estimateTokens2(messages, mode = "fast") {
|
|
4248
|
+
if (mode === "off") return 0;
|
|
4249
|
+
return estimateMessagesTokens(messages);
|
|
4250
|
+
}
|
|
4251
|
+
|
|
4252
|
+
// src/react/message-history/strategies/sliding-window.ts
|
|
4253
|
+
function applySlidingWindow(messages, options) {
|
|
4254
|
+
const { tokenBudget, recentBuffer } = options;
|
|
4255
|
+
if (messages.length === 0) return messages;
|
|
4256
|
+
const systemMessages = messages.filter(
|
|
4257
|
+
(m) => m.role === "system" || isCompactionMarker(m)
|
|
4258
|
+
);
|
|
4259
|
+
const conversationMessages = messages.filter(
|
|
4260
|
+
(m) => m.role !== "system" && !isCompactionMarker(m)
|
|
4261
|
+
);
|
|
4262
|
+
const systemTokens = estimateMessagesTokens(toLLMMessages(systemMessages));
|
|
4263
|
+
const remainingBudget = tokenBudget - systemTokens;
|
|
4264
|
+
if (conversationMessages.length === 0) return systemMessages;
|
|
4265
|
+
const recentStart = Math.max(0, conversationMessages.length - recentBuffer);
|
|
4266
|
+
const recent = conversationMessages.slice(recentStart);
|
|
4267
|
+
const older = conversationMessages.slice(0, recentStart);
|
|
4268
|
+
const allTokens = estimateMessagesTokens(toLLMMessages(conversationMessages));
|
|
4269
|
+
if (allTokens <= remainingBudget) {
|
|
4270
|
+
return messages;
|
|
4271
|
+
}
|
|
4272
|
+
const recentTokens = estimateMessagesTokens(toLLMMessages(recent));
|
|
4273
|
+
let available = remainingBudget - recentTokens;
|
|
4274
|
+
const included = [];
|
|
4275
|
+
for (let i = older.length - 1; i >= 0; i--) {
|
|
4276
|
+
const msgTokens = estimateMessagesTokens(toLLMMessages([older[i]]));
|
|
4277
|
+
if (available - msgTokens < 0) break;
|
|
4278
|
+
included.unshift(older[i]);
|
|
4279
|
+
available -= msgTokens;
|
|
4280
|
+
}
|
|
4281
|
+
const combined = [...included, ...recent];
|
|
4282
|
+
const safeStart = findSafeWindowStart(combined, 0);
|
|
4283
|
+
const safeWindow = combined.slice(safeStart);
|
|
4284
|
+
return [...systemMessages, ...safeWindow];
|
|
4285
|
+
}
|
|
4286
|
+
function truncateToolResults(messages, maxChars) {
|
|
4287
|
+
if (maxChars === 0) return messages;
|
|
4288
|
+
return messages.map((msg) => {
|
|
4289
|
+
if (msg.role !== "tool") return msg;
|
|
4290
|
+
if (!msg.content || msg.content.length <= maxChars) return msg;
|
|
4291
|
+
return {
|
|
4292
|
+
...msg,
|
|
4293
|
+
content: msg.content.slice(0, maxChars) + `
|
|
4294
|
+
[truncated \u2014 original ${msg.content.length} chars, limit ${maxChars}]`
|
|
4295
|
+
};
|
|
4296
|
+
});
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4299
|
+
// src/react/message-history/strategies/selective-prune.ts
|
|
4300
|
+
function applySelectivePrune(displayMessages, recentBuffer, options = {}) {
|
|
4301
|
+
const {
|
|
4302
|
+
toolResultAgeTurns = 3,
|
|
4303
|
+
stripOldReasoning = true,
|
|
4304
|
+
deduplicateSkills = true
|
|
4305
|
+
} = options;
|
|
4306
|
+
const cutoff = Math.max(0, displayMessages.length - recentBuffer);
|
|
4307
|
+
const seenSkillContent = /* @__PURE__ */ new Set();
|
|
4308
|
+
return displayMessages.map((msg, idx) => {
|
|
4309
|
+
const llm = toLLMMessage(msg);
|
|
4310
|
+
const isOld = idx < cutoff;
|
|
4311
|
+
if (deduplicateSkills && msg.role === "system" && llm.content) {
|
|
4312
|
+
const key = llm.content.slice(0, 100);
|
|
4313
|
+
if (seenSkillContent.has(key)) {
|
|
4314
|
+
return { ...llm, content: "[skill instruction \u2014 deduplicated]" };
|
|
4315
|
+
}
|
|
4316
|
+
seenSkillContent.add(key);
|
|
4317
|
+
}
|
|
4318
|
+
if (!isOld) return llm;
|
|
4319
|
+
if (stripOldReasoning && msg.role === "assistant" && msg.thinking) {
|
|
4320
|
+
llm.content = llm.content;
|
|
4321
|
+
}
|
|
4322
|
+
if (msg.role === "tool" && llm.content) {
|
|
4323
|
+
const originalSize = llm.content.length;
|
|
4324
|
+
if (originalSize > 500) {
|
|
4325
|
+
const stub = buildToolResultStub(msg, llm.content);
|
|
4326
|
+
return {
|
|
4327
|
+
role: "tool",
|
|
4328
|
+
tool_call_id: llm.tool_call_id,
|
|
4329
|
+
content: JSON.stringify(stub)
|
|
4330
|
+
};
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
4333
|
+
return llm;
|
|
4334
|
+
});
|
|
4335
|
+
}
|
|
4336
|
+
function buildToolResultStub(msg, content) {
|
|
4337
|
+
return {
|
|
4338
|
+
type: "compacted-tool-result",
|
|
4339
|
+
toolName: msg.metadata?.toolName ?? "tool",
|
|
4340
|
+
toolCallId: msg.toolCallId ?? "",
|
|
4341
|
+
args: msg.metadata?.toolArgs ?? {},
|
|
4342
|
+
executedAt: msg.timestamp,
|
|
4343
|
+
status: content.includes('"error"') ? "error" : "success",
|
|
4344
|
+
originalSize: content.length,
|
|
4345
|
+
summary: buildSummary(content),
|
|
4346
|
+
extract: content.slice(0, 200)
|
|
4347
|
+
};
|
|
4348
|
+
}
|
|
4349
|
+
function buildSummary(content) {
|
|
4350
|
+
try {
|
|
4351
|
+
const parsed = JSON.parse(content);
|
|
4352
|
+
if (parsed?.message) return String(parsed.message).slice(0, 120);
|
|
4353
|
+
if (parsed?.error) return `Error: ${String(parsed.error).slice(0, 100)}`;
|
|
4354
|
+
if (Array.isArray(parsed)) return `Array result \u2014 ${parsed.length} items`;
|
|
4355
|
+
const keys = Object.keys(parsed).slice(0, 3).join(", ");
|
|
4356
|
+
return `Object result \u2014 keys: ${keys}`;
|
|
4357
|
+
} catch {
|
|
4358
|
+
return content.slice(0, 120);
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
|
|
4362
|
+
// src/react/message-history/strategies/summary-buffer.ts
|
|
4363
|
+
function buildSummaryBufferContext(displayMessages, compactionState, options) {
|
|
4364
|
+
const { recentBuffer } = options;
|
|
4365
|
+
const systemMessages = displayMessages.filter(
|
|
4366
|
+
(m) => m.role === "system" || isCompactionMarker(m)
|
|
4367
|
+
);
|
|
4368
|
+
const conversationMessages = displayMessages.filter(
|
|
4369
|
+
(m) => m.role !== "system" && !isCompactionMarker(m)
|
|
4370
|
+
);
|
|
4371
|
+
const recentStart = Math.max(0, conversationMessages.length - recentBuffer);
|
|
4372
|
+
const recentMessages = conversationMessages.slice(recentStart);
|
|
4373
|
+
const result = [];
|
|
4374
|
+
if (compactionState.workingMemory.length > 0) {
|
|
4375
|
+
result.push({
|
|
4376
|
+
role: "system",
|
|
4377
|
+
content: `[Working memory \u2014 always active]
|
|
4378
|
+
${compactionState.workingMemory.join("\n")}`
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
if (compactionState.rollingSummary) {
|
|
4382
|
+
result.push({
|
|
4383
|
+
role: "system",
|
|
4384
|
+
content: `[Previous conversation summary]
|
|
4385
|
+
${compactionState.rollingSummary}`
|
|
4386
|
+
});
|
|
4387
|
+
}
|
|
4388
|
+
result.push(...toLLMMessages(systemMessages));
|
|
4389
|
+
result.push(...toLLMMessages(recentMessages));
|
|
4390
|
+
return result;
|
|
4391
|
+
}
|
|
4392
|
+
async function runCompaction(displayMessages, compactionState, options) {
|
|
4393
|
+
const { recentBuffer, compactionUrl, summarizer } = options;
|
|
4394
|
+
const conversationMessages = displayMessages.filter(
|
|
4395
|
+
(m) => m.role !== "system" && !isCompactionMarker(m)
|
|
4396
|
+
);
|
|
4397
|
+
const cutoff = Math.max(0, conversationMessages.length - recentBuffer);
|
|
4398
|
+
const toSummarize = conversationMessages.slice(0, cutoff);
|
|
4399
|
+
if (toSummarize.length === 0) {
|
|
4400
|
+
return { llmMessages: toLLMMessages(displayMessages) };
|
|
4401
|
+
}
|
|
4402
|
+
const llmToSummarize = toLLMMessages(toSummarize);
|
|
4403
|
+
const originalTokens = estimateMessagesTokens(llmToSummarize);
|
|
4404
|
+
let newSummary;
|
|
4405
|
+
if (summarizer) {
|
|
4406
|
+
newSummary = await summarizer(llmToSummarize);
|
|
4407
|
+
} else if (compactionUrl) {
|
|
4408
|
+
newSummary = await fetchSummary(compactionUrl, {
|
|
4409
|
+
messages: llmToSummarize,
|
|
4410
|
+
existingSummary: compactionState.rollingSummary,
|
|
4411
|
+
workingMemory: compactionState.workingMemory
|
|
4412
|
+
});
|
|
4413
|
+
} else {
|
|
4414
|
+
newSummary = buildFallbackSummary(
|
|
4415
|
+
llmToSummarize,
|
|
4416
|
+
compactionState.rollingSummary
|
|
4417
|
+
);
|
|
4418
|
+
}
|
|
4419
|
+
const summaryTokens = Math.ceil(newSummary.length / 3.5);
|
|
4420
|
+
const tokensSaved = Math.max(0, originalTokens - summaryTokens);
|
|
4421
|
+
return {
|
|
4422
|
+
llmMessages: buildSummaryBufferContext(
|
|
4423
|
+
displayMessages,
|
|
4424
|
+
{ ...compactionState, rollingSummary: newSummary },
|
|
4425
|
+
options
|
|
4426
|
+
),
|
|
4427
|
+
newSummary,
|
|
4428
|
+
tokensSaved,
|
|
4429
|
+
messagesSummarized: toSummarize.length
|
|
4430
|
+
};
|
|
4431
|
+
}
|
|
4432
|
+
async function fetchSummary(url, body) {
|
|
4433
|
+
const res = await fetch(url, {
|
|
4434
|
+
method: "POST",
|
|
4435
|
+
headers: { "Content-Type": "application/json" },
|
|
4436
|
+
body: JSON.stringify(body)
|
|
4437
|
+
});
|
|
4438
|
+
if (!res.ok) {
|
|
4439
|
+
throw new Error(`Compaction endpoint returned ${res.status}`);
|
|
4440
|
+
}
|
|
4441
|
+
const data = await res.json();
|
|
4442
|
+
if (!data?.summary) {
|
|
4443
|
+
throw new Error("Compaction endpoint did not return { summary: string }");
|
|
4444
|
+
}
|
|
4445
|
+
return data.summary;
|
|
4446
|
+
}
|
|
4447
|
+
function buildFallbackSummary(messages, existingSummary) {
|
|
4448
|
+
const lines = messages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => `${m.role}: ${(m.content ?? "").slice(0, 200)}`).join("\n");
|
|
4449
|
+
return existingSummary ? `${existingSummary}
|
|
4450
|
+
|
|
4451
|
+
[Additional context]
|
|
4452
|
+
${lines}` : lines;
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
// src/react/message-history/session-persistence.ts
|
|
4456
|
+
var IDB_DB_NAME = "copilot-sdk";
|
|
4457
|
+
var IDB_STORE = "sessions";
|
|
4458
|
+
var IDB_VERSION = 1;
|
|
4459
|
+
function saveCompactionState(storageKey, state) {
|
|
4460
|
+
try {
|
|
4461
|
+
localStorage.setItem(
|
|
4462
|
+
`${storageKey}-state`,
|
|
4463
|
+
JSON.stringify({ ...state, _savedAt: Date.now() })
|
|
4464
|
+
);
|
|
4465
|
+
} catch {
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
function loadCompactionState(storageKey) {
|
|
4469
|
+
try {
|
|
4470
|
+
const raw = localStorage.getItem(`${storageKey}-state`);
|
|
4471
|
+
if (!raw) return null;
|
|
4472
|
+
const parsed = JSON.parse(raw);
|
|
4473
|
+
delete parsed._savedAt;
|
|
4474
|
+
return parsed;
|
|
4475
|
+
} catch {
|
|
4476
|
+
return null;
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
function clearCompactionState(storageKey) {
|
|
4480
|
+
try {
|
|
4481
|
+
localStorage.removeItem(`${storageKey}-state`);
|
|
4482
|
+
} catch {
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4485
|
+
function openDB() {
|
|
4486
|
+
return new Promise((resolve, reject) => {
|
|
4487
|
+
const req = indexedDB.open(IDB_DB_NAME, IDB_VERSION);
|
|
4488
|
+
req.onupgradeneeded = () => {
|
|
4489
|
+
req.result.createObjectStore(IDB_STORE, { keyPath: "sessionId" });
|
|
4490
|
+
};
|
|
4491
|
+
req.onsuccess = () => resolve(req.result);
|
|
4492
|
+
req.onerror = () => reject(req.error);
|
|
4493
|
+
});
|
|
4494
|
+
}
|
|
4495
|
+
async function saveDisplayMessages(storageKey, messages) {
|
|
4496
|
+
try {
|
|
4497
|
+
const db = await openDB();
|
|
4498
|
+
const tx = db.transaction(IDB_STORE, "readwrite");
|
|
4499
|
+
tx.objectStore(IDB_STORE).put({
|
|
4500
|
+
sessionId: storageKey,
|
|
4501
|
+
messages,
|
|
4502
|
+
savedAt: Date.now()
|
|
4503
|
+
});
|
|
4504
|
+
await new Promise((res, rej) => {
|
|
4505
|
+
tx.oncomplete = () => res();
|
|
4506
|
+
tx.onerror = () => rej(tx.error);
|
|
4507
|
+
});
|
|
4508
|
+
db.close();
|
|
4509
|
+
} catch {
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
async function loadDisplayMessages(storageKey) {
|
|
4513
|
+
try {
|
|
4514
|
+
const db = await openDB();
|
|
4515
|
+
const tx = db.transaction(IDB_STORE, "readonly");
|
|
4516
|
+
const req = tx.objectStore(IDB_STORE).get(storageKey);
|
|
4517
|
+
const result = await new Promise((res, rej) => {
|
|
4518
|
+
req.onsuccess = () => res(req.result);
|
|
4519
|
+
req.onerror = () => rej(req.error);
|
|
4520
|
+
});
|
|
4521
|
+
db.close();
|
|
4522
|
+
return result?.messages ?? null;
|
|
4523
|
+
} catch {
|
|
4524
|
+
return null;
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4527
|
+
async function clearDisplayMessages(storageKey) {
|
|
4528
|
+
try {
|
|
4529
|
+
const db = await openDB();
|
|
4530
|
+
const tx = db.transaction(IDB_STORE, "readwrite");
|
|
4531
|
+
tx.objectStore(IDB_STORE).delete(storageKey);
|
|
4532
|
+
await new Promise((res, rej) => {
|
|
4533
|
+
tx.oncomplete = () => res();
|
|
4534
|
+
tx.onerror = () => rej(tx.error);
|
|
4535
|
+
});
|
|
4536
|
+
db.close();
|
|
4537
|
+
} catch {
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
async function clearSession(storageKey) {
|
|
4541
|
+
clearCompactionState(storageKey);
|
|
4542
|
+
await clearDisplayMessages(storageKey);
|
|
4543
|
+
}
|
|
4544
|
+
|
|
4545
|
+
// src/react/message-history/useMessageHistory.ts
|
|
4546
|
+
var DEFAULT_COMPACTION_STATE = {
|
|
4547
|
+
rollingSummary: null,
|
|
4548
|
+
lastCompactionAt: null,
|
|
4549
|
+
compactionCount: 0,
|
|
4550
|
+
totalTokensSaved: 0,
|
|
4551
|
+
workingMemory: [],
|
|
4552
|
+
displayMessageCount: 0,
|
|
4553
|
+
llmMessageCount: 0
|
|
4554
|
+
};
|
|
4555
|
+
function useMessageHistory(options = {}) {
|
|
4556
|
+
const { messages } = useCopilot();
|
|
4557
|
+
const ctx = useMessageHistoryContext();
|
|
4558
|
+
const config = React2.useMemo(
|
|
4559
|
+
() => ({ ...defaultMessageHistoryConfig, ...ctx.config, ...options }),
|
|
4560
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4561
|
+
[
|
|
4562
|
+
ctx.config,
|
|
4563
|
+
options.strategy,
|
|
4564
|
+
options.maxContextTokens,
|
|
4565
|
+
options.recentBuffer,
|
|
4566
|
+
options.compactionThreshold
|
|
4567
|
+
]
|
|
4568
|
+
);
|
|
4569
|
+
const storageKey = config.storageKey ?? "copilot-session";
|
|
4570
|
+
const strategy = options.skipCompaction ? "none" : config.strategy ?? "none";
|
|
4571
|
+
const [compactionState, setCompactionState] = React2.useState(() => {
|
|
4572
|
+
if (config.persistSession) {
|
|
4573
|
+
return loadCompactionState(storageKey) ?? DEFAULT_COMPACTION_STATE;
|
|
4574
|
+
}
|
|
4575
|
+
return DEFAULT_COMPACTION_STATE;
|
|
4576
|
+
});
|
|
4577
|
+
const displayMessages = React2.useMemo(
|
|
4578
|
+
() => messages.map(toDisplayMessage),
|
|
4579
|
+
[messages]
|
|
4580
|
+
);
|
|
4581
|
+
const restoredRef = React2.useRef(false);
|
|
4582
|
+
React2.useEffect(() => {
|
|
4583
|
+
if (!config.persistSession || restoredRef.current) return;
|
|
4584
|
+
restoredRef.current = true;
|
|
4585
|
+
loadDisplayMessages(storageKey).then((saved) => {
|
|
4586
|
+
if (saved?.length && messages.length === 0) ;
|
|
4587
|
+
});
|
|
4588
|
+
}, [config.persistSession, storageKey, messages.length]);
|
|
4589
|
+
React2.useEffect(() => {
|
|
4590
|
+
if (!config.persistSession || displayMessages.length === 0) return;
|
|
4591
|
+
saveDisplayMessages(storageKey, displayMessages);
|
|
4592
|
+
}, [config.persistSession, storageKey, displayMessages]);
|
|
4593
|
+
const llmMessages = React2.useMemo(() => {
|
|
4594
|
+
const maxTokens = config.maxContextTokens ?? 128e3;
|
|
4595
|
+
const reserve = config.reserveForResponse ?? 4096;
|
|
4596
|
+
const tokenBudget = maxTokens - reserve;
|
|
4597
|
+
const recentBuffer = config.recentBuffer ?? 10;
|
|
4598
|
+
const maxChars = config.toolResultMaxChars ?? 1e4;
|
|
4599
|
+
let result;
|
|
4600
|
+
switch (strategy) {
|
|
4601
|
+
case "sliding-window": {
|
|
4602
|
+
const windowed = applySlidingWindow(displayMessages, {
|
|
4603
|
+
tokenBudget,
|
|
4604
|
+
recentBuffer
|
|
4605
|
+
});
|
|
4606
|
+
result = truncateToolResults(toLLMMessages(windowed), maxChars);
|
|
4607
|
+
break;
|
|
4608
|
+
}
|
|
4609
|
+
case "selective-prune": {
|
|
4610
|
+
result = truncateToolResults(
|
|
4611
|
+
applySelectivePrune(displayMessages, recentBuffer),
|
|
4612
|
+
maxChars
|
|
4613
|
+
);
|
|
4614
|
+
break;
|
|
4615
|
+
}
|
|
4616
|
+
case "summary-buffer": {
|
|
4617
|
+
result = truncateToolResults(
|
|
4618
|
+
buildSummaryBufferContext(displayMessages, compactionState, {
|
|
4619
|
+
recentBuffer,
|
|
4620
|
+
compactionThreshold: config.compactionThreshold ?? 0.75,
|
|
4621
|
+
compactionUrl: config.compactionUrl,
|
|
4622
|
+
summarizer: options.summarizer
|
|
4623
|
+
}),
|
|
4624
|
+
maxChars
|
|
4625
|
+
);
|
|
4626
|
+
break;
|
|
4627
|
+
}
|
|
4628
|
+
default:
|
|
4629
|
+
result = truncateToolResults(toLLMMessages(displayMessages), maxChars);
|
|
4630
|
+
}
|
|
4631
|
+
return result;
|
|
4632
|
+
}, [displayMessages, compactionState, strategy, config, options.summarizer]);
|
|
4633
|
+
const tokenUsage = React2.useMemo(() => {
|
|
4634
|
+
const mode = options.tokenEstimation ?? "fast";
|
|
4635
|
+
const current = estimateTokens2(toLLMMessages(displayMessages), mode);
|
|
4636
|
+
const max = config.maxContextTokens ?? 128e3;
|
|
4637
|
+
const threshold = config.compactionThreshold ?? 0.75;
|
|
4638
|
+
const percentage = current / max;
|
|
4639
|
+
return { current, max, percentage, isApproaching: percentage >= threshold };
|
|
4640
|
+
}, [
|
|
4641
|
+
displayMessages,
|
|
4642
|
+
config.maxContextTokens,
|
|
4643
|
+
config.compactionThreshold,
|
|
4644
|
+
options.tokenEstimation
|
|
4645
|
+
]);
|
|
4646
|
+
React2.useEffect(() => {
|
|
4647
|
+
if (config.onTokenUsage && tokenUsage.current > 0) {
|
|
4648
|
+
config.onTokenUsage(tokenUsage);
|
|
4649
|
+
}
|
|
4650
|
+
}, [tokenUsage, config.onTokenUsage]);
|
|
4651
|
+
React2.useEffect(() => {
|
|
4652
|
+
if (config.persistSession) {
|
|
4653
|
+
saveCompactionState(storageKey, {
|
|
4654
|
+
...compactionState,
|
|
4655
|
+
displayMessageCount: displayMessages.length,
|
|
4656
|
+
llmMessageCount: llmMessages.length
|
|
4657
|
+
});
|
|
4658
|
+
}
|
|
4659
|
+
}, [
|
|
4660
|
+
config.persistSession,
|
|
4661
|
+
storageKey,
|
|
4662
|
+
compactionState,
|
|
4663
|
+
displayMessages.length,
|
|
4664
|
+
llmMessages.length
|
|
4665
|
+
]);
|
|
4666
|
+
const isCompactingRef = React2.useRef(false);
|
|
4667
|
+
const [isCompacting, setIsCompacting] = React2.useState(false);
|
|
4668
|
+
React2.useEffect(() => {
|
|
4669
|
+
if (strategy !== "summary-buffer" || options.skipCompaction || isCompactingRef.current || !tokenUsage.isApproaching)
|
|
4670
|
+
return;
|
|
4671
|
+
isCompactingRef.current = true;
|
|
4672
|
+
setIsCompacting(true);
|
|
4673
|
+
runCompaction(displayMessages, compactionState, {
|
|
4674
|
+
recentBuffer: config.recentBuffer ?? 10,
|
|
4675
|
+
tokenBudget: (config.maxContextTokens ?? 128e3) - (config.reserveForResponse ?? 4096),
|
|
4676
|
+
compactionThreshold: config.compactionThreshold ?? 0.75,
|
|
4677
|
+
compactionUrl: config.compactionUrl,
|
|
4678
|
+
summarizer: options.summarizer
|
|
4679
|
+
}).then((result) => {
|
|
4680
|
+
if (result.newSummary) {
|
|
4681
|
+
const event = {
|
|
4682
|
+
type: "auto",
|
|
4683
|
+
compactionCount: compactionState.compactionCount + 1,
|
|
4684
|
+
messagesSummarized: result.messagesSummarized ?? 0,
|
|
4685
|
+
tokensSaved: result.tokensSaved ?? 0,
|
|
4686
|
+
timestamp: Date.now()
|
|
4687
|
+
};
|
|
4688
|
+
setCompactionState((prev) => ({
|
|
4689
|
+
...prev,
|
|
4690
|
+
rollingSummary: result.newSummary,
|
|
4691
|
+
lastCompactionAt: Date.now(),
|
|
4692
|
+
compactionCount: prev.compactionCount + 1,
|
|
4693
|
+
totalTokensSaved: prev.totalTokensSaved + (result.tokensSaved ?? 0)
|
|
4694
|
+
}));
|
|
4695
|
+
config.onCompaction?.(event);
|
|
4696
|
+
}
|
|
4697
|
+
}).finally(() => {
|
|
4698
|
+
isCompactingRef.current = false;
|
|
4699
|
+
setIsCompacting(false);
|
|
4700
|
+
});
|
|
4701
|
+
}, [tokenUsage.isApproaching, strategy]);
|
|
4702
|
+
const compactSession = React2.useCallback(
|
|
4703
|
+
async (instructions) => {
|
|
4704
|
+
if (strategy !== "summary-buffer") return;
|
|
4705
|
+
const result = await runCompaction(displayMessages, compactionState, {
|
|
4706
|
+
recentBuffer: config.recentBuffer ?? 10,
|
|
4707
|
+
tokenBudget: (config.maxContextTokens ?? 128e3) - (config.reserveForResponse ?? 4096),
|
|
4708
|
+
compactionThreshold: config.compactionThreshold ?? 0.75,
|
|
4709
|
+
compactionUrl: config.compactionUrl,
|
|
4710
|
+
summarizer: options.summarizer ? (msgs) => options.summarizer(msgs) : instructions ? (msgs) => fetchWithInstructions(config.compactionUrl, msgs, instructions) : void 0
|
|
4711
|
+
});
|
|
4712
|
+
if (result.newSummary) {
|
|
4713
|
+
const event = {
|
|
4714
|
+
type: "manual",
|
|
4715
|
+
compactionCount: compactionState.compactionCount + 1,
|
|
4716
|
+
messagesSummarized: result.messagesSummarized ?? 0,
|
|
4717
|
+
tokensSaved: result.tokensSaved ?? 0,
|
|
4718
|
+
timestamp: Date.now()
|
|
4719
|
+
};
|
|
4720
|
+
setCompactionState((prev) => ({
|
|
4721
|
+
...prev,
|
|
4722
|
+
rollingSummary: result.newSummary,
|
|
4723
|
+
lastCompactionAt: Date.now(),
|
|
4724
|
+
compactionCount: prev.compactionCount + 1,
|
|
4725
|
+
totalTokensSaved: prev.totalTokensSaved + (result.tokensSaved ?? 0)
|
|
4726
|
+
}));
|
|
4727
|
+
config.onCompaction?.(event);
|
|
4728
|
+
}
|
|
4729
|
+
},
|
|
4730
|
+
[displayMessages, compactionState, config, strategy, options.summarizer]
|
|
4731
|
+
);
|
|
4732
|
+
const addToWorkingMemory = React2.useCallback((fact) => {
|
|
4733
|
+
setCompactionState((prev) => ({
|
|
4734
|
+
...prev,
|
|
4735
|
+
workingMemory: [...prev.workingMemory, fact]
|
|
4736
|
+
}));
|
|
4737
|
+
}, []);
|
|
4738
|
+
const clearWorkingMemory = React2.useCallback(() => {
|
|
4739
|
+
setCompactionState((prev) => ({ ...prev, workingMemory: [] }));
|
|
4740
|
+
}, []);
|
|
4741
|
+
const resetSession = React2.useCallback(async () => {
|
|
4742
|
+
setCompactionState(DEFAULT_COMPACTION_STATE);
|
|
4743
|
+
if (config.persistSession) {
|
|
4744
|
+
await clearSession(storageKey);
|
|
4745
|
+
}
|
|
4746
|
+
}, [config.persistSession, storageKey]);
|
|
4747
|
+
return {
|
|
4748
|
+
displayMessages,
|
|
4749
|
+
llmMessages,
|
|
4750
|
+
tokenUsage,
|
|
4751
|
+
isCompacting,
|
|
4752
|
+
compactionState: {
|
|
4753
|
+
...compactionState,
|
|
4754
|
+
displayMessageCount: displayMessages.length,
|
|
4755
|
+
llmMessageCount: llmMessages.length
|
|
4756
|
+
},
|
|
4757
|
+
compactSession,
|
|
4758
|
+
addToWorkingMemory,
|
|
4759
|
+
clearWorkingMemory,
|
|
4760
|
+
resetSession
|
|
4761
|
+
};
|
|
4762
|
+
}
|
|
4763
|
+
async function fetchWithInstructions(url, messages, instructions) {
|
|
4764
|
+
const res = await fetch(url, {
|
|
4765
|
+
method: "POST",
|
|
4766
|
+
headers: { "Content-Type": "application/json" },
|
|
4767
|
+
body: JSON.stringify({ messages, instructions })
|
|
4768
|
+
});
|
|
4769
|
+
const data = await res.json();
|
|
4770
|
+
return data.summary;
|
|
4771
|
+
}
|
|
4772
|
+
var SkillContext = React2.createContext(null);
|
|
4773
|
+
function useSkillContext() {
|
|
4774
|
+
const ctx = React2.useContext(SkillContext);
|
|
4775
|
+
if (!ctx) {
|
|
4776
|
+
throw new Error("useSkillContext must be used within <SkillProvider>");
|
|
4777
|
+
}
|
|
4778
|
+
return ctx;
|
|
4779
|
+
}
|
|
4780
|
+
function useAIContext(item) {
|
|
4781
|
+
const { addContext, removeContext } = useCopilot();
|
|
4782
|
+
const contextIdRef = React2.useRef(null);
|
|
4783
|
+
const serializedData = typeof item.data === "string" ? item.data : JSON.stringify(item.data);
|
|
4784
|
+
React2.useEffect(() => {
|
|
4785
|
+
const formattedValue = typeof item.data === "string" ? item.data : JSON.stringify(item.data, null, 2);
|
|
4786
|
+
const contextString = item.description ? `${item.description}:
|
|
4787
|
+
${formattedValue}` : `${item.key}:
|
|
4788
|
+
${formattedValue}`;
|
|
4789
|
+
contextIdRef.current = addContext(contextString, item.parentId);
|
|
4790
|
+
return () => {
|
|
4791
|
+
if (contextIdRef.current) {
|
|
4792
|
+
removeContext(contextIdRef.current);
|
|
4793
|
+
contextIdRef.current = null;
|
|
4794
|
+
}
|
|
4795
|
+
};
|
|
4796
|
+
}, [
|
|
4797
|
+
item.key,
|
|
4798
|
+
serializedData,
|
|
4799
|
+
item.description,
|
|
4800
|
+
item.parentId,
|
|
4801
|
+
addContext,
|
|
4802
|
+
removeContext
|
|
4803
|
+
]);
|
|
4804
|
+
return contextIdRef.current ?? void 0;
|
|
4805
|
+
}
|
|
4806
|
+
function useAIContexts(items) {
|
|
4807
|
+
const { addContext, removeContext } = useCopilot();
|
|
4808
|
+
const contextIdsRef = React2.useRef([]);
|
|
4809
|
+
const serializedItems = JSON.stringify(
|
|
4810
|
+
items.map((item) => ({
|
|
4811
|
+
key: item.key,
|
|
4812
|
+
data: item.data,
|
|
4813
|
+
description: item.description,
|
|
4814
|
+
parentId: item.parentId
|
|
4815
|
+
}))
|
|
4816
|
+
);
|
|
4817
|
+
React2.useEffect(() => {
|
|
4818
|
+
contextIdsRef.current.forEach((id) => removeContext(id));
|
|
4819
|
+
contextIdsRef.current = [];
|
|
4820
|
+
const parsedItems = JSON.parse(serializedItems);
|
|
4821
|
+
for (const item of parsedItems) {
|
|
4822
|
+
const formattedValue = typeof item.data === "string" ? item.data : JSON.stringify(item.data, null, 2);
|
|
4823
|
+
const contextString = item.description ? `${item.description}:
|
|
4824
|
+
${formattedValue}` : `${item.key}:
|
|
4825
|
+
${formattedValue}`;
|
|
4826
|
+
const id = addContext(contextString, item.parentId);
|
|
4827
|
+
contextIdsRef.current.push(id);
|
|
4828
|
+
}
|
|
4829
|
+
return () => {
|
|
4830
|
+
contextIdsRef.current.forEach((id) => removeContext(id));
|
|
4831
|
+
contextIdsRef.current = [];
|
|
4832
|
+
};
|
|
4833
|
+
}, [serializedItems, addContext, removeContext]);
|
|
4834
|
+
}
|
|
4835
|
+
function isZodSchema(value) {
|
|
4836
|
+
if (value === null || typeof value !== "object") return false;
|
|
4837
|
+
const obj = value;
|
|
4838
|
+
return "_def" in obj && typeof obj._def === "object" || "_zod" in obj && typeof obj._zod === "object" || "~standard" in obj;
|
|
4839
|
+
}
|
|
4840
|
+
function useTool(config, dependencies = []) {
|
|
4841
|
+
const { registerTool, unregisterTool } = useCopilot();
|
|
4842
|
+
const configRef = React2.useRef(config);
|
|
4843
|
+
configRef.current = config;
|
|
4844
|
+
const inputSchema = React2.useMemo(() => {
|
|
4845
|
+
if (isZodSchema(config.inputSchema)) {
|
|
4846
|
+
return chunkI3SQUNTT_cjs.zodToJsonSchema(config.inputSchema);
|
|
4847
|
+
}
|
|
4848
|
+
return config.inputSchema;
|
|
4849
|
+
}, [config.inputSchema]);
|
|
4850
|
+
React2.useEffect(() => {
|
|
4851
|
+
const tool2 = {
|
|
4852
|
+
name: config.name,
|
|
4853
|
+
description: config.description,
|
|
4854
|
+
location: "client",
|
|
4855
|
+
inputSchema,
|
|
4856
|
+
handler: async (params, context) => {
|
|
4857
|
+
return configRef.current.handler(params, context);
|
|
4858
|
+
},
|
|
4859
|
+
render: config.render,
|
|
4860
|
+
available: config.available ?? true,
|
|
4861
|
+
needsApproval: config.needsApproval,
|
|
4862
|
+
approvalMessage: config.approvalMessage,
|
|
4863
|
+
hidden: config.hidden,
|
|
4864
|
+
deferLoading: config.deferLoading,
|
|
4865
|
+
profiles: config.profiles,
|
|
4866
|
+
searchKeywords: config.searchKeywords,
|
|
4867
|
+
group: config.group,
|
|
4868
|
+
category: config.category,
|
|
4869
|
+
resultConfig: config.resultConfig,
|
|
4870
|
+
title: config.title,
|
|
4871
|
+
executingTitle: config.executingTitle,
|
|
4872
|
+
completedTitle: config.completedTitle,
|
|
4873
|
+
aiResponseMode: config.aiResponseMode,
|
|
4874
|
+
aiContext: config.aiContext
|
|
4875
|
+
};
|
|
4876
|
+
registerTool(tool2);
|
|
4877
|
+
return () => {
|
|
4878
|
+
unregisterTool(config.name);
|
|
4879
|
+
};
|
|
4880
|
+
}, [config.name, inputSchema, ...dependencies]);
|
|
4881
|
+
}
|
|
4882
|
+
function useTools(tools) {
|
|
4883
|
+
const { registerTool, unregisterTool } = useCopilot();
|
|
4884
|
+
const registeredToolsRef = React2.useRef([]);
|
|
4885
|
+
const toolsRef = React2.useRef(tools);
|
|
4886
|
+
toolsRef.current = tools;
|
|
4887
|
+
const toolsKey = Object.keys(tools).sort().join(",");
|
|
4888
|
+
React2.useEffect(() => {
|
|
4889
|
+
const currentTools = toolsRef.current;
|
|
4890
|
+
const toolNames = [];
|
|
4891
|
+
for (const [name, toolDef] of Object.entries(currentTools)) {
|
|
4892
|
+
const fullTool = {
|
|
4893
|
+
...toolDef,
|
|
4894
|
+
name
|
|
4895
|
+
// Use the key as the name
|
|
4896
|
+
};
|
|
4897
|
+
registerTool(fullTool);
|
|
4898
|
+
toolNames.push(name);
|
|
4899
|
+
}
|
|
4900
|
+
registeredToolsRef.current = toolNames;
|
|
4901
|
+
return () => {
|
|
4902
|
+
for (const name of registeredToolsRef.current) {
|
|
4903
|
+
unregisterTool(name);
|
|
4904
|
+
}
|
|
4905
|
+
registeredToolsRef.current = [];
|
|
4906
|
+
};
|
|
4907
|
+
}, [toolsKey]);
|
|
4908
|
+
}
|
|
4909
|
+
function SkillContextInjector({
|
|
4910
|
+
registry,
|
|
4911
|
+
skills
|
|
4912
|
+
}) {
|
|
4913
|
+
const catalog = React2.useMemo(() => registry.buildCatalog(), [skills]);
|
|
4914
|
+
const eagerContent = React2.useMemo(() => registry.buildEagerContent(), [skills]);
|
|
4915
|
+
useAIContext({
|
|
4916
|
+
key: "__skill_catalog__",
|
|
4917
|
+
description: "Skills the AI can load on demand",
|
|
4918
|
+
data: catalog ? `You have access to specialized skills. Call load_skill({ name }) when relevant.
|
|
4919
|
+
|
|
4920
|
+
${catalog}` : ""
|
|
4921
|
+
});
|
|
4922
|
+
useAIContext({
|
|
4923
|
+
key: "__skill_eager__",
|
|
4924
|
+
description: "Always-active skill instructions",
|
|
4925
|
+
data: eagerContent
|
|
4926
|
+
});
|
|
4927
|
+
return null;
|
|
4928
|
+
}
|
|
4929
|
+
function SkillRequestSync({ skills }) {
|
|
4930
|
+
const { setInlineSkills } = useCopilot();
|
|
4931
|
+
React2.useEffect(() => {
|
|
4932
|
+
const inlineSkills = skills.filter((s) => s.source.type === "inline").map((s) => ({
|
|
4933
|
+
name: s.name,
|
|
4934
|
+
description: s.description,
|
|
4935
|
+
content: s.content,
|
|
4936
|
+
strategy: s.strategy
|
|
4937
|
+
}));
|
|
4938
|
+
setInlineSkills(inlineSkills);
|
|
4939
|
+
}, [skills, setInlineSkills]);
|
|
4940
|
+
return null;
|
|
4941
|
+
}
|
|
4942
|
+
function SkillToolRegistrar({
|
|
4943
|
+
registry,
|
|
4944
|
+
skills
|
|
4945
|
+
}) {
|
|
4946
|
+
useTool(
|
|
4947
|
+
{
|
|
4948
|
+
name: "load_skill",
|
|
4949
|
+
description: "Load a skill by name to get full instructions for a specialized task.",
|
|
4950
|
+
inputSchema: {
|
|
4951
|
+
type: "object",
|
|
4952
|
+
properties: {
|
|
4953
|
+
name: {
|
|
4954
|
+
type: "string",
|
|
4955
|
+
description: "The name of the skill to load."
|
|
4956
|
+
}
|
|
4957
|
+
},
|
|
4958
|
+
required: ["name"]
|
|
4959
|
+
},
|
|
4960
|
+
handler: async ({ name }) => {
|
|
4961
|
+
const skill = registry.get(name);
|
|
4962
|
+
if (!skill) {
|
|
4963
|
+
const available = registry.getAuto().map((s) => s.name).join(", ") || "none";
|
|
4964
|
+
return {
|
|
4965
|
+
success: false,
|
|
4966
|
+
error: `Skill "${name}" not found. Available skills: ${available}`
|
|
4967
|
+
};
|
|
4968
|
+
}
|
|
4969
|
+
const sourceTypeMap = {
|
|
4970
|
+
inline: "client-inline",
|
|
4971
|
+
url: "remote-url",
|
|
4972
|
+
file: "server-dir"
|
|
4973
|
+
};
|
|
4974
|
+
return {
|
|
4975
|
+
success: true,
|
|
4976
|
+
name: skill.name,
|
|
4977
|
+
description: skill.description,
|
|
4978
|
+
strategy: skill.strategy ?? "auto",
|
|
4979
|
+
content: skill.content,
|
|
4980
|
+
source: sourceTypeMap[skill.source.type]
|
|
4981
|
+
};
|
|
4982
|
+
}
|
|
4983
|
+
},
|
|
4984
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4985
|
+
[skills]
|
|
4986
|
+
);
|
|
4987
|
+
return null;
|
|
4988
|
+
}
|
|
4989
|
+
function SkillProvider({
|
|
4990
|
+
children,
|
|
4991
|
+
skills: skillsProp
|
|
4992
|
+
}) {
|
|
4993
|
+
const registryRef = React2.useRef(null);
|
|
4994
|
+
if (registryRef.current === null) {
|
|
4995
|
+
registryRef.current = new chunkBJYA5NDL_cjs.SkillRegistry();
|
|
4996
|
+
}
|
|
4997
|
+
const registry = registryRef.current;
|
|
4998
|
+
const [skills, setSkills] = React2.useState([]);
|
|
4999
|
+
React2.useEffect(() => {
|
|
5000
|
+
if (!skillsProp?.length) return;
|
|
5001
|
+
for (const def of skillsProp) {
|
|
5002
|
+
if (def.source.type !== "inline") {
|
|
5003
|
+
console.warn(
|
|
5004
|
+
`[copilot-sdk/skills] Client-side SkillProvider only supports inline skills. Skill "${def.name}" has source type "${def.source.type}" and will be skipped. Use loadSkills() on the server for file/url skills.`
|
|
5005
|
+
);
|
|
5006
|
+
continue;
|
|
5007
|
+
}
|
|
5008
|
+
const resolved = {
|
|
5009
|
+
...def,
|
|
5010
|
+
content: def.source.content
|
|
5011
|
+
};
|
|
5012
|
+
registry.register(resolved);
|
|
5013
|
+
}
|
|
5014
|
+
setSkills(registry.getAll());
|
|
5015
|
+
return () => {
|
|
5016
|
+
for (const def of skillsProp ?? []) {
|
|
5017
|
+
registry.unregister(def.name);
|
|
2535
5018
|
}
|
|
2536
|
-
|
|
5019
|
+
setSkills(registry.getAll());
|
|
2537
5020
|
};
|
|
2538
|
-
}, [
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
5021
|
+
}, [skillsProp]);
|
|
5022
|
+
const register = React2.useCallback((skill) => {
|
|
5023
|
+
registry.register(skill);
|
|
5024
|
+
setSkills(registry.getAll());
|
|
5025
|
+
}, []);
|
|
5026
|
+
const unregister = React2.useCallback((name) => {
|
|
5027
|
+
registry.unregister(name);
|
|
5028
|
+
setSkills(registry.getAll());
|
|
5029
|
+
}, []);
|
|
5030
|
+
const contextValue = React2.useMemo(
|
|
5031
|
+
() => ({ registry, register, unregister, skills }),
|
|
5032
|
+
[register, unregister, skills]
|
|
5033
|
+
);
|
|
5034
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(SkillContext.Provider, { value: contextValue, children: [
|
|
5035
|
+
/* @__PURE__ */ jsxRuntime.jsx(SkillContextInjector, { registry, skills }),
|
|
5036
|
+
/* @__PURE__ */ jsxRuntime.jsx(SkillToolRegistrar, { registry, skills }),
|
|
5037
|
+
/* @__PURE__ */ jsxRuntime.jsx(SkillRequestSync, { skills }),
|
|
5038
|
+
children
|
|
5039
|
+
] });
|
|
2549
5040
|
}
|
|
2550
5041
|
function MCPConnection({ config }) {
|
|
2551
5042
|
useMCPTools({
|
|
@@ -2559,9 +5050,126 @@ function MCPConnection({ config }) {
|
|
|
2559
5050
|
});
|
|
2560
5051
|
return null;
|
|
2561
5052
|
}
|
|
2562
|
-
var
|
|
5053
|
+
var COMPACTING_MARKER_ID = "__compacting-in-progress__";
|
|
5054
|
+
function MessageHistoryBridge({
|
|
5055
|
+
chatRef
|
|
5056
|
+
}) {
|
|
5057
|
+
const { compactionState, tokenUsage } = useMessageHistory();
|
|
5058
|
+
const ctx = useMessageHistoryContext();
|
|
5059
|
+
const loaderAddedRef = React2.useRef(false);
|
|
5060
|
+
const prevCompactionCountRef = React2.useRef(compactionState.compactionCount);
|
|
5061
|
+
React2.useEffect(() => {
|
|
5062
|
+
if (!tokenUsage.isApproaching) {
|
|
5063
|
+
loaderAddedRef.current = false;
|
|
5064
|
+
return;
|
|
5065
|
+
}
|
|
5066
|
+
if (loaderAddedRef.current) return;
|
|
5067
|
+
const chat = chatRef.current;
|
|
5068
|
+
if (!chat) return;
|
|
5069
|
+
const alreadyAdded = chat.messages.some(
|
|
5070
|
+
(m) => m.id === COMPACTING_MARKER_ID
|
|
5071
|
+
);
|
|
5072
|
+
if (alreadyAdded) return;
|
|
5073
|
+
loaderAddedRef.current = true;
|
|
5074
|
+
const loading = {
|
|
5075
|
+
id: COMPACTING_MARKER_ID,
|
|
5076
|
+
role: "system",
|
|
5077
|
+
content: "Compacting conversation\u2026",
|
|
5078
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
5079
|
+
metadata: { type: "compaction-marker", compacting: true }
|
|
5080
|
+
};
|
|
5081
|
+
chat.setMessages([...chat.messages, loading]);
|
|
5082
|
+
}, [tokenUsage.isApproaching]);
|
|
5083
|
+
React2.useEffect(() => {
|
|
5084
|
+
if (compactionState.compactionCount <= prevCompactionCountRef.current)
|
|
5085
|
+
return;
|
|
5086
|
+
prevCompactionCountRef.current = compactionState.compactionCount;
|
|
5087
|
+
loaderAddedRef.current = false;
|
|
5088
|
+
const chat = chatRef.current;
|
|
5089
|
+
if (!chat) return;
|
|
5090
|
+
const hasLoader = chat.messages.some((m) => m.id === COMPACTING_MARKER_ID);
|
|
5091
|
+
const base = hasLoader ? chat.messages.map(
|
|
5092
|
+
(m) => m.id === COMPACTING_MARKER_ID ? {
|
|
5093
|
+
...m,
|
|
5094
|
+
id: `compaction-marker-${compactionState.compactionCount}`,
|
|
5095
|
+
content: `Conversation compacted \u2014 context window refreshed`,
|
|
5096
|
+
metadata: { type: "compaction-marker", compacting: false }
|
|
5097
|
+
} : m
|
|
5098
|
+
) : [
|
|
5099
|
+
...chat.messages,
|
|
5100
|
+
{
|
|
5101
|
+
id: `compaction-marker-${compactionState.compactionCount}`,
|
|
5102
|
+
role: "system",
|
|
5103
|
+
content: `Conversation compacted \u2014 context window refreshed`,
|
|
5104
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
5105
|
+
metadata: { type: "compaction-marker", compacting: false }
|
|
5106
|
+
}
|
|
5107
|
+
];
|
|
5108
|
+
chat.setMessages(base);
|
|
5109
|
+
}, [compactionState.compactionCount]);
|
|
5110
|
+
const compactionStateRef = React2.useRef(compactionState);
|
|
5111
|
+
compactionStateRef.current = compactionState;
|
|
5112
|
+
const configRef = React2.useRef(ctx.config);
|
|
5113
|
+
configRef.current = ctx.config;
|
|
5114
|
+
React2.useEffect(() => {
|
|
5115
|
+
const chat = chatRef.current;
|
|
5116
|
+
if (!chat) return;
|
|
5117
|
+
chat.setRequestMessageTransform((allMessages) => {
|
|
5118
|
+
if (allMessages.length === 0) return allMessages;
|
|
5119
|
+
let lastUserIdx = -1;
|
|
5120
|
+
for (let i = allMessages.length - 1; i >= 0; i--) {
|
|
5121
|
+
if (allMessages[i].role === "user") {
|
|
5122
|
+
lastUserIdx = i;
|
|
5123
|
+
break;
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
if (lastUserIdx === -1) return allMessages;
|
|
5127
|
+
const historyMessages = allMessages.slice(0, lastUserIdx);
|
|
5128
|
+
const currentTurn = allMessages.slice(lastUserIdx);
|
|
5129
|
+
if (historyMessages.length === 0) return allMessages;
|
|
5130
|
+
const cfg = configRef.current;
|
|
5131
|
+
const cs = compactionStateRef.current;
|
|
5132
|
+
const recentBuffer = cfg.recentBuffer ?? 10;
|
|
5133
|
+
const isCompactionMsg = (m) => m.metadata?.["type"] === "compaction-marker";
|
|
5134
|
+
const windowedHistory = [];
|
|
5135
|
+
if (cs.workingMemory.length > 0) {
|
|
5136
|
+
windowedHistory.push({
|
|
5137
|
+
id: "working-memory",
|
|
5138
|
+
role: "system",
|
|
5139
|
+
content: `[Working memory \u2014 always active]
|
|
5140
|
+
${cs.workingMemory.join("\n")}`,
|
|
5141
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
5142
|
+
});
|
|
5143
|
+
}
|
|
5144
|
+
if (cs.rollingSummary) {
|
|
5145
|
+
windowedHistory.push({
|
|
5146
|
+
id: "rolling-summary",
|
|
5147
|
+
role: "system",
|
|
5148
|
+
content: `[Previous conversation summary]
|
|
5149
|
+
${cs.rollingSummary}`,
|
|
5150
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
5151
|
+
});
|
|
5152
|
+
}
|
|
5153
|
+
const systemMsgs = historyMessages.filter(
|
|
5154
|
+
(m) => m.role === "system" && !isCompactionMsg(m)
|
|
5155
|
+
);
|
|
5156
|
+
windowedHistory.push(...systemMsgs);
|
|
5157
|
+
const conversationMsgs = historyMessages.filter(
|
|
5158
|
+
(m) => m.role !== "system"
|
|
5159
|
+
);
|
|
5160
|
+
const recentStart = Math.max(0, conversationMsgs.length - recentBuffer);
|
|
5161
|
+
windowedHistory.push(...conversationMsgs.slice(recentStart));
|
|
5162
|
+
return [...windowedHistory, ...currentTurn];
|
|
5163
|
+
});
|
|
5164
|
+
return () => {
|
|
5165
|
+
chatRef.current?.setRequestMessageTransform(null);
|
|
5166
|
+
};
|
|
5167
|
+
}, []);
|
|
5168
|
+
return null;
|
|
5169
|
+
}
|
|
5170
|
+
var CopilotContext = React2.createContext(null);
|
|
2563
5171
|
function useCopilot() {
|
|
2564
|
-
const context =
|
|
5172
|
+
const context = React2.useContext(CopilotContext);
|
|
2565
5173
|
if (!context) {
|
|
2566
5174
|
throw new Error("useCopilot must be used within CopilotProvider");
|
|
2567
5175
|
}
|
|
@@ -2582,23 +5190,26 @@ function CopilotProvider({
|
|
|
2582
5190
|
debug = false,
|
|
2583
5191
|
maxIterations,
|
|
2584
5192
|
maxIterationsMessage,
|
|
2585
|
-
mcpServers
|
|
5193
|
+
mcpServers,
|
|
5194
|
+
optimization,
|
|
5195
|
+
messageHistory,
|
|
5196
|
+
skills
|
|
2586
5197
|
}) {
|
|
2587
|
-
const debugLog =
|
|
2588
|
-
(
|
|
2589
|
-
|
|
5198
|
+
const debugLog = React2.useCallback(
|
|
5199
|
+
(action, data) => {
|
|
5200
|
+
chunkI3SQUNTT_cjs.createLogger("provider", () => debug ?? false)(action, data);
|
|
2590
5201
|
},
|
|
2591
5202
|
[debug]
|
|
2592
5203
|
);
|
|
2593
|
-
|
|
5204
|
+
React2.useEffect(() => {
|
|
2594
5205
|
if (toolsConfig && (toolsConfig.screenshot || toolsConfig.console || toolsConfig.network)) {
|
|
2595
5206
|
console.warn(
|
|
2596
5207
|
"[Copilot SDK] The `tools` prop is deprecated. Use the `useTools` hook instead."
|
|
2597
5208
|
);
|
|
2598
5209
|
}
|
|
2599
5210
|
}, [toolsConfig]);
|
|
2600
|
-
const [toolExecutions, setToolExecutions] =
|
|
2601
|
-
const chatRef =
|
|
5211
|
+
const [toolExecutions, setToolExecutions] = React2.useState([]);
|
|
5212
|
+
const chatRef = React2.useRef(null);
|
|
2602
5213
|
if (chatRef.current !== null && chatRef.current.disposed) {
|
|
2603
5214
|
chatRef.current.revive();
|
|
2604
5215
|
debugLog("Revived disposed instance (React StrictMode)");
|
|
@@ -2626,7 +5237,8 @@ function CopilotProvider({
|
|
|
2626
5237
|
body,
|
|
2627
5238
|
debug,
|
|
2628
5239
|
maxIterations,
|
|
2629
|
-
maxIterationsMessage
|
|
5240
|
+
maxIterationsMessage,
|
|
5241
|
+
optimization
|
|
2630
5242
|
},
|
|
2631
5243
|
{
|
|
2632
5244
|
onToolExecutionsChange: (executions) => {
|
|
@@ -2636,92 +5248,111 @@ function CopilotProvider({
|
|
|
2636
5248
|
onApprovalRequired: (execution) => {
|
|
2637
5249
|
debugLog("Tool approval required:", execution.name);
|
|
2638
5250
|
},
|
|
5251
|
+
onContextUsageChange: (usage) => {
|
|
5252
|
+
setContextUsage(usage);
|
|
5253
|
+
},
|
|
2639
5254
|
onError: (error2) => {
|
|
2640
5255
|
if (error2) onError?.(error2);
|
|
2641
5256
|
}
|
|
2642
5257
|
}
|
|
2643
5258
|
);
|
|
2644
5259
|
}
|
|
2645
|
-
|
|
5260
|
+
React2.useEffect(() => {
|
|
2646
5261
|
if (chatRef.current && systemPrompt !== void 0) {
|
|
2647
5262
|
chatRef.current.setSystemPrompt(systemPrompt);
|
|
2648
5263
|
debugLog("System prompt updated from prop");
|
|
2649
5264
|
}
|
|
2650
5265
|
}, [systemPrompt, debugLog]);
|
|
2651
|
-
|
|
5266
|
+
React2.useEffect(() => {
|
|
2652
5267
|
if (chatRef.current && headers !== void 0) {
|
|
2653
5268
|
chatRef.current.setHeaders(headers);
|
|
2654
5269
|
debugLog("Headers config updated from prop");
|
|
2655
5270
|
}
|
|
2656
5271
|
}, [headers, debugLog]);
|
|
2657
|
-
|
|
5272
|
+
React2.useEffect(() => {
|
|
2658
5273
|
if (chatRef.current && body !== void 0) {
|
|
2659
5274
|
chatRef.current.setBody(body);
|
|
2660
5275
|
debugLog("Body config updated from prop");
|
|
2661
5276
|
}
|
|
2662
5277
|
}, [body, debugLog]);
|
|
2663
|
-
|
|
5278
|
+
React2.useEffect(() => {
|
|
2664
5279
|
if (chatRef.current && runtimeUrl !== void 0) {
|
|
2665
5280
|
chatRef.current.setUrl(runtimeUrl);
|
|
2666
5281
|
debugLog("URL config updated from prop");
|
|
2667
5282
|
}
|
|
2668
5283
|
}, [runtimeUrl, debugLog]);
|
|
2669
|
-
const
|
|
5284
|
+
const EMPTY_MESSAGES = React2.useRef([]);
|
|
5285
|
+
const getMessagesSnapshot = React2.useCallback(() => chatRef.current.messages, []);
|
|
5286
|
+
const getServerMessagesSnapshot = React2.useCallback(
|
|
5287
|
+
() => EMPTY_MESSAGES.current,
|
|
5288
|
+
[]
|
|
5289
|
+
);
|
|
5290
|
+
const getStatusSnapshot = React2.useCallback(() => chatRef.current.status, []);
|
|
5291
|
+
const getErrorSnapshot = React2.useCallback(() => chatRef.current.error, []);
|
|
5292
|
+
const messages = React2.useSyncExternalStore(
|
|
2670
5293
|
chatRef.current.subscribe,
|
|
2671
|
-
|
|
2672
|
-
|
|
5294
|
+
getMessagesSnapshot,
|
|
5295
|
+
getServerMessagesSnapshot
|
|
2673
5296
|
);
|
|
2674
|
-
const status =
|
|
5297
|
+
const status = React2.useSyncExternalStore(
|
|
2675
5298
|
chatRef.current.subscribe,
|
|
2676
|
-
|
|
5299
|
+
getStatusSnapshot,
|
|
2677
5300
|
() => "ready"
|
|
2678
5301
|
);
|
|
2679
|
-
const errorFromChat =
|
|
5302
|
+
const errorFromChat = React2.useSyncExternalStore(
|
|
2680
5303
|
chatRef.current.subscribe,
|
|
2681
|
-
|
|
5304
|
+
getErrorSnapshot,
|
|
2682
5305
|
() => void 0
|
|
2683
5306
|
);
|
|
2684
5307
|
const error = errorFromChat ?? null;
|
|
2685
5308
|
const isLoading = status === "streaming" || status === "submitted";
|
|
2686
|
-
const registerTool =
|
|
5309
|
+
const registerTool = React2.useCallback((tool2) => {
|
|
2687
5310
|
chatRef.current?.registerTool(tool2);
|
|
2688
5311
|
}, []);
|
|
2689
|
-
const unregisterTool =
|
|
5312
|
+
const unregisterTool = React2.useCallback((name) => {
|
|
2690
5313
|
chatRef.current?.unregisterTool(name);
|
|
2691
5314
|
}, []);
|
|
2692
|
-
const approveToolExecution =
|
|
5315
|
+
const approveToolExecution = React2.useCallback(
|
|
2693
5316
|
(id, extraData, permissionLevel) => {
|
|
2694
5317
|
chatRef.current?.approveToolExecution(id, extraData, permissionLevel);
|
|
2695
5318
|
},
|
|
2696
5319
|
[]
|
|
2697
5320
|
);
|
|
2698
|
-
const rejectToolExecution =
|
|
5321
|
+
const rejectToolExecution = React2.useCallback(
|
|
2699
5322
|
(id, reason, permissionLevel) => {
|
|
2700
5323
|
chatRef.current?.rejectToolExecution(id, reason, permissionLevel);
|
|
2701
5324
|
},
|
|
2702
5325
|
[]
|
|
2703
5326
|
);
|
|
2704
|
-
const registeredTools =
|
|
2705
|
-
|
|
2706
|
-
|
|
5327
|
+
const registeredTools = React2.useMemo(
|
|
5328
|
+
() => chatRef.current?.tools ?? [],
|
|
5329
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5330
|
+
[toolExecutions]
|
|
5331
|
+
// re-derive when tool executions change (tools change alongside)
|
|
2707
5332
|
);
|
|
2708
|
-
const
|
|
2709
|
-
|
|
2710
|
-
|
|
5333
|
+
const pendingApprovals = React2.useMemo(
|
|
5334
|
+
() => toolExecutions.filter((e) => e.approvalStatus === "required"),
|
|
5335
|
+
[toolExecutions]
|
|
5336
|
+
);
|
|
5337
|
+
const actionsRef = React2.useRef(/* @__PURE__ */ new Map());
|
|
5338
|
+
const [actionsVersion, setActionsVersion] = React2.useState(0);
|
|
5339
|
+
const registerAction = React2.useCallback((action) => {
|
|
2711
5340
|
actionsRef.current.set(action.name, action);
|
|
2712
5341
|
setActionsVersion((v) => v + 1);
|
|
2713
5342
|
}, []);
|
|
2714
|
-
const unregisterAction =
|
|
5343
|
+
const unregisterAction = React2.useCallback((name) => {
|
|
2715
5344
|
actionsRef.current.delete(name);
|
|
2716
5345
|
setActionsVersion((v) => v + 1);
|
|
2717
5346
|
}, []);
|
|
2718
|
-
const registeredActions =
|
|
5347
|
+
const registeredActions = React2.useMemo(
|
|
2719
5348
|
() => Array.from(actionsRef.current.values()),
|
|
2720
5349
|
[actionsVersion]
|
|
2721
5350
|
);
|
|
2722
|
-
const contextTreeRef =
|
|
2723
|
-
const contextIdCounter =
|
|
2724
|
-
const
|
|
5351
|
+
const contextTreeRef = React2.useRef([]);
|
|
5352
|
+
const contextIdCounter = React2.useRef(0);
|
|
5353
|
+
const [contextChars, setContextChars] = React2.useState(0);
|
|
5354
|
+
const [contextUsage, setContextUsage] = React2.useState(null);
|
|
5355
|
+
const addContext = React2.useCallback(
|
|
2725
5356
|
(context, parentId) => {
|
|
2726
5357
|
const id = `ctx-${++contextIdCounter.current}`;
|
|
2727
5358
|
contextTreeRef.current = addNode(
|
|
@@ -2731,55 +5362,95 @@ function CopilotProvider({
|
|
|
2731
5362
|
);
|
|
2732
5363
|
const contextString = printTree(contextTreeRef.current);
|
|
2733
5364
|
chatRef.current?.setContext(contextString);
|
|
5365
|
+
setContextChars(contextString.length);
|
|
2734
5366
|
debugLog("Context added:", id);
|
|
2735
5367
|
return id;
|
|
2736
5368
|
},
|
|
2737
5369
|
[debugLog]
|
|
2738
5370
|
);
|
|
2739
|
-
const removeContext =
|
|
5371
|
+
const removeContext = React2.useCallback(
|
|
2740
5372
|
(id) => {
|
|
2741
5373
|
contextTreeRef.current = removeNode(contextTreeRef.current, id);
|
|
2742
5374
|
const contextString = printTree(contextTreeRef.current);
|
|
2743
5375
|
chatRef.current?.setContext(contextString);
|
|
5376
|
+
setContextChars(contextString.length);
|
|
2744
5377
|
debugLog("Context removed:", id);
|
|
2745
5378
|
},
|
|
2746
5379
|
[debugLog]
|
|
2747
5380
|
);
|
|
2748
|
-
const setSystemPrompt =
|
|
5381
|
+
const setSystemPrompt = React2.useCallback(
|
|
2749
5382
|
(prompt) => {
|
|
2750
5383
|
chatRef.current?.setSystemPrompt(prompt);
|
|
2751
5384
|
debugLog("System prompt updated via function");
|
|
2752
5385
|
},
|
|
2753
5386
|
[debugLog]
|
|
2754
5387
|
);
|
|
2755
|
-
const
|
|
5388
|
+
const setInlineSkills = React2.useCallback(
|
|
5389
|
+
(skills2) => {
|
|
5390
|
+
chatRef.current?.setInlineSkills(skills2);
|
|
5391
|
+
debugLog("Inline skills updated", { count: skills2.length });
|
|
5392
|
+
},
|
|
5393
|
+
[debugLog]
|
|
5394
|
+
);
|
|
5395
|
+
const sendMessage = React2.useCallback(
|
|
2756
5396
|
async (content, attachments) => {
|
|
2757
5397
|
debugLog("Sending message:", content);
|
|
2758
5398
|
await chatRef.current?.sendMessage(content, attachments);
|
|
2759
5399
|
},
|
|
2760
5400
|
[debugLog]
|
|
2761
5401
|
);
|
|
2762
|
-
const stop =
|
|
5402
|
+
const stop = React2.useCallback(() => {
|
|
2763
5403
|
chatRef.current?.stop();
|
|
2764
5404
|
}, []);
|
|
2765
|
-
const clearMessages =
|
|
5405
|
+
const clearMessages = React2.useCallback(() => {
|
|
2766
5406
|
chatRef.current?.clearMessages();
|
|
2767
5407
|
}, []);
|
|
2768
|
-
const setMessages =
|
|
5408
|
+
const setMessages = React2.useCallback((messages2) => {
|
|
2769
5409
|
chatRef.current?.setMessages(messages2);
|
|
2770
5410
|
}, []);
|
|
2771
|
-
const regenerate =
|
|
5411
|
+
const regenerate = React2.useCallback(async (messageId) => {
|
|
2772
5412
|
await chatRef.current?.regenerate(messageId);
|
|
2773
5413
|
}, []);
|
|
2774
|
-
|
|
5414
|
+
const switchBranch = React2.useCallback((messageId) => {
|
|
5415
|
+
chatRef.current?.switchBranch(messageId);
|
|
5416
|
+
}, []);
|
|
5417
|
+
const getBranchInfo = React2.useCallback(
|
|
5418
|
+
(messageId) => chatRef.current?.getBranchInfo(messageId) ?? null,
|
|
5419
|
+
[]
|
|
5420
|
+
);
|
|
5421
|
+
const editMessage = React2.useCallback(
|
|
5422
|
+
async (messageId, newContent) => {
|
|
5423
|
+
await chatRef.current?.sendMessage(newContent, void 0, {
|
|
5424
|
+
editMessageId: messageId
|
|
5425
|
+
});
|
|
5426
|
+
},
|
|
5427
|
+
[]
|
|
5428
|
+
);
|
|
5429
|
+
const getHasBranchesSnapshot = React2.useCallback(
|
|
5430
|
+
() => chatRef.current.hasBranches,
|
|
5431
|
+
[]
|
|
5432
|
+
);
|
|
5433
|
+
const hasBranches = React2.useSyncExternalStore(
|
|
5434
|
+
chatRef.current.subscribe,
|
|
5435
|
+
getHasBranchesSnapshot,
|
|
5436
|
+
() => false
|
|
5437
|
+
);
|
|
5438
|
+
const getAllMessages = React2.useCallback(
|
|
5439
|
+
() => chatRef.current?.getAllMessages?.() ?? [],
|
|
5440
|
+
[]
|
|
5441
|
+
);
|
|
5442
|
+
React2.useEffect(() => {
|
|
2775
5443
|
if (onMessagesChange && messages.length > 0) {
|
|
2776
|
-
const
|
|
5444
|
+
const allUIMessages = chatRef.current?.getAllMessages?.() ?? messages;
|
|
5445
|
+
const coreMessages = allUIMessages.map((m) => ({
|
|
2777
5446
|
id: m.id,
|
|
2778
5447
|
role: m.role,
|
|
2779
5448
|
content: m.content,
|
|
2780
5449
|
created_at: m.createdAt,
|
|
2781
5450
|
tool_calls: m.toolCalls,
|
|
2782
5451
|
tool_call_id: m.toolCallId,
|
|
5452
|
+
parent_id: m.parentId,
|
|
5453
|
+
children_ids: m.childrenIds,
|
|
2783
5454
|
metadata: {
|
|
2784
5455
|
attachments: m.attachments,
|
|
2785
5456
|
thinking: m.thinking
|
|
@@ -2788,17 +5459,17 @@ function CopilotProvider({
|
|
|
2788
5459
|
onMessagesChange(coreMessages);
|
|
2789
5460
|
}
|
|
2790
5461
|
}, [messages, onMessagesChange]);
|
|
2791
|
-
|
|
5462
|
+
React2.useEffect(() => {
|
|
2792
5463
|
if (error && onError) {
|
|
2793
5464
|
onError(error);
|
|
2794
5465
|
}
|
|
2795
5466
|
}, [error, onError]);
|
|
2796
|
-
|
|
5467
|
+
React2.useEffect(() => {
|
|
2797
5468
|
return () => {
|
|
2798
5469
|
chatRef.current?.dispose();
|
|
2799
5470
|
};
|
|
2800
5471
|
}, []);
|
|
2801
|
-
const contextValue =
|
|
5472
|
+
const contextValue = React2.useMemo(
|
|
2802
5473
|
() => ({
|
|
2803
5474
|
// Chat state
|
|
2804
5475
|
messages,
|
|
@@ -2811,6 +5482,12 @@ function CopilotProvider({
|
|
|
2811
5482
|
clearMessages,
|
|
2812
5483
|
setMessages,
|
|
2813
5484
|
regenerate,
|
|
5485
|
+
// Branching
|
|
5486
|
+
switchBranch,
|
|
5487
|
+
getBranchInfo,
|
|
5488
|
+
editMessage,
|
|
5489
|
+
hasBranches,
|
|
5490
|
+
getAllMessages,
|
|
2814
5491
|
// Tool execution
|
|
2815
5492
|
registerTool,
|
|
2816
5493
|
unregisterTool,
|
|
@@ -2826,8 +5503,12 @@ function CopilotProvider({
|
|
|
2826
5503
|
// AI Context
|
|
2827
5504
|
addContext,
|
|
2828
5505
|
removeContext,
|
|
5506
|
+
contextChars,
|
|
5507
|
+
contextUsage,
|
|
2829
5508
|
// System Prompt
|
|
2830
5509
|
setSystemPrompt,
|
|
5510
|
+
// Skills
|
|
5511
|
+
setInlineSkills,
|
|
2831
5512
|
// Config
|
|
2832
5513
|
threadId,
|
|
2833
5514
|
runtimeUrl,
|
|
@@ -2843,6 +5524,11 @@ function CopilotProvider({
|
|
|
2843
5524
|
clearMessages,
|
|
2844
5525
|
setMessages,
|
|
2845
5526
|
regenerate,
|
|
5527
|
+
switchBranch,
|
|
5528
|
+
getBranchInfo,
|
|
5529
|
+
editMessage,
|
|
5530
|
+
hasBranches,
|
|
5531
|
+
getAllMessages,
|
|
2846
5532
|
registerTool,
|
|
2847
5533
|
unregisterTool,
|
|
2848
5534
|
registeredTools,
|
|
@@ -2855,20 +5541,45 @@ function CopilotProvider({
|
|
|
2855
5541
|
registeredActions,
|
|
2856
5542
|
addContext,
|
|
2857
5543
|
removeContext,
|
|
5544
|
+
contextChars,
|
|
5545
|
+
contextUsage,
|
|
2858
5546
|
setSystemPrompt,
|
|
5547
|
+
setInlineSkills,
|
|
2859
5548
|
threadId,
|
|
2860
5549
|
runtimeUrl,
|
|
2861
5550
|
toolsConfig
|
|
2862
5551
|
]
|
|
2863
5552
|
);
|
|
2864
|
-
|
|
5553
|
+
const messageHistoryContextValue = React2__default.default.useMemo(
|
|
5554
|
+
() => ({
|
|
5555
|
+
config: { ...defaultMessageHistoryConfig, ...messageHistory },
|
|
5556
|
+
tokenUsage: {
|
|
5557
|
+
current: 0,
|
|
5558
|
+
max: messageHistory?.maxContextTokens ?? 128e3,
|
|
5559
|
+
percentage: 0,
|
|
5560
|
+
isApproaching: false
|
|
5561
|
+
},
|
|
5562
|
+
compactionState: {
|
|
5563
|
+
rollingSummary: null,
|
|
5564
|
+
lastCompactionAt: null,
|
|
5565
|
+
compactionCount: 0,
|
|
5566
|
+
totalTokensSaved: 0,
|
|
5567
|
+
workingMemory: [],
|
|
5568
|
+
displayMessageCount: 0,
|
|
5569
|
+
llmMessageCount: 0
|
|
5570
|
+
}
|
|
5571
|
+
}),
|
|
5572
|
+
[messageHistory]
|
|
5573
|
+
);
|
|
5574
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MessageHistoryContext.Provider, { value: messageHistoryContextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(CopilotContext.Provider, { value: contextValue, children: [
|
|
2865
5575
|
mcpServers?.map((config) => /* @__PURE__ */ jsxRuntime.jsx(MCPConnection, { config }, config.name)),
|
|
2866
|
-
|
|
2867
|
-
|
|
5576
|
+
messageHistory?.strategy && messageHistory.strategy !== "none" && /* @__PURE__ */ jsxRuntime.jsx(MessageHistoryBridge, { chatRef }),
|
|
5577
|
+
skills ? /* @__PURE__ */ jsxRuntime.jsx(SkillProvider, { skills, children }) : children
|
|
5578
|
+
] }) });
|
|
2868
5579
|
}
|
|
2869
5580
|
function useAIActions(actions) {
|
|
2870
5581
|
const { registerAction, unregisterAction } = useCopilot();
|
|
2871
|
-
|
|
5582
|
+
React2.useEffect(() => {
|
|
2872
5583
|
for (const action of actions) {
|
|
2873
5584
|
registerAction(action);
|
|
2874
5585
|
}
|
|
@@ -2882,61 +5593,6 @@ function useAIActions(actions) {
|
|
|
2882
5593
|
function useAIAction(action) {
|
|
2883
5594
|
useAIActions([action]);
|
|
2884
5595
|
}
|
|
2885
|
-
function useAIContext(item) {
|
|
2886
|
-
const { addContext, removeContext } = useCopilot();
|
|
2887
|
-
const contextIdRef = react.useRef(null);
|
|
2888
|
-
const serializedData = typeof item.data === "string" ? item.data : JSON.stringify(item.data);
|
|
2889
|
-
react.useEffect(() => {
|
|
2890
|
-
const formattedValue = typeof item.data === "string" ? item.data : JSON.stringify(item.data, null, 2);
|
|
2891
|
-
const contextString = item.description ? `${item.description}:
|
|
2892
|
-
${formattedValue}` : `${item.key}:
|
|
2893
|
-
${formattedValue}`;
|
|
2894
|
-
contextIdRef.current = addContext(contextString, item.parentId);
|
|
2895
|
-
return () => {
|
|
2896
|
-
if (contextIdRef.current) {
|
|
2897
|
-
removeContext(contextIdRef.current);
|
|
2898
|
-
contextIdRef.current = null;
|
|
2899
|
-
}
|
|
2900
|
-
};
|
|
2901
|
-
}, [
|
|
2902
|
-
item.key,
|
|
2903
|
-
serializedData,
|
|
2904
|
-
item.description,
|
|
2905
|
-
item.parentId,
|
|
2906
|
-
addContext,
|
|
2907
|
-
removeContext
|
|
2908
|
-
]);
|
|
2909
|
-
return contextIdRef.current ?? void 0;
|
|
2910
|
-
}
|
|
2911
|
-
function useAIContexts(items) {
|
|
2912
|
-
const { addContext, removeContext } = useCopilot();
|
|
2913
|
-
const contextIdsRef = react.useRef([]);
|
|
2914
|
-
const serializedItems = JSON.stringify(
|
|
2915
|
-
items.map((item) => ({
|
|
2916
|
-
key: item.key,
|
|
2917
|
-
data: item.data,
|
|
2918
|
-
description: item.description,
|
|
2919
|
-
parentId: item.parentId
|
|
2920
|
-
}))
|
|
2921
|
-
);
|
|
2922
|
-
react.useEffect(() => {
|
|
2923
|
-
contextIdsRef.current.forEach((id) => removeContext(id));
|
|
2924
|
-
contextIdsRef.current = [];
|
|
2925
|
-
const parsedItems = JSON.parse(serializedItems);
|
|
2926
|
-
for (const item of parsedItems) {
|
|
2927
|
-
const formattedValue = typeof item.data === "string" ? item.data : JSON.stringify(item.data, null, 2);
|
|
2928
|
-
const contextString = item.description ? `${item.description}:
|
|
2929
|
-
${formattedValue}` : `${item.key}:
|
|
2930
|
-
${formattedValue}`;
|
|
2931
|
-
const id = addContext(contextString, item.parentId);
|
|
2932
|
-
contextIdsRef.current.push(id);
|
|
2933
|
-
}
|
|
2934
|
-
return () => {
|
|
2935
|
-
contextIdsRef.current.forEach((id) => removeContext(id));
|
|
2936
|
-
contextIdsRef.current = [];
|
|
2937
|
-
};
|
|
2938
|
-
}, [serializedItems, addContext, removeContext]);
|
|
2939
|
-
}
|
|
2940
5596
|
function useAITools(options = {}) {
|
|
2941
5597
|
const {
|
|
2942
5598
|
screenshot = false,
|
|
@@ -2949,27 +5605,27 @@ function useAITools(options = {}) {
|
|
|
2949
5605
|
onConsentRequest,
|
|
2950
5606
|
autoStart = true
|
|
2951
5607
|
} = options;
|
|
2952
|
-
const [isEnabled] =
|
|
2953
|
-
const [activeCaptures, setActiveCaptures] =
|
|
5608
|
+
const [isEnabled] = React2.useState(screenshot || consoleCapture || network);
|
|
5609
|
+
const [activeCaptures, setActiveCaptures] = React2.useState({
|
|
2954
5610
|
console: false,
|
|
2955
5611
|
network: false
|
|
2956
5612
|
});
|
|
2957
|
-
const [pendingConsent, setPendingConsent] =
|
|
2958
|
-
const consentResolverRef =
|
|
2959
|
-
const rememberedConsentRef =
|
|
2960
|
-
|
|
5613
|
+
const [pendingConsent, setPendingConsent] = React2.useState(null);
|
|
5614
|
+
const consentResolverRef = React2.useRef(null);
|
|
5615
|
+
const rememberedConsentRef = React2.useRef(/* @__PURE__ */ new Set());
|
|
5616
|
+
React2.useEffect(() => {
|
|
2961
5617
|
if (!autoStart || !isEnabled) return;
|
|
2962
|
-
if (consoleCapture && !
|
|
2963
|
-
|
|
5618
|
+
if (consoleCapture && !chunkI3SQUNTT_cjs.isConsoleCaptureActive()) {
|
|
5619
|
+
chunkI3SQUNTT_cjs.startConsoleCapture(consoleOptions);
|
|
2964
5620
|
setActiveCaptures((prev) => ({ ...prev, console: true }));
|
|
2965
5621
|
}
|
|
2966
|
-
if (network && !
|
|
2967
|
-
|
|
5622
|
+
if (network && !chunkI3SQUNTT_cjs.isNetworkCaptureActive()) {
|
|
5623
|
+
chunkI3SQUNTT_cjs.startNetworkCapture(networkOptions);
|
|
2968
5624
|
setActiveCaptures((prev) => ({ ...prev, network: true }));
|
|
2969
5625
|
}
|
|
2970
5626
|
return () => {
|
|
2971
|
-
|
|
2972
|
-
|
|
5627
|
+
chunkI3SQUNTT_cjs.stopConsoleCapture();
|
|
5628
|
+
chunkI3SQUNTT_cjs.stopNetworkCapture();
|
|
2973
5629
|
};
|
|
2974
5630
|
}, [
|
|
2975
5631
|
autoStart,
|
|
@@ -2979,39 +5635,39 @@ function useAITools(options = {}) {
|
|
|
2979
5635
|
consoleOptions,
|
|
2980
5636
|
networkOptions
|
|
2981
5637
|
]);
|
|
2982
|
-
const captureScreenshotFn =
|
|
5638
|
+
const captureScreenshotFn = React2.useCallback(
|
|
2983
5639
|
async (opts) => {
|
|
2984
5640
|
if (!screenshot) {
|
|
2985
5641
|
throw new Error("Screenshot capture is not enabled");
|
|
2986
5642
|
}
|
|
2987
|
-
if (!
|
|
5643
|
+
if (!chunkI3SQUNTT_cjs.isScreenshotSupported()) {
|
|
2988
5644
|
throw new Error(
|
|
2989
5645
|
"Screenshot capture is not supported in this environment"
|
|
2990
5646
|
);
|
|
2991
5647
|
}
|
|
2992
|
-
return
|
|
5648
|
+
return chunkI3SQUNTT_cjs.captureScreenshot({ ...screenshotOptions, ...opts });
|
|
2993
5649
|
},
|
|
2994
5650
|
[screenshot, screenshotOptions]
|
|
2995
5651
|
);
|
|
2996
|
-
const getConsoleLogsFn =
|
|
5652
|
+
const getConsoleLogsFn = React2.useCallback(
|
|
2997
5653
|
(opts) => {
|
|
2998
5654
|
if (!consoleCapture) {
|
|
2999
5655
|
return { logs: [], totalCaptured: 0 };
|
|
3000
5656
|
}
|
|
3001
|
-
return
|
|
5657
|
+
return chunkI3SQUNTT_cjs.getConsoleLogs({ ...consoleOptions, ...opts });
|
|
3002
5658
|
},
|
|
3003
5659
|
[consoleCapture, consoleOptions]
|
|
3004
5660
|
);
|
|
3005
|
-
const getNetworkRequestsFn =
|
|
5661
|
+
const getNetworkRequestsFn = React2.useCallback(
|
|
3006
5662
|
(opts) => {
|
|
3007
5663
|
if (!network) {
|
|
3008
5664
|
return { requests: [], totalCaptured: 0 };
|
|
3009
5665
|
}
|
|
3010
|
-
return
|
|
5666
|
+
return chunkI3SQUNTT_cjs.getNetworkRequests({ ...networkOptions, ...opts });
|
|
3011
5667
|
},
|
|
3012
5668
|
[network, networkOptions]
|
|
3013
5669
|
);
|
|
3014
|
-
const requestConsent =
|
|
5670
|
+
const requestConsent = React2.useCallback(
|
|
3015
5671
|
async (tools, reason = "") => {
|
|
3016
5672
|
const enabledTools = tools.filter((tool2) => {
|
|
3017
5673
|
if (tool2 === "screenshot") return screenshot;
|
|
@@ -3059,14 +5715,14 @@ function useAITools(options = {}) {
|
|
|
3059
5715
|
},
|
|
3060
5716
|
[screenshot, consoleCapture, network, requireConsent, onConsentRequest]
|
|
3061
5717
|
);
|
|
3062
|
-
const respondToConsent =
|
|
5718
|
+
const respondToConsent = React2.useCallback((response) => {
|
|
3063
5719
|
if (consentResolverRef.current) {
|
|
3064
5720
|
consentResolverRef.current(response);
|
|
3065
5721
|
consentResolverRef.current = null;
|
|
3066
5722
|
}
|
|
3067
5723
|
setPendingConsent(null);
|
|
3068
5724
|
}, []);
|
|
3069
|
-
const captureContext =
|
|
5725
|
+
const captureContext = React2.useCallback(
|
|
3070
5726
|
async (tools) => {
|
|
3071
5727
|
const toolsToCapture = tools || ["screenshot", "console", "network"];
|
|
3072
5728
|
const context = {
|
|
@@ -3099,26 +5755,26 @@ function useAITools(options = {}) {
|
|
|
3099
5755
|
getNetworkRequestsFn
|
|
3100
5756
|
]
|
|
3101
5757
|
);
|
|
3102
|
-
const startCapturing =
|
|
3103
|
-
if (consoleCapture && !
|
|
3104
|
-
|
|
5758
|
+
const startCapturing = React2.useCallback(() => {
|
|
5759
|
+
if (consoleCapture && !chunkI3SQUNTT_cjs.isConsoleCaptureActive()) {
|
|
5760
|
+
chunkI3SQUNTT_cjs.startConsoleCapture(consoleOptions);
|
|
3105
5761
|
setActiveCaptures((prev) => ({ ...prev, console: true }));
|
|
3106
5762
|
}
|
|
3107
|
-
if (network && !
|
|
3108
|
-
|
|
5763
|
+
if (network && !chunkI3SQUNTT_cjs.isNetworkCaptureActive()) {
|
|
5764
|
+
chunkI3SQUNTT_cjs.startNetworkCapture(networkOptions);
|
|
3109
5765
|
setActiveCaptures((prev) => ({ ...prev, network: true }));
|
|
3110
5766
|
}
|
|
3111
5767
|
}, [consoleCapture, network, consoleOptions, networkOptions]);
|
|
3112
|
-
const stopCapturing =
|
|
3113
|
-
|
|
3114
|
-
|
|
5768
|
+
const stopCapturing = React2.useCallback(() => {
|
|
5769
|
+
chunkI3SQUNTT_cjs.stopConsoleCapture();
|
|
5770
|
+
chunkI3SQUNTT_cjs.stopNetworkCapture();
|
|
3115
5771
|
setActiveCaptures({ console: false, network: false });
|
|
3116
5772
|
}, []);
|
|
3117
|
-
const clearCaptured =
|
|
3118
|
-
|
|
3119
|
-
|
|
5773
|
+
const clearCaptured = React2.useCallback(() => {
|
|
5774
|
+
chunkI3SQUNTT_cjs.clearConsoleLogs();
|
|
5775
|
+
chunkI3SQUNTT_cjs.clearNetworkRequests();
|
|
3120
5776
|
}, []);
|
|
3121
|
-
const formatForAI =
|
|
5777
|
+
const formatForAI = React2.useCallback((context) => {
|
|
3122
5778
|
const parts = [];
|
|
3123
5779
|
if (context.screenshot) {
|
|
3124
5780
|
parts.push(
|
|
@@ -3126,16 +5782,16 @@ function useAITools(options = {}) {
|
|
|
3126
5782
|
);
|
|
3127
5783
|
}
|
|
3128
5784
|
if (context.consoleLogs && context.consoleLogs.logs.length > 0) {
|
|
3129
|
-
parts.push(
|
|
5785
|
+
parts.push(chunkI3SQUNTT_cjs.formatLogsForAI(context.consoleLogs.logs));
|
|
3130
5786
|
}
|
|
3131
5787
|
if (context.networkRequests && context.networkRequests.requests.length > 0) {
|
|
3132
|
-
parts.push(
|
|
5788
|
+
parts.push(chunkI3SQUNTT_cjs.formatRequestsForAI(context.networkRequests.requests));
|
|
3133
5789
|
}
|
|
3134
5790
|
return parts.length > 0 ? parts.join("\n\n---\n\n") : "No context captured.";
|
|
3135
5791
|
}, []);
|
|
3136
|
-
const detectIntentFn =
|
|
5792
|
+
const detectIntentFn = React2.useCallback(
|
|
3137
5793
|
(message) => {
|
|
3138
|
-
const result =
|
|
5794
|
+
const result = chunkI3SQUNTT_cjs.detectIntent(message);
|
|
3139
5795
|
result.suggestedTools = result.suggestedTools.filter((tool2) => {
|
|
3140
5796
|
if (tool2 === "screenshot") return screenshot;
|
|
3141
5797
|
if (tool2 === "console") return consoleCapture;
|
|
@@ -3146,7 +5802,7 @@ function useAITools(options = {}) {
|
|
|
3146
5802
|
},
|
|
3147
5803
|
[screenshot, consoleCapture, network]
|
|
3148
5804
|
);
|
|
3149
|
-
return
|
|
5805
|
+
return React2.useMemo(
|
|
3150
5806
|
() => ({
|
|
3151
5807
|
isEnabled,
|
|
3152
5808
|
activeCaptures,
|
|
@@ -3181,69 +5837,6 @@ function useAITools(options = {}) {
|
|
|
3181
5837
|
]
|
|
3182
5838
|
);
|
|
3183
5839
|
}
|
|
3184
|
-
function isZodSchema(value) {
|
|
3185
|
-
if (value === null || typeof value !== "object") return false;
|
|
3186
|
-
const obj = value;
|
|
3187
|
-
return "_def" in obj && typeof obj._def === "object" || "_zod" in obj && typeof obj._zod === "object" || "~standard" in obj;
|
|
3188
|
-
}
|
|
3189
|
-
function useTool(config, dependencies = []) {
|
|
3190
|
-
const { registerTool, unregisterTool } = useCopilot();
|
|
3191
|
-
const configRef = react.useRef(config);
|
|
3192
|
-
configRef.current = config;
|
|
3193
|
-
const inputSchema = react.useMemo(() => {
|
|
3194
|
-
if (isZodSchema(config.inputSchema)) {
|
|
3195
|
-
return chunkOQPRIB73_cjs.zodToJsonSchema(config.inputSchema);
|
|
3196
|
-
}
|
|
3197
|
-
return config.inputSchema;
|
|
3198
|
-
}, [config.inputSchema]);
|
|
3199
|
-
react.useEffect(() => {
|
|
3200
|
-
const tool2 = {
|
|
3201
|
-
name: config.name,
|
|
3202
|
-
description: config.description,
|
|
3203
|
-
location: "client",
|
|
3204
|
-
inputSchema,
|
|
3205
|
-
handler: async (params, context) => {
|
|
3206
|
-
return configRef.current.handler(params, context);
|
|
3207
|
-
},
|
|
3208
|
-
render: config.render,
|
|
3209
|
-
available: config.available ?? true,
|
|
3210
|
-
needsApproval: config.needsApproval,
|
|
3211
|
-
approvalMessage: config.approvalMessage,
|
|
3212
|
-
hidden: config.hidden
|
|
3213
|
-
};
|
|
3214
|
-
registerTool(tool2);
|
|
3215
|
-
return () => {
|
|
3216
|
-
unregisterTool(config.name);
|
|
3217
|
-
};
|
|
3218
|
-
}, [config.name, inputSchema, ...dependencies]);
|
|
3219
|
-
}
|
|
3220
|
-
function useTools(tools) {
|
|
3221
|
-
const { registerTool, unregisterTool } = useCopilot();
|
|
3222
|
-
const registeredToolsRef = react.useRef([]);
|
|
3223
|
-
const toolsRef = react.useRef(tools);
|
|
3224
|
-
toolsRef.current = tools;
|
|
3225
|
-
const toolsKey = Object.keys(tools).sort().join(",");
|
|
3226
|
-
react.useEffect(() => {
|
|
3227
|
-
const currentTools = toolsRef.current;
|
|
3228
|
-
const toolNames = [];
|
|
3229
|
-
for (const [name, toolDef] of Object.entries(currentTools)) {
|
|
3230
|
-
const fullTool = {
|
|
3231
|
-
...toolDef,
|
|
3232
|
-
name
|
|
3233
|
-
// Use the key as the name
|
|
3234
|
-
};
|
|
3235
|
-
registerTool(fullTool);
|
|
3236
|
-
toolNames.push(name);
|
|
3237
|
-
}
|
|
3238
|
-
registeredToolsRef.current = toolNames;
|
|
3239
|
-
return () => {
|
|
3240
|
-
for (const name of registeredToolsRef.current) {
|
|
3241
|
-
unregisterTool(name);
|
|
3242
|
-
}
|
|
3243
|
-
registeredToolsRef.current = [];
|
|
3244
|
-
};
|
|
3245
|
-
}, [toolsKey]);
|
|
3246
|
-
}
|
|
3247
5840
|
function convertZodSchema(schema, _toolName) {
|
|
3248
5841
|
try {
|
|
3249
5842
|
const zodWithJsonSchema = z__namespace;
|
|
@@ -3261,13 +5854,13 @@ function convertZodSchema(schema, _toolName) {
|
|
|
3261
5854
|
}
|
|
3262
5855
|
} catch {
|
|
3263
5856
|
}
|
|
3264
|
-
return
|
|
5857
|
+
return chunkI3SQUNTT_cjs.zodObjectToInputSchema(schema);
|
|
3265
5858
|
}
|
|
3266
5859
|
function useToolWithSchema(config, dependencies = []) {
|
|
3267
5860
|
const { registerTool, unregisterTool } = useCopilot();
|
|
3268
|
-
const configRef =
|
|
5861
|
+
const configRef = React2.useRef(config);
|
|
3269
5862
|
configRef.current = config;
|
|
3270
|
-
const inputSchema =
|
|
5863
|
+
const inputSchema = React2.useMemo(() => {
|
|
3271
5864
|
try {
|
|
3272
5865
|
return convertZodSchema(config.schema, config.name);
|
|
3273
5866
|
} catch (error) {
|
|
@@ -3281,7 +5874,7 @@ function useToolWithSchema(config, dependencies = []) {
|
|
|
3281
5874
|
};
|
|
3282
5875
|
}
|
|
3283
5876
|
}, [config.schema, config.name]);
|
|
3284
|
-
|
|
5877
|
+
React2.useEffect(() => {
|
|
3285
5878
|
const tool2 = {
|
|
3286
5879
|
name: config.name,
|
|
3287
5880
|
description: config.description,
|
|
@@ -3301,9 +5894,9 @@ function useToolWithSchema(config, dependencies = []) {
|
|
|
3301
5894
|
}
|
|
3302
5895
|
function useToolsWithSchema(tools, dependencies = []) {
|
|
3303
5896
|
const { registerTool, unregisterTool } = useCopilot();
|
|
3304
|
-
const toolsRef =
|
|
5897
|
+
const toolsRef = React2.useRef(tools);
|
|
3305
5898
|
toolsRef.current = tools;
|
|
3306
|
-
|
|
5899
|
+
React2.useEffect(() => {
|
|
3307
5900
|
const toolNames = [];
|
|
3308
5901
|
for (const config of tools) {
|
|
3309
5902
|
let inputSchema;
|
|
@@ -3342,9 +5935,9 @@ function useToolsWithSchema(tools, dependencies = []) {
|
|
|
3342
5935
|
};
|
|
3343
5936
|
}, [tools.map((t) => t.name).join(","), ...dependencies]);
|
|
3344
5937
|
}
|
|
3345
|
-
var CopilotContext2 =
|
|
5938
|
+
var CopilotContext2 = React2.createContext(null);
|
|
3346
5939
|
function useCopilotContext() {
|
|
3347
|
-
const context =
|
|
5940
|
+
const context = React2.useContext(CopilotContext2);
|
|
3348
5941
|
if (!context) {
|
|
3349
5942
|
throw new Error("useCopilotContext must be used within a CopilotProvider");
|
|
3350
5943
|
}
|
|
@@ -3360,9 +5953,9 @@ function useToolExecutor() {
|
|
|
3360
5953
|
addToolExecution,
|
|
3361
5954
|
updateToolExecution
|
|
3362
5955
|
} = useCopilotContext();
|
|
3363
|
-
const toolsRef =
|
|
5956
|
+
const toolsRef = React2.useRef(registeredTools);
|
|
3364
5957
|
toolsRef.current = registeredTools;
|
|
3365
|
-
const executeTool =
|
|
5958
|
+
const executeTool = React2.useCallback(
|
|
3366
5959
|
async (toolCall) => {
|
|
3367
5960
|
const tool2 = toolsRef.current.find((t) => t.name === toolCall.name);
|
|
3368
5961
|
if (!tool2) {
|
|
@@ -3412,7 +6005,7 @@ function useToolExecutor() {
|
|
|
3412
6005
|
},
|
|
3413
6006
|
[addToolExecution, updateToolExecution]
|
|
3414
6007
|
);
|
|
3415
|
-
const sendToolResult =
|
|
6008
|
+
const sendToolResult = React2.useCallback(
|
|
3416
6009
|
async (toolCallId, result) => {
|
|
3417
6010
|
const runtimeUrl = config.runtimeUrl || config.cloud?.endpoint;
|
|
3418
6011
|
if (!runtimeUrl) {
|
|
@@ -3446,10 +6039,10 @@ function useToolExecutor() {
|
|
|
3446
6039
|
},
|
|
3447
6040
|
[config.runtimeUrl, config.cloud?.endpoint, chat.threadId]
|
|
3448
6041
|
);
|
|
3449
|
-
const getTool =
|
|
6042
|
+
const getTool = React2.useCallback((name) => {
|
|
3450
6043
|
return toolsRef.current.find((t) => t.name === name);
|
|
3451
6044
|
}, []);
|
|
3452
|
-
const hasTool =
|
|
6045
|
+
const hasTool = React2.useCallback((name) => {
|
|
3453
6046
|
return toolsRef.current.some((t) => t.name === name);
|
|
3454
6047
|
}, []);
|
|
3455
6048
|
return {
|
|
@@ -3467,13 +6060,13 @@ function useSuggestions(options = {}) {
|
|
|
3467
6060
|
autoRefresh = true
|
|
3468
6061
|
} = options;
|
|
3469
6062
|
const { chat, actions, config } = useCopilotContext();
|
|
3470
|
-
const [suggestions, setSuggestions] =
|
|
3471
|
-
const [isLoading, setIsLoading] =
|
|
3472
|
-
const normalizedStatic =
|
|
6063
|
+
const [suggestions, setSuggestions] = React2.useState([]);
|
|
6064
|
+
const [isLoading, setIsLoading] = React2.useState(false);
|
|
6065
|
+
const normalizedStatic = React2.useMemo(
|
|
3473
6066
|
() => staticSuggestions?.map((s) => typeof s === "string" ? { text: s } : s),
|
|
3474
6067
|
[staticSuggestions]
|
|
3475
6068
|
);
|
|
3476
|
-
const refresh =
|
|
6069
|
+
const refresh = React2.useCallback(async () => {
|
|
3477
6070
|
if (normalizedStatic) {
|
|
3478
6071
|
setSuggestions(normalizedStatic.slice(0, count));
|
|
3479
6072
|
return;
|
|
@@ -3512,14 +6105,14 @@ function useSuggestions(options = {}) {
|
|
|
3512
6105
|
setIsLoading(false);
|
|
3513
6106
|
}
|
|
3514
6107
|
}, [config.cloud, count, context, chat.messages, normalizedStatic]);
|
|
3515
|
-
const select =
|
|
6108
|
+
const select = React2.useCallback(
|
|
3516
6109
|
(suggestion) => {
|
|
3517
6110
|
const text = typeof suggestion === "string" ? suggestion : suggestion.text;
|
|
3518
6111
|
actions.sendMessage(text);
|
|
3519
6112
|
},
|
|
3520
6113
|
[actions]
|
|
3521
6114
|
);
|
|
3522
|
-
|
|
6115
|
+
React2.useEffect(() => {
|
|
3523
6116
|
if (autoRefresh && chat.messages.length === 0) {
|
|
3524
6117
|
refresh();
|
|
3525
6118
|
}
|
|
@@ -3534,18 +6127,18 @@ function useSuggestions(options = {}) {
|
|
|
3534
6127
|
function useAgent(options) {
|
|
3535
6128
|
const { name, initialState = {}, onStateChange } = options;
|
|
3536
6129
|
const { config } = useCopilotContext();
|
|
3537
|
-
const [state, setStateInternal] =
|
|
3538
|
-
const [isRunning, setIsRunning] =
|
|
3539
|
-
const [nodeName, setNodeName] =
|
|
3540
|
-
const [error, setError] =
|
|
3541
|
-
const abortControllerRef =
|
|
3542
|
-
const getEndpoint =
|
|
6130
|
+
const [state, setStateInternal] = React2.useState(initialState);
|
|
6131
|
+
const [isRunning, setIsRunning] = React2.useState(false);
|
|
6132
|
+
const [nodeName, setNodeName] = React2.useState(null);
|
|
6133
|
+
const [error, setError] = React2.useState(null);
|
|
6134
|
+
const abortControllerRef = React2.useRef(null);
|
|
6135
|
+
const getEndpoint = React2.useCallback(() => {
|
|
3543
6136
|
if (config.cloud) {
|
|
3544
6137
|
return `${config.cloud.endpoint || "https://api.yourgpt.ai/v1"}/agents/${name}`;
|
|
3545
6138
|
}
|
|
3546
6139
|
return `${config.runtimeUrl || "/api"}/agents/${name}`;
|
|
3547
6140
|
}, [config, name]);
|
|
3548
|
-
const start =
|
|
6141
|
+
const start = React2.useCallback(
|
|
3549
6142
|
async (input) => {
|
|
3550
6143
|
setIsRunning(true);
|
|
3551
6144
|
setError(null);
|
|
@@ -3570,7 +6163,7 @@ function useAgent(options) {
|
|
|
3570
6163
|
if (!response.ok) {
|
|
3571
6164
|
throw new Error(`Agent error: ${response.status}`);
|
|
3572
6165
|
}
|
|
3573
|
-
for await (const event of
|
|
6166
|
+
for await (const event of chunkI3SQUNTT_cjs.streamSSE(response)) {
|
|
3574
6167
|
handleAgentEvent(event);
|
|
3575
6168
|
}
|
|
3576
6169
|
} catch (err) {
|
|
@@ -3584,7 +6177,7 @@ function useAgent(options) {
|
|
|
3584
6177
|
},
|
|
3585
6178
|
[config, getEndpoint, state]
|
|
3586
6179
|
);
|
|
3587
|
-
const handleAgentEvent =
|
|
6180
|
+
const handleAgentEvent = React2.useCallback(
|
|
3588
6181
|
(event) => {
|
|
3589
6182
|
if (event.type === "error") {
|
|
3590
6183
|
setError(new Error(event.message));
|
|
@@ -3600,10 +6193,10 @@ function useAgent(options) {
|
|
|
3600
6193
|
},
|
|
3601
6194
|
[onStateChange]
|
|
3602
6195
|
);
|
|
3603
|
-
const stop =
|
|
6196
|
+
const stop = React2.useCallback(() => {
|
|
3604
6197
|
abortControllerRef.current?.abort();
|
|
3605
6198
|
}, []);
|
|
3606
|
-
const setState =
|
|
6199
|
+
const setState = React2.useCallback(
|
|
3607
6200
|
(partialState) => {
|
|
3608
6201
|
setStateInternal((prev) => {
|
|
3609
6202
|
const newState = { ...prev, ...partialState };
|
|
@@ -3613,7 +6206,7 @@ function useAgent(options) {
|
|
|
3613
6206
|
},
|
|
3614
6207
|
[onStateChange]
|
|
3615
6208
|
);
|
|
3616
|
-
|
|
6209
|
+
React2.useEffect(() => {
|
|
3617
6210
|
return () => {
|
|
3618
6211
|
abortControllerRef.current?.abort();
|
|
3619
6212
|
};
|
|
@@ -3694,9 +6287,9 @@ function formatKnowledgeResultsForAI(results) {
|
|
|
3694
6287
|
// src/react/hooks/useKnowledgeBase.ts
|
|
3695
6288
|
function useKnowledgeBase(config) {
|
|
3696
6289
|
const { registerTool, unregisterTool } = useCopilot();
|
|
3697
|
-
const configRef =
|
|
6290
|
+
const configRef = React2.useRef(config);
|
|
3698
6291
|
configRef.current = config;
|
|
3699
|
-
const handleSearch =
|
|
6292
|
+
const handleSearch = React2.useCallback(
|
|
3700
6293
|
async (params) => {
|
|
3701
6294
|
const query = params.query;
|
|
3702
6295
|
if (!query) {
|
|
@@ -3734,7 +6327,7 @@ function useKnowledgeBase(config) {
|
|
|
3734
6327
|
},
|
|
3735
6328
|
[]
|
|
3736
6329
|
);
|
|
3737
|
-
|
|
6330
|
+
React2.useEffect(() => {
|
|
3738
6331
|
if (config.enabled === false) {
|
|
3739
6332
|
return;
|
|
3740
6333
|
}
|
|
@@ -3781,14 +6374,14 @@ var DEFAULT_CAPABILITIES = {
|
|
|
3781
6374
|
};
|
|
3782
6375
|
function useCapabilities() {
|
|
3783
6376
|
const { config } = useCopilotContext();
|
|
3784
|
-
const [capabilities, setCapabilities] =
|
|
3785
|
-
const [provider, setProvider] =
|
|
3786
|
-
const [model, setModel] =
|
|
3787
|
-
const [supportedModels, setSupportedModels] =
|
|
3788
|
-
const [isLoading, setIsLoading] =
|
|
3789
|
-
const [error, setError] =
|
|
6377
|
+
const [capabilities, setCapabilities] = React2.useState(DEFAULT_CAPABILITIES);
|
|
6378
|
+
const [provider, setProvider] = React2.useState("unknown");
|
|
6379
|
+
const [model, setModel] = React2.useState("unknown");
|
|
6380
|
+
const [supportedModels, setSupportedModels] = React2.useState([]);
|
|
6381
|
+
const [isLoading, setIsLoading] = React2.useState(true);
|
|
6382
|
+
const [error, setError] = React2.useState(null);
|
|
3790
6383
|
const capabilitiesUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/chat\/?$/, "/capabilities") : null;
|
|
3791
|
-
const fetchCapabilities =
|
|
6384
|
+
const fetchCapabilities = React2.useCallback(async () => {
|
|
3792
6385
|
if (!capabilitiesUrl) {
|
|
3793
6386
|
setIsLoading(false);
|
|
3794
6387
|
return;
|
|
@@ -3811,7 +6404,7 @@ function useCapabilities() {
|
|
|
3811
6404
|
setIsLoading(false);
|
|
3812
6405
|
}
|
|
3813
6406
|
}, [capabilitiesUrl]);
|
|
3814
|
-
|
|
6407
|
+
React2.useEffect(() => {
|
|
3815
6408
|
fetchCapabilities();
|
|
3816
6409
|
}, [fetchCapabilities]);
|
|
3817
6410
|
return {
|
|
@@ -3854,7 +6447,7 @@ function useSupportedMediaTypes() {
|
|
|
3854
6447
|
}
|
|
3855
6448
|
function useDevLogger() {
|
|
3856
6449
|
const ctx = useCopilotContext();
|
|
3857
|
-
return
|
|
6450
|
+
return React2.useMemo(() => {
|
|
3858
6451
|
const toolExecutions = (ctx.agentLoop?.toolExecutions || []).map(
|
|
3859
6452
|
(exec) => ({
|
|
3860
6453
|
id: exec.id,
|
|
@@ -4064,7 +6657,7 @@ function createReactThreadManagerState(initialThreads) {
|
|
|
4064
6657
|
}
|
|
4065
6658
|
|
|
4066
6659
|
// src/react/internal/ReactThreadManager.ts
|
|
4067
|
-
var _ReactThreadManager = class _ReactThreadManager extends
|
|
6660
|
+
var _ReactThreadManager = class _ReactThreadManager extends chunkI3SQUNTT_cjs.ThreadManager {
|
|
4068
6661
|
constructor(config = {}, callbacks = {}) {
|
|
4069
6662
|
const reactState = new ReactThreadManagerState();
|
|
4070
6663
|
super({ ...config, state: reactState }, callbacks);
|
|
@@ -4200,7 +6793,7 @@ function getInternalManager(config) {
|
|
|
4200
6793
|
return internalManager;
|
|
4201
6794
|
}
|
|
4202
6795
|
function useThreadManager(config) {
|
|
4203
|
-
const manager =
|
|
6796
|
+
const manager = React2.useMemo(() => {
|
|
4204
6797
|
if (!config) {
|
|
4205
6798
|
return getDefaultManager();
|
|
4206
6799
|
}
|
|
@@ -4223,50 +6816,50 @@ function useThreadManager(config) {
|
|
|
4223
6816
|
config?.autoRestoreLastThread
|
|
4224
6817
|
// Note: callbacks are intentionally not in deps to avoid recreating manager
|
|
4225
6818
|
]);
|
|
4226
|
-
const threads =
|
|
6819
|
+
const threads = React2.useSyncExternalStore(
|
|
4227
6820
|
manager.subscribe,
|
|
4228
6821
|
manager.getThreadsSnapshot,
|
|
4229
6822
|
manager.getThreadsServerSnapshot
|
|
4230
6823
|
// SSR - always empty array
|
|
4231
6824
|
);
|
|
4232
|
-
const currentThread =
|
|
6825
|
+
const currentThread = React2.useSyncExternalStore(
|
|
4233
6826
|
manager.subscribe,
|
|
4234
6827
|
manager.getCurrentThreadSnapshot,
|
|
4235
6828
|
manager.getCurrentThreadServerSnapshot
|
|
4236
6829
|
// SSR - always null
|
|
4237
6830
|
);
|
|
4238
|
-
const currentThreadId =
|
|
6831
|
+
const currentThreadId = React2.useSyncExternalStore(
|
|
4239
6832
|
manager.subscribe,
|
|
4240
6833
|
manager.getCurrentThreadIdSnapshot,
|
|
4241
6834
|
manager.getCurrentThreadIdServerSnapshot
|
|
4242
6835
|
// SSR - always null
|
|
4243
6836
|
);
|
|
4244
|
-
const loadStatus =
|
|
6837
|
+
const loadStatus = React2.useSyncExternalStore(
|
|
4245
6838
|
manager.subscribe,
|
|
4246
6839
|
manager.getLoadStatusSnapshot,
|
|
4247
6840
|
manager.getLoadStatusServerSnapshot
|
|
4248
6841
|
// SSR - always "idle"
|
|
4249
6842
|
);
|
|
4250
|
-
const error =
|
|
6843
|
+
const error = React2.useSyncExternalStore(
|
|
4251
6844
|
manager.subscribe,
|
|
4252
6845
|
manager.getErrorSnapshot,
|
|
4253
6846
|
manager.getErrorServerSnapshot
|
|
4254
6847
|
// SSR - always undefined
|
|
4255
6848
|
);
|
|
4256
|
-
const isLoading =
|
|
6849
|
+
const isLoading = React2.useSyncExternalStore(
|
|
4257
6850
|
manager.subscribe,
|
|
4258
6851
|
manager.getIsLoadingSnapshot,
|
|
4259
6852
|
manager.getIsLoadingServerSnapshot
|
|
4260
6853
|
// SSR - always false
|
|
4261
6854
|
);
|
|
4262
|
-
|
|
6855
|
+
React2.useEffect(() => {
|
|
4263
6856
|
return () => {
|
|
4264
6857
|
if (config?.adapter && manager !== defaultManager && manager !== internalManager) {
|
|
4265
6858
|
manager.dispose();
|
|
4266
6859
|
}
|
|
4267
6860
|
};
|
|
4268
6861
|
}, [manager, config]);
|
|
4269
|
-
|
|
6862
|
+
React2.useEffect(() => {
|
|
4270
6863
|
const handleBeforeUnload = () => {
|
|
4271
6864
|
if (manager.hasPendingChanges) {
|
|
4272
6865
|
manager.saveNow().catch(() => {
|
|
@@ -4280,37 +6873,37 @@ function useThreadManager(config) {
|
|
|
4280
6873
|
};
|
|
4281
6874
|
}
|
|
4282
6875
|
}, [manager]);
|
|
4283
|
-
const createThread =
|
|
6876
|
+
const createThread = React2.useCallback(
|
|
4284
6877
|
(options) => manager.createThread(options),
|
|
4285
6878
|
[manager]
|
|
4286
6879
|
);
|
|
4287
|
-
const switchThread =
|
|
6880
|
+
const switchThread = React2.useCallback(
|
|
4288
6881
|
(id) => manager.switchThread(id),
|
|
4289
6882
|
[manager]
|
|
4290
6883
|
);
|
|
4291
|
-
const updateCurrentThread =
|
|
6884
|
+
const updateCurrentThread = React2.useCallback(
|
|
4292
6885
|
(updates) => manager.updateCurrentThread(updates),
|
|
4293
6886
|
[manager]
|
|
4294
6887
|
);
|
|
4295
|
-
const deleteThread =
|
|
6888
|
+
const deleteThread = React2.useCallback(
|
|
4296
6889
|
(id) => manager.deleteThread(id),
|
|
4297
6890
|
[manager]
|
|
4298
6891
|
);
|
|
4299
|
-
const clearCurrentThread =
|
|
6892
|
+
const clearCurrentThread = React2.useCallback(
|
|
4300
6893
|
() => manager.clearCurrentThread(),
|
|
4301
6894
|
[manager]
|
|
4302
6895
|
);
|
|
4303
|
-
const refreshThreads =
|
|
4304
|
-
const saveNow =
|
|
4305
|
-
const clearAllThreads =
|
|
6896
|
+
const refreshThreads = React2.useCallback(() => manager.loadThreads(), [manager]);
|
|
6897
|
+
const saveNow = React2.useCallback(() => manager.saveNow(), [manager]);
|
|
6898
|
+
const clearAllThreads = React2.useCallback(
|
|
4306
6899
|
() => manager.clearAllThreads(),
|
|
4307
6900
|
[manager]
|
|
4308
6901
|
);
|
|
4309
|
-
const messages =
|
|
6902
|
+
const messages = React2.useMemo(
|
|
4310
6903
|
() => currentThread?.messages ?? [],
|
|
4311
6904
|
[currentThread]
|
|
4312
6905
|
);
|
|
4313
|
-
const setMessages =
|
|
6906
|
+
const setMessages = React2.useCallback(
|
|
4314
6907
|
(newMessages) => updateCurrentThread({ messages: newMessages }),
|
|
4315
6908
|
[updateCurrentThread]
|
|
4316
6909
|
);
|
|
@@ -4347,7 +6940,7 @@ function useMCPUIIntents(config = {}) {
|
|
|
4347
6940
|
onLink,
|
|
4348
6941
|
requireConsent = { tool: false, link: true }
|
|
4349
6942
|
} = config;
|
|
4350
|
-
const handleIntent =
|
|
6943
|
+
const handleIntent = React2.useCallback(
|
|
4351
6944
|
async (intent, context) => {
|
|
4352
6945
|
switch (intent.type) {
|
|
4353
6946
|
case "tool": {
|
|
@@ -4401,7 +6994,7 @@ function useMCPUIIntents(config = {}) {
|
|
|
4401
6994
|
},
|
|
4402
6995
|
[onToolCall, onIntent, onPrompt, onNotify, onLink, requireConsent]
|
|
4403
6996
|
);
|
|
4404
|
-
return
|
|
6997
|
+
return React2.useMemo(
|
|
4405
6998
|
() => ({
|
|
4406
6999
|
handleIntent
|
|
4407
7000
|
}),
|
|
@@ -4431,6 +7024,94 @@ function createToolIntentHandler(callTool) {
|
|
|
4431
7024
|
}
|
|
4432
7025
|
};
|
|
4433
7026
|
}
|
|
7027
|
+
function getLastResponseUsage(messages) {
|
|
7028
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
7029
|
+
const msg = messages[i];
|
|
7030
|
+
if (msg.role === "assistant" && msg.metadata?.usage) {
|
|
7031
|
+
const u = msg.metadata.usage;
|
|
7032
|
+
const prompt = u.prompt_tokens ?? 0;
|
|
7033
|
+
const completion = u.completion_tokens ?? 0;
|
|
7034
|
+
return {
|
|
7035
|
+
prompt_tokens: prompt,
|
|
7036
|
+
completion_tokens: completion,
|
|
7037
|
+
total_tokens: u.total_tokens ?? prompt + completion
|
|
7038
|
+
};
|
|
7039
|
+
}
|
|
7040
|
+
}
|
|
7041
|
+
return null;
|
|
7042
|
+
}
|
|
7043
|
+
function useContextStats() {
|
|
7044
|
+
const { contextChars, contextUsage, registeredTools, messages } = useCopilot();
|
|
7045
|
+
const toolCount = React2.useMemo(() => registeredTools.length, [registeredTools]);
|
|
7046
|
+
const messageCount = React2.useMemo(
|
|
7047
|
+
() => messages.filter((m) => m.role !== "system").length,
|
|
7048
|
+
[messages]
|
|
7049
|
+
);
|
|
7050
|
+
const totalTokens = React2.useMemo(() => {
|
|
7051
|
+
if (contextUsage) return contextUsage.total.tokens;
|
|
7052
|
+
return Math.ceil(contextChars / 3.5);
|
|
7053
|
+
}, [contextUsage, contextChars]);
|
|
7054
|
+
const usagePercent = React2.useMemo(() => {
|
|
7055
|
+
if (contextUsage) return contextUsage.total.percent;
|
|
7056
|
+
return 0;
|
|
7057
|
+
}, [contextUsage]);
|
|
7058
|
+
const lastResponseUsage = React2.useMemo(
|
|
7059
|
+
() => getLastResponseUsage(messages),
|
|
7060
|
+
[messages]
|
|
7061
|
+
);
|
|
7062
|
+
return {
|
|
7063
|
+
contextUsage,
|
|
7064
|
+
totalTokens,
|
|
7065
|
+
usagePercent,
|
|
7066
|
+
contextChars,
|
|
7067
|
+
toolCount,
|
|
7068
|
+
messageCount,
|
|
7069
|
+
lastResponseUsage
|
|
7070
|
+
};
|
|
7071
|
+
}
|
|
7072
|
+
var DEV_CONTENT_WARN_THRESHOLD = 2e3;
|
|
7073
|
+
function useSkill(skill) {
|
|
7074
|
+
const { register, unregister } = useSkillContext();
|
|
7075
|
+
if (process.env.NODE_ENV !== "production" && skill.source.type === "inline" && skill.source.content.length > DEV_CONTENT_WARN_THRESHOLD) {
|
|
7076
|
+
console.warn(
|
|
7077
|
+
`[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.`
|
|
7078
|
+
);
|
|
7079
|
+
}
|
|
7080
|
+
React2.useEffect(() => {
|
|
7081
|
+
if (skill.source.type !== "inline") {
|
|
7082
|
+
console.warn(
|
|
7083
|
+
`[copilot-sdk/skills] useSkill only supports inline skills client-side. Skill "${skill.name}" has source type "${skill.source.type}" and will be skipped.`
|
|
7084
|
+
);
|
|
7085
|
+
return;
|
|
7086
|
+
}
|
|
7087
|
+
const resolved = {
|
|
7088
|
+
...skill,
|
|
7089
|
+
content: skill.source.content
|
|
7090
|
+
};
|
|
7091
|
+
register(resolved);
|
|
7092
|
+
return () => {
|
|
7093
|
+
unregister(skill.name);
|
|
7094
|
+
};
|
|
7095
|
+
}, [
|
|
7096
|
+
skill.name,
|
|
7097
|
+
skill.source.type === "inline" ? skill.source.content : "",
|
|
7098
|
+
skill.strategy,
|
|
7099
|
+
skill.description
|
|
7100
|
+
]);
|
|
7101
|
+
}
|
|
7102
|
+
function useSkillStatus() {
|
|
7103
|
+
const { skills, registry } = useSkillContext();
|
|
7104
|
+
const has = React2.useCallback(
|
|
7105
|
+
(name) => registry.has(name),
|
|
7106
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7107
|
+
[skills]
|
|
7108
|
+
);
|
|
7109
|
+
return {
|
|
7110
|
+
skills,
|
|
7111
|
+
count: skills.length,
|
|
7112
|
+
has
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
4434
7115
|
|
|
4435
7116
|
// src/react/utils/permission-storage.ts
|
|
4436
7117
|
var DEFAULT_KEY_PREFIX = "yourgpt-permissions";
|
|
@@ -4583,6 +7264,34 @@ var ReactChat = class extends AbstractChat {
|
|
|
4583
7264
|
return this.on("error", handler);
|
|
4584
7265
|
}
|
|
4585
7266
|
// ============================================
|
|
7267
|
+
// Branching API — pass-throughs to ReactChatState
|
|
7268
|
+
// ============================================
|
|
7269
|
+
/**
|
|
7270
|
+
* Navigate to a sibling branch (makes it the active path).
|
|
7271
|
+
*/
|
|
7272
|
+
switchBranch(messageId) {
|
|
7273
|
+
this.reactState.switchBranch(messageId);
|
|
7274
|
+
}
|
|
7275
|
+
/**
|
|
7276
|
+
* Get branch navigation info for a message.
|
|
7277
|
+
* Returns null if the message has no siblings.
|
|
7278
|
+
*/
|
|
7279
|
+
getBranchInfo(messageId) {
|
|
7280
|
+
return this.reactState.getBranchInfo(messageId);
|
|
7281
|
+
}
|
|
7282
|
+
/**
|
|
7283
|
+
* Get all messages across all branches (for persistence).
|
|
7284
|
+
*/
|
|
7285
|
+
getAllMessages() {
|
|
7286
|
+
return this.reactState.getAllMessages();
|
|
7287
|
+
}
|
|
7288
|
+
/**
|
|
7289
|
+
* Whether any message has siblings (branching has occurred).
|
|
7290
|
+
*/
|
|
7291
|
+
get hasBranches() {
|
|
7292
|
+
return this.reactState.hasBranches;
|
|
7293
|
+
}
|
|
7294
|
+
// ============================================
|
|
4586
7295
|
// Override dispose to clean up state
|
|
4587
7296
|
// ============================================
|
|
4588
7297
|
dispose() {
|
|
@@ -4601,8 +7310,8 @@ function createReactChat(config) {
|
|
|
4601
7310
|
return new ReactChat(config);
|
|
4602
7311
|
}
|
|
4603
7312
|
function useChat(config) {
|
|
4604
|
-
const chatRef =
|
|
4605
|
-
const [input, setInput] =
|
|
7313
|
+
const chatRef = React2.useRef(null);
|
|
7314
|
+
const [input, setInput] = React2.useState("");
|
|
4606
7315
|
if (chatRef.current !== null && chatRef.current.disposed) {
|
|
4607
7316
|
chatRef.current.revive();
|
|
4608
7317
|
}
|
|
@@ -4624,51 +7333,74 @@ function useChat(config) {
|
|
|
4624
7333
|
}
|
|
4625
7334
|
});
|
|
4626
7335
|
}
|
|
4627
|
-
const messages =
|
|
7336
|
+
const messages = React2.useSyncExternalStore(
|
|
4628
7337
|
chatRef.current.subscribe,
|
|
4629
7338
|
() => chatRef.current.messages,
|
|
4630
7339
|
() => chatRef.current.messages
|
|
4631
7340
|
// Server snapshot
|
|
4632
7341
|
);
|
|
4633
|
-
const status =
|
|
7342
|
+
const status = React2.useSyncExternalStore(
|
|
4634
7343
|
chatRef.current.subscribe,
|
|
4635
7344
|
() => chatRef.current.status,
|
|
4636
7345
|
() => "ready"
|
|
4637
7346
|
// Server snapshot
|
|
4638
7347
|
);
|
|
4639
|
-
const error =
|
|
7348
|
+
const error = React2.useSyncExternalStore(
|
|
4640
7349
|
chatRef.current.subscribe,
|
|
4641
7350
|
() => chatRef.current.error,
|
|
4642
7351
|
() => void 0
|
|
4643
7352
|
// Server snapshot
|
|
4644
7353
|
);
|
|
7354
|
+
const hasBranches = React2.useSyncExternalStore(
|
|
7355
|
+
chatRef.current.subscribe,
|
|
7356
|
+
() => chatRef.current.hasBranches,
|
|
7357
|
+
() => false
|
|
7358
|
+
);
|
|
4645
7359
|
const isLoading = status === "streaming" || status === "submitted";
|
|
4646
|
-
const sendMessage =
|
|
7360
|
+
const sendMessage = React2.useCallback(
|
|
4647
7361
|
async (content, attachments) => {
|
|
4648
7362
|
await chatRef.current?.sendMessage(content, attachments);
|
|
4649
7363
|
setInput("");
|
|
4650
7364
|
},
|
|
4651
7365
|
[]
|
|
4652
7366
|
);
|
|
4653
|
-
const stop =
|
|
7367
|
+
const stop = React2.useCallback(() => {
|
|
4654
7368
|
chatRef.current?.stop();
|
|
4655
7369
|
}, []);
|
|
4656
|
-
const clearMessages =
|
|
7370
|
+
const clearMessages = React2.useCallback(() => {
|
|
4657
7371
|
chatRef.current?.clearMessages();
|
|
4658
7372
|
}, []);
|
|
4659
|
-
const setMessages =
|
|
7373
|
+
const setMessages = React2.useCallback((messages2) => {
|
|
4660
7374
|
chatRef.current?.setMessages(messages2);
|
|
4661
7375
|
}, []);
|
|
4662
|
-
const regenerate =
|
|
7376
|
+
const regenerate = React2.useCallback(async (messageId) => {
|
|
4663
7377
|
await chatRef.current?.regenerate(messageId);
|
|
4664
7378
|
}, []);
|
|
4665
|
-
const continueWithToolResults =
|
|
7379
|
+
const continueWithToolResults = React2.useCallback(
|
|
4666
7380
|
async (toolResults) => {
|
|
4667
7381
|
await chatRef.current?.continueWithToolResults(toolResults);
|
|
4668
7382
|
},
|
|
4669
7383
|
[]
|
|
4670
7384
|
);
|
|
4671
|
-
|
|
7385
|
+
const switchBranch = React2.useCallback((messageId) => {
|
|
7386
|
+
chatRef.current?.switchBranch(messageId);
|
|
7387
|
+
}, []);
|
|
7388
|
+
const getBranchInfo = React2.useCallback(
|
|
7389
|
+
(messageId) => {
|
|
7390
|
+
return chatRef.current?.getBranchInfo(messageId) ?? null;
|
|
7391
|
+
},
|
|
7392
|
+
[]
|
|
7393
|
+
);
|
|
7394
|
+
const editMessage = React2.useCallback(
|
|
7395
|
+
async (messageId, newContent) => {
|
|
7396
|
+
await chatRef.current?.sendMessage(newContent, void 0, {
|
|
7397
|
+
editMessageId: messageId
|
|
7398
|
+
});
|
|
7399
|
+
setInput("");
|
|
7400
|
+
},
|
|
7401
|
+
[]
|
|
7402
|
+
);
|
|
7403
|
+
React2.useEffect(() => {
|
|
4672
7404
|
return () => {
|
|
4673
7405
|
chatRef.current?.dispose();
|
|
4674
7406
|
};
|
|
@@ -4686,17 +7418,30 @@ function useChat(config) {
|
|
|
4686
7418
|
setMessages,
|
|
4687
7419
|
regenerate,
|
|
4688
7420
|
continueWithToolResults,
|
|
4689
|
-
chatRef
|
|
7421
|
+
chatRef,
|
|
7422
|
+
// Branching
|
|
7423
|
+
switchBranch,
|
|
7424
|
+
getBranchInfo,
|
|
7425
|
+
editMessage,
|
|
7426
|
+
hasBranches
|
|
4690
7427
|
};
|
|
4691
7428
|
}
|
|
4692
7429
|
|
|
7430
|
+
// src/react/skill/define-skill.ts
|
|
7431
|
+
function defineSkill(def) {
|
|
7432
|
+
return def;
|
|
7433
|
+
}
|
|
7434
|
+
|
|
4693
7435
|
exports.AbstractAgentLoop = AbstractAgentLoop;
|
|
4694
7436
|
exports.AbstractChat = AbstractChat;
|
|
4695
7437
|
exports.CopilotProvider = CopilotProvider;
|
|
7438
|
+
exports.MessageHistoryContext = MessageHistoryContext;
|
|
7439
|
+
exports.MessageTree = MessageTree;
|
|
4696
7440
|
exports.ReactChat = ReactChat;
|
|
4697
7441
|
exports.ReactChatState = ReactChatState;
|
|
4698
7442
|
exports.ReactThreadManager = ReactThreadManager;
|
|
4699
7443
|
exports.ReactThreadManagerState = ReactThreadManagerState;
|
|
7444
|
+
exports.SkillProvider = SkillProvider;
|
|
4700
7445
|
exports.createMessageIntentHandler = createMessageIntentHandler;
|
|
4701
7446
|
exports.createPermissionStorage = createPermissionStorage;
|
|
4702
7447
|
exports.createReactChat = createReactChat;
|
|
@@ -4705,9 +7450,16 @@ exports.createReactThreadManager = createReactThreadManager;
|
|
|
4705
7450
|
exports.createReactThreadManagerState = createReactThreadManagerState;
|
|
4706
7451
|
exports.createSessionPermissionCache = createSessionPermissionCache;
|
|
4707
7452
|
exports.createToolIntentHandler = createToolIntentHandler;
|
|
7453
|
+
exports.defaultMessageHistoryConfig = defaultMessageHistoryConfig;
|
|
7454
|
+
exports.defineSkill = defineSkill;
|
|
4708
7455
|
exports.formatKnowledgeResultsForAI = formatKnowledgeResultsForAI;
|
|
4709
7456
|
exports.initialAgentLoopState = initialAgentLoopState;
|
|
7457
|
+
exports.isCompactionMarker = isCompactionMarker;
|
|
7458
|
+
exports.keepToolPairsAtomic = keepToolPairsAtomic;
|
|
4710
7459
|
exports.searchKnowledgeBase = searchKnowledgeBase;
|
|
7460
|
+
exports.toDisplayMessage = toDisplayMessage;
|
|
7461
|
+
exports.toLLMMessage = toLLMMessage;
|
|
7462
|
+
exports.toLLMMessages = toLLMMessages;
|
|
4711
7463
|
exports.useAIAction = useAIAction;
|
|
4712
7464
|
exports.useAIActions = useAIActions;
|
|
4713
7465
|
exports.useAIContext = useAIContext;
|
|
@@ -4716,6 +7468,7 @@ exports.useAITools = useAITools;
|
|
|
4716
7468
|
exports.useAgent = useAgent;
|
|
4717
7469
|
exports.useCapabilities = useCapabilities;
|
|
4718
7470
|
exports.useChat = useChat;
|
|
7471
|
+
exports.useContextStats = useContextStats;
|
|
4719
7472
|
exports.useCopilot = useCopilot;
|
|
4720
7473
|
exports.useDevLogger = useDevLogger;
|
|
4721
7474
|
exports.useFeatureSupport = useFeatureSupport;
|
|
@@ -4723,6 +7476,10 @@ exports.useKnowledgeBase = useKnowledgeBase;
|
|
|
4723
7476
|
exports.useMCPClient = useMCPClient;
|
|
4724
7477
|
exports.useMCPTools = useMCPTools;
|
|
4725
7478
|
exports.useMCPUIIntents = useMCPUIIntents;
|
|
7479
|
+
exports.useMessageHistory = useMessageHistory;
|
|
7480
|
+
exports.useMessageHistoryContext = useMessageHistoryContext;
|
|
7481
|
+
exports.useSkill = useSkill;
|
|
7482
|
+
exports.useSkillStatus = useSkillStatus;
|
|
4726
7483
|
exports.useSuggestions = useSuggestions;
|
|
4727
7484
|
exports.useSupportedMediaTypes = useSupportedMediaTypes;
|
|
4728
7485
|
exports.useThreadManager = useThreadManager;
|
|
@@ -4731,5 +7488,5 @@ exports.useToolExecutor = useToolExecutor;
|
|
|
4731
7488
|
exports.useToolWithSchema = useToolWithSchema;
|
|
4732
7489
|
exports.useTools = useTools;
|
|
4733
7490
|
exports.useToolsWithSchema = useToolsWithSchema;
|
|
4734
|
-
//# sourceMappingURL=chunk-
|
|
4735
|
-
//# sourceMappingURL=chunk-
|
|
7491
|
+
//# sourceMappingURL=chunk-76RE7AJE.cjs.map
|
|
7492
|
+
//# sourceMappingURL=chunk-76RE7AJE.cjs.map
|