@yourgpt/copilot-sdk 2.1.4 → 2.1.5-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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-5Q72LZ5H.js} +3107 -357
- package/dist/chunk-5Q72LZ5H.js.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-N6VZ7FOW.cjs → chunk-IXWNDR7H.cjs} +3290 -522
- package/dist/chunk-IXWNDR7H.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 +509 -209
- 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 +457 -158
- 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();
|
|
@@ -691,20 +1647,25 @@ var AbstractChat = class {
|
|
|
691
1647
|
"resolveUnresolvedToolCalls",
|
|
692
1648
|
`Adding ${unresolvedIds.length} missing tool results`
|
|
693
1649
|
);
|
|
1650
|
+
const visibleMsgs = this.state.messages;
|
|
1651
|
+
let errorChainParentId = visibleMsgs.length > 0 ? visibleMsgs[visibleMsgs.length - 1].id : void 0;
|
|
694
1652
|
for (const toolCallId of unresolvedIds) {
|
|
1653
|
+
const toolMessageId = generateMessageId();
|
|
695
1654
|
const toolMessage = {
|
|
696
|
-
id:
|
|
1655
|
+
id: toolMessageId,
|
|
697
1656
|
role: "tool",
|
|
698
1657
|
content: JSON.stringify({
|
|
699
1658
|
success: false,
|
|
700
1659
|
error: "Tool execution was interrupted. Please try again."
|
|
701
1660
|
}),
|
|
702
1661
|
toolCallId,
|
|
703
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
1662
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1663
|
+
...errorChainParentId !== void 0 ? { parentId: errorChainParentId } : {}
|
|
704
1664
|
};
|
|
705
1665
|
this.state.pushMessage(toolMessage);
|
|
1666
|
+
errorChainParentId = toolMessageId;
|
|
706
1667
|
}
|
|
707
|
-
this.callbacks.onMessagesChange?.(this.
|
|
1668
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
708
1669
|
}
|
|
709
1670
|
}
|
|
710
1671
|
/**
|
|
@@ -718,6 +1679,8 @@ var AbstractChat = class {
|
|
|
718
1679
|
this.debug("continueWithToolResults", toolResults);
|
|
719
1680
|
try {
|
|
720
1681
|
const attachmentsToAdd = [];
|
|
1682
|
+
const visibleMessages = this.state.messages;
|
|
1683
|
+
let chainParentId = visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1].id : void 0;
|
|
721
1684
|
for (const { toolCallId, result } of toolResults) {
|
|
722
1685
|
const typedResult = result;
|
|
723
1686
|
let messageContent;
|
|
@@ -734,14 +1697,17 @@ var AbstractChat = class {
|
|
|
734
1697
|
} else {
|
|
735
1698
|
messageContent = typeof result === "string" ? result : JSON.stringify(result);
|
|
736
1699
|
}
|
|
1700
|
+
const toolMessageId = generateMessageId();
|
|
737
1701
|
const toolMessage = {
|
|
738
|
-
id:
|
|
1702
|
+
id: toolMessageId,
|
|
739
1703
|
role: "tool",
|
|
740
1704
|
content: messageContent,
|
|
741
1705
|
toolCallId,
|
|
742
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
1706
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1707
|
+
...chainParentId !== void 0 ? { parentId: chainParentId } : {}
|
|
743
1708
|
};
|
|
744
1709
|
this.state.pushMessage(toolMessage);
|
|
1710
|
+
chainParentId = toolMessageId;
|
|
745
1711
|
}
|
|
746
1712
|
if (attachmentsToAdd.length > 0) {
|
|
747
1713
|
this.debug(
|
|
@@ -753,14 +1719,15 @@ var AbstractChat = class {
|
|
|
753
1719
|
role: "user",
|
|
754
1720
|
content: "Here's my screen:",
|
|
755
1721
|
attachments: attachmentsToAdd,
|
|
756
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
1722
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1723
|
+
...chainParentId !== void 0 ? { parentId: chainParentId } : {}
|
|
757
1724
|
};
|
|
758
1725
|
this.state.pushMessage(userMessage);
|
|
759
1726
|
}
|
|
760
1727
|
this.state.status = "submitted";
|
|
761
|
-
this.callbacks.onMessagesChange?.(this.
|
|
1728
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
762
1729
|
this.callbacks.onStatusChange?.("submitted");
|
|
763
|
-
await Promise
|
|
1730
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
764
1731
|
await this.processRequest();
|
|
765
1732
|
} catch (error) {
|
|
766
1733
|
this.handleError(error);
|
|
@@ -789,30 +1756,58 @@ var AbstractChat = class {
|
|
|
789
1756
|
this.callbacks.onMessagesChange?.(messages);
|
|
790
1757
|
}
|
|
791
1758
|
/**
|
|
792
|
-
* Regenerate last response
|
|
1759
|
+
* Regenerate last response.
|
|
1760
|
+
*
|
|
1761
|
+
* Branch-aware: when the state supports branching (setCurrentLeaf is available),
|
|
1762
|
+
* regenerate creates a new sibling response instead of destroying the original.
|
|
1763
|
+
* The old response is preserved and navigable via switchBranch().
|
|
1764
|
+
*
|
|
1765
|
+
* Legacy fallback: when branching is not available, uses old slice() behavior.
|
|
793
1766
|
*/
|
|
794
1767
|
async regenerate(messageId) {
|
|
1768
|
+
if (this.isBusy) return;
|
|
795
1769
|
const messages = this.state.messages;
|
|
796
|
-
let
|
|
1770
|
+
let targetMessage;
|
|
797
1771
|
if (messageId) {
|
|
798
|
-
|
|
1772
|
+
targetMessage = messages.find((m) => m.id === messageId);
|
|
1773
|
+
if (!targetMessage) {
|
|
1774
|
+
targetMessage = this.state.getAllMessages?.().find((m) => m.id === messageId);
|
|
1775
|
+
}
|
|
799
1776
|
} else {
|
|
800
1777
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
801
1778
|
if (messages[i].role === "assistant") {
|
|
802
|
-
|
|
1779
|
+
targetMessage = messages[i];
|
|
803
1780
|
break;
|
|
804
1781
|
}
|
|
805
1782
|
}
|
|
806
1783
|
}
|
|
1784
|
+
if (!targetMessage) return;
|
|
1785
|
+
if (targetMessage.parentId !== void 0 && this.state.setCurrentLeaf) {
|
|
1786
|
+
this.state.setCurrentLeaf(targetMessage.parentId ?? null);
|
|
1787
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
1788
|
+
this.state.status = "submitted";
|
|
1789
|
+
await Promise.resolve();
|
|
1790
|
+
await this.processRequest();
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
const targetIndex = messages.indexOf(targetMessage);
|
|
807
1794
|
if (targetIndex > 0) {
|
|
808
1795
|
this.state.setMessages(messages.slice(0, targetIndex));
|
|
809
|
-
this.callbacks.onMessagesChange?.(this.
|
|
1796
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
810
1797
|
await this.processRequest();
|
|
811
1798
|
}
|
|
812
1799
|
}
|
|
813
1800
|
// ============================================
|
|
814
1801
|
// Event Handling
|
|
815
1802
|
// ============================================
|
|
1803
|
+
/**
|
|
1804
|
+
* Returns all messages across all branches when the state supports it
|
|
1805
|
+
* (branch-aware), otherwise returns the visible path.
|
|
1806
|
+
* Use this whenever firing onMessagesChange so inactive branches are not lost.
|
|
1807
|
+
*/
|
|
1808
|
+
_allMessages() {
|
|
1809
|
+
return this.state.getAllMessages?.() ?? this.state.messages;
|
|
1810
|
+
}
|
|
816
1811
|
/**
|
|
817
1812
|
* Subscribe to events
|
|
818
1813
|
*/
|
|
@@ -844,10 +1839,32 @@ var AbstractChat = class {
|
|
|
844
1839
|
*/
|
|
845
1840
|
async processRequest() {
|
|
846
1841
|
const request = this.buildRequest();
|
|
1842
|
+
let preCreatedMessageId;
|
|
1843
|
+
if (this.config.streaming !== false) {
|
|
1844
|
+
const visibleMessages = this.state.messages;
|
|
1845
|
+
const currentLeafId = visibleMessages.length > 0 ? visibleMessages[visibleMessages.length - 1].id : void 0;
|
|
1846
|
+
const preMsg = createEmptyAssistantMessage(void 0, {
|
|
1847
|
+
parentId: currentLeafId
|
|
1848
|
+
});
|
|
1849
|
+
this.state.pushMessage(preMsg);
|
|
1850
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
1851
|
+
preCreatedMessageId = preMsg.id;
|
|
1852
|
+
}
|
|
847
1853
|
const response = await this.transport.send(request);
|
|
848
1854
|
if (this.isAsyncIterable(response)) {
|
|
849
|
-
await this.handleStreamResponse(response);
|
|
1855
|
+
await this.handleStreamResponse(response, preCreatedMessageId);
|
|
850
1856
|
} else {
|
|
1857
|
+
if (preCreatedMessageId) {
|
|
1858
|
+
const id = preCreatedMessageId;
|
|
1859
|
+
const visibleMsgs = this.state.messages;
|
|
1860
|
+
const placeholderIdx = visibleMsgs.findIndex((m) => m.id === id);
|
|
1861
|
+
const intendedLeafId = placeholderIdx > 0 ? visibleMsgs[placeholderIdx - 1].id : null;
|
|
1862
|
+
const allMsgs = this.state.getAllMessages?.() ?? this.state.messages;
|
|
1863
|
+
this.state.setMessages(allMsgs.filter((m) => m.id !== id));
|
|
1864
|
+
if (intendedLeafId && this.state.setCurrentLeaf) {
|
|
1865
|
+
this.state.setCurrentLeaf(intendedLeafId);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
851
1868
|
this.handleJsonResponse(response);
|
|
852
1869
|
}
|
|
853
1870
|
}
|
|
@@ -858,19 +1875,50 @@ var AbstractChat = class {
|
|
|
858
1875
|
this.config.tools = tools;
|
|
859
1876
|
}
|
|
860
1877
|
/**
|
|
861
|
-
*
|
|
1878
|
+
* Update prompt/tool optimization behavior.
|
|
862
1879
|
*/
|
|
863
|
-
|
|
864
|
-
this.
|
|
865
|
-
this.
|
|
1880
|
+
setOptimizationConfig(config) {
|
|
1881
|
+
this.config.optimization = config;
|
|
1882
|
+
this.optimizer.updateConfig(config);
|
|
866
1883
|
}
|
|
867
1884
|
/**
|
|
868
|
-
*
|
|
869
|
-
* This allows updating the system prompt after initialization
|
|
1885
|
+
* Select the active tool profile for future requests.
|
|
870
1886
|
*/
|
|
871
|
-
|
|
1887
|
+
setToolProfile(profile) {
|
|
1888
|
+
this.optimizer.setActiveProfile(profile);
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Get the most recent prompt context usage snapshot.
|
|
1892
|
+
*/
|
|
1893
|
+
getContextUsage() {
|
|
1894
|
+
return this.lastContextUsage;
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Set inline skills (called by SkillProvider via React layer)
|
|
1898
|
+
*/
|
|
1899
|
+
setInlineSkills(skills) {
|
|
1900
|
+
this.inlineSkills = skills;
|
|
1901
|
+
this.debug("Inline skills updated", { count: skills.length });
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* Set (or clear) the per-request message transform.
|
|
1905
|
+
* Pass null to disable.
|
|
1906
|
+
*/
|
|
1907
|
+
setRequestMessageTransform(fn) {
|
|
1908
|
+
this.requestMessageTransform = fn;
|
|
1909
|
+
}
|
|
1910
|
+
/**
|
|
1911
|
+
* Set dynamic context (appended to system prompt)
|
|
1912
|
+
*/
|
|
1913
|
+
setContext(context) {
|
|
1914
|
+
this.dynamicContext = context;
|
|
1915
|
+
}
|
|
1916
|
+
/**
|
|
1917
|
+
* Set system prompt dynamically
|
|
1918
|
+
* This allows updating the system prompt after initialization
|
|
1919
|
+
*/
|
|
1920
|
+
setSystemPrompt(prompt) {
|
|
872
1921
|
this.config.systemPrompt = prompt;
|
|
873
|
-
this.debug("System prompt updated", { length: prompt.length });
|
|
874
1922
|
}
|
|
875
1923
|
/**
|
|
876
1924
|
* Set headers configuration
|
|
@@ -881,7 +1929,6 @@ var AbstractChat = class {
|
|
|
881
1929
|
if (this.transport.setHeaders && headers !== void 0) {
|
|
882
1930
|
this.transport.setHeaders(headers);
|
|
883
1931
|
}
|
|
884
|
-
this.debug("Headers config updated");
|
|
885
1932
|
}
|
|
886
1933
|
/**
|
|
887
1934
|
* Set URL configuration
|
|
@@ -892,7 +1939,6 @@ var AbstractChat = class {
|
|
|
892
1939
|
if (this.transport.setUrl) {
|
|
893
1940
|
this.transport.setUrl(url);
|
|
894
1941
|
}
|
|
895
|
-
this.debug("URL config updated");
|
|
896
1942
|
}
|
|
897
1943
|
/**
|
|
898
1944
|
* Set body configuration
|
|
@@ -903,110 +1949,88 @@ var AbstractChat = class {
|
|
|
903
1949
|
if (this.transport.setBody && body !== void 0) {
|
|
904
1950
|
this.transport.setBody(body);
|
|
905
1951
|
}
|
|
906
|
-
this.debug("Body config updated");
|
|
907
1952
|
}
|
|
908
1953
|
/**
|
|
909
1954
|
* Build the request payload
|
|
910
1955
|
*/
|
|
911
1956
|
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 || ""}
|
|
1957
|
+
const systemPrompt = this.dynamicContext ? `${this.config.systemPrompt || ""}
|
|
980
1958
|
|
|
981
1959
|
## Current App Context:
|
|
982
|
-
${this.dynamicContext}`.trim() : this.config.systemPrompt
|
|
1960
|
+
${this.dynamicContext}`.trim() : this.config.systemPrompt;
|
|
1961
|
+
const rawMessages = this.requestMessageTransform ? this.requestMessageTransform(
|
|
1962
|
+
this.state.messages
|
|
1963
|
+
) : this.state.messages;
|
|
1964
|
+
const optimized = this.optimizer.prepare({
|
|
1965
|
+
messages: rawMessages,
|
|
1966
|
+
tools: this.config.tools,
|
|
1967
|
+
systemPrompt
|
|
1968
|
+
});
|
|
1969
|
+
this.lastContextUsage = optimized.contextUsage;
|
|
1970
|
+
this.callbacks.onContextUsageChange?.(optimized.contextUsage);
|
|
1971
|
+
return {
|
|
1972
|
+
messages: optimized.messages,
|
|
1973
|
+
threadId: this.config.threadId,
|
|
1974
|
+
systemPrompt,
|
|
983
1975
|
llm: this.config.llm,
|
|
984
|
-
tools: tools?.length ? tools
|
|
1976
|
+
tools: this.config.tools?.length ? this.config.tools.map((tool2) => ({
|
|
1977
|
+
name: tool2.name,
|
|
1978
|
+
description: tool2.description,
|
|
1979
|
+
category: tool2.category,
|
|
1980
|
+
group: tool2.group,
|
|
1981
|
+
deferLoading: tool2.deferLoading,
|
|
1982
|
+
profiles: tool2.profiles,
|
|
1983
|
+
searchKeywords: tool2.searchKeywords,
|
|
1984
|
+
inputSchema: tool2.inputSchema
|
|
1985
|
+
})) : void 0,
|
|
1986
|
+
__skills: this.inlineSkills.length ? this.inlineSkills : void 0
|
|
985
1987
|
};
|
|
986
1988
|
}
|
|
987
1989
|
/**
|
|
988
1990
|
* Handle streaming response
|
|
989
1991
|
*/
|
|
990
|
-
async handleStreamResponse(stream) {
|
|
1992
|
+
async handleStreamResponse(stream, preCreatedMessageId) {
|
|
991
1993
|
this.state.status = "streaming";
|
|
992
1994
|
this.callbacks.onStatusChange?.("streaming");
|
|
993
|
-
|
|
994
|
-
|
|
1995
|
+
let assistantMessage;
|
|
1996
|
+
if (preCreatedMessageId) {
|
|
1997
|
+
const existing = this.state.messages.find(
|
|
1998
|
+
(m) => m.id === preCreatedMessageId
|
|
1999
|
+
);
|
|
2000
|
+
if (existing) {
|
|
2001
|
+
assistantMessage = existing;
|
|
2002
|
+
} else {
|
|
2003
|
+
assistantMessage = createEmptyAssistantMessage();
|
|
2004
|
+
this.state.pushMessage(assistantMessage);
|
|
2005
|
+
}
|
|
2006
|
+
} else {
|
|
2007
|
+
assistantMessage = createEmptyAssistantMessage();
|
|
2008
|
+
this.state.pushMessage(assistantMessage);
|
|
2009
|
+
}
|
|
995
2010
|
this.streamState = createStreamState(assistantMessage.id);
|
|
996
2011
|
this.callbacks.onMessageStart?.(assistantMessage.id);
|
|
997
|
-
this.
|
|
2012
|
+
this.debugGroup("handleStreamResponse");
|
|
2013
|
+
this.debug("Starting to process stream");
|
|
998
2014
|
let chunkCount = 0;
|
|
999
2015
|
let toolCallsEmitted = false;
|
|
2016
|
+
let pendingClientToolCalls;
|
|
1000
2017
|
for await (const chunk of stream) {
|
|
1001
2018
|
chunkCount++;
|
|
1002
|
-
|
|
2019
|
+
if (chunk.type !== "message:delta") {
|
|
2020
|
+
this.debug("chunk", { count: chunkCount, type: chunk.type });
|
|
2021
|
+
}
|
|
1003
2022
|
if (chunk.type === "error") {
|
|
1004
2023
|
const error = new Error(chunk.message || "Stream error");
|
|
1005
2024
|
this.handleError(error);
|
|
1006
2025
|
return;
|
|
1007
2026
|
}
|
|
1008
2027
|
if (chunk.type === "message:end" && this.streamState?.content) {
|
|
1009
|
-
this.debug("message:end mid-stream
|
|
2028
|
+
this.debug("message:end mid-stream", {
|
|
2029
|
+
messageId: this.streamState.messageId,
|
|
2030
|
+
contentLength: this.streamState.content.length,
|
|
2031
|
+
toolCallsInState: this.streamState.toolCalls?.length ?? 0,
|
|
2032
|
+
chunkCount
|
|
2033
|
+
});
|
|
1010
2034
|
const turnMessage = streamStateToMessage(this.streamState);
|
|
1011
2035
|
const toolCallsHidden = {};
|
|
1012
2036
|
for (const [id, result] of this.streamState.toolResults) {
|
|
@@ -1022,7 +2046,11 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1022
2046
|
}
|
|
1023
2047
|
this.state.updateMessageById(
|
|
1024
2048
|
this.streamState.messageId,
|
|
1025
|
-
() =>
|
|
2049
|
+
(existing) => ({
|
|
2050
|
+
...turnMessage,
|
|
2051
|
+
...existing.parentId !== void 0 ? { parentId: existing.parentId } : {},
|
|
2052
|
+
...existing.childrenIds !== void 0 ? { childrenIds: existing.childrenIds } : {}
|
|
2053
|
+
})
|
|
1026
2054
|
);
|
|
1027
2055
|
this.callbacks.onMessageFinish?.(turnMessage);
|
|
1028
2056
|
this.streamState = null;
|
|
@@ -1037,6 +2065,93 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1037
2065
|
continue;
|
|
1038
2066
|
}
|
|
1039
2067
|
if (!this.streamState) {
|
|
2068
|
+
if (chunk.type === "tool_calls") {
|
|
2069
|
+
pendingClientToolCalls = chunk.toolCalls;
|
|
2070
|
+
this.debug("tool_calls (post-message:end, stored as pending)", {
|
|
2071
|
+
count: pendingClientToolCalls?.length,
|
|
2072
|
+
ids: pendingClientToolCalls?.map((tc) => tc.id)
|
|
2073
|
+
});
|
|
2074
|
+
continue;
|
|
2075
|
+
}
|
|
2076
|
+
if (chunk.type === "done") {
|
|
2077
|
+
this.debug("done (post-message:end)", {
|
|
2078
|
+
hasPendingToolCalls: !!pendingClientToolCalls?.length,
|
|
2079
|
+
pendingCount: pendingClientToolCalls?.length ?? 0,
|
|
2080
|
+
doneMessagesCount: chunk.messages?.length ?? 0,
|
|
2081
|
+
requiresAction: chunk.requiresAction,
|
|
2082
|
+
toolCallsEmitted
|
|
2083
|
+
});
|
|
2084
|
+
if (chunk.messages?.length) {
|
|
2085
|
+
const pendingIds = new Set(
|
|
2086
|
+
(pendingClientToolCalls ?? []).filter((tc) => tc?.id).map((tc) => tc.id)
|
|
2087
|
+
);
|
|
2088
|
+
const messagesToInsert = [];
|
|
2089
|
+
let clientAssistantToolCalls;
|
|
2090
|
+
for (const msg of chunk.messages) {
|
|
2091
|
+
if (msg.role === "assistant" && msg.tool_calls?.length && pendingIds.size > 0 && msg.tool_calls.every(
|
|
2092
|
+
(tc) => pendingIds.has(tc?.id ?? "")
|
|
2093
|
+
)) {
|
|
2094
|
+
clientAssistantToolCalls = msg.tool_calls;
|
|
2095
|
+
continue;
|
|
2096
|
+
}
|
|
2097
|
+
if (msg.role === "assistant" && !msg.tool_calls?.length) continue;
|
|
2098
|
+
messagesToInsert.push({
|
|
2099
|
+
id: generateMessageId(),
|
|
2100
|
+
role: msg.role,
|
|
2101
|
+
content: msg.content ?? "",
|
|
2102
|
+
toolCalls: msg.tool_calls,
|
|
2103
|
+
toolCallId: msg.tool_call_id,
|
|
2104
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
if (clientAssistantToolCalls) {
|
|
2108
|
+
const currentMessages = this.state.messages;
|
|
2109
|
+
for (let i = currentMessages.length - 1; i >= 0; i--) {
|
|
2110
|
+
if (currentMessages[i].role === "assistant") {
|
|
2111
|
+
this.state.updateMessageById(
|
|
2112
|
+
currentMessages[i].id,
|
|
2113
|
+
(m) => ({
|
|
2114
|
+
...m,
|
|
2115
|
+
toolCalls: clientAssistantToolCalls
|
|
2116
|
+
})
|
|
2117
|
+
);
|
|
2118
|
+
break;
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
if (messagesToInsert.length > 0) {
|
|
2123
|
+
const currentMessages = this.state.messages;
|
|
2124
|
+
let insertIdx = currentMessages.length;
|
|
2125
|
+
for (let i = currentMessages.length - 1; i >= 0; i--) {
|
|
2126
|
+
if (currentMessages[i].role === "assistant") {
|
|
2127
|
+
insertIdx = i;
|
|
2128
|
+
break;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
this.state.setMessages([
|
|
2132
|
+
...currentMessages.slice(0, insertIdx),
|
|
2133
|
+
...messagesToInsert,
|
|
2134
|
+
...currentMessages.slice(insertIdx)
|
|
2135
|
+
]);
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
if (!toolCallsEmitted && pendingClientToolCalls?.length) {
|
|
2139
|
+
toolCallsEmitted = true;
|
|
2140
|
+
this.debug("emit toolCalls (post-message:end path)", {
|
|
2141
|
+
count: pendingClientToolCalls.length,
|
|
2142
|
+
names: pendingClientToolCalls.map(
|
|
2143
|
+
(tc) => tc.function?.name ?? tc.name
|
|
2144
|
+
)
|
|
2145
|
+
});
|
|
2146
|
+
this.emit("toolCalls", { toolCalls: pendingClientToolCalls });
|
|
2147
|
+
} else {
|
|
2148
|
+
this.debug("skip emit toolCalls (post-message:end path)", {
|
|
2149
|
+
toolCallsEmitted,
|
|
2150
|
+
hasPending: !!pendingClientToolCalls?.length
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
continue;
|
|
2154
|
+
}
|
|
1040
2155
|
this.debug("warning", "streamState is null, skipping chunk");
|
|
1041
2156
|
continue;
|
|
1042
2157
|
}
|
|
@@ -1072,22 +2187,38 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1072
2187
|
const updatedMessage = streamStateToMessage(this.streamState);
|
|
1073
2188
|
this.state.updateMessageById(
|
|
1074
2189
|
this.streamState.messageId,
|
|
1075
|
-
|
|
2190
|
+
// Preserve parentId/childrenIds from the existing placeholder so the
|
|
2191
|
+
// branch tree structure (activeChildMap) is not corrupted when
|
|
2192
|
+
// setCurrentLeaf() walks up the chain later.
|
|
2193
|
+
(existing) => ({
|
|
2194
|
+
...updatedMessage,
|
|
2195
|
+
...existing.parentId !== void 0 ? { parentId: existing.parentId } : {},
|
|
2196
|
+
...existing.childrenIds !== void 0 ? { childrenIds: existing.childrenIds } : {}
|
|
2197
|
+
})
|
|
1076
2198
|
);
|
|
1077
2199
|
if (chunk.type === "message:delta") {
|
|
1078
2200
|
this.callbacks.onMessageDelta?.(assistantMessage.id, chunk.content);
|
|
1079
2201
|
}
|
|
1080
|
-
if (requiresToolExecution(chunk) && !toolCallsEmitted) {
|
|
1081
|
-
toolCallsEmitted = true;
|
|
1082
|
-
this.debug("toolCalls", { toolCalls: updatedMessage.toolCalls });
|
|
1083
|
-
this.emit("toolCalls", { toolCalls: updatedMessage.toolCalls });
|
|
1084
|
-
}
|
|
1085
2202
|
if (isStreamDone(chunk)) {
|
|
1086
|
-
this.debug("streamDone", {
|
|
2203
|
+
this.debug("streamDone", {
|
|
2204
|
+
chunkType: chunk.type,
|
|
2205
|
+
requiresAction: chunk.requiresAction,
|
|
2206
|
+
doneMessagesCount: chunk.messages?.length ?? 0,
|
|
2207
|
+
streamToolCallsCount: this.streamState?.toolCalls?.length ?? 0,
|
|
2208
|
+
toolCallsEmitted,
|
|
2209
|
+
chunkCount
|
|
2210
|
+
});
|
|
1087
2211
|
if (chunk.type === "done" && chunk.messages?.length) {
|
|
1088
2212
|
this.debug("processDoneMessages", {
|
|
1089
|
-
count: chunk.messages.length
|
|
2213
|
+
count: chunk.messages.length,
|
|
2214
|
+
roles: chunk.messages.map(
|
|
2215
|
+
(m) => `${m.role}${m.tool_calls?.length ? `[${m.tool_calls.length}tc]` : ""}`
|
|
2216
|
+
)
|
|
1090
2217
|
});
|
|
2218
|
+
const currentStreamToolCallIds = new Set(
|
|
2219
|
+
this.streamState?.toolCalls?.map((toolCall) => toolCall.id) ?? []
|
|
2220
|
+
);
|
|
2221
|
+
const messagesToInsert = [];
|
|
1091
2222
|
const toolCallsHidden = {};
|
|
1092
2223
|
if (this.streamState?.toolResults) {
|
|
1093
2224
|
for (const [id, result] of this.streamState.toolResults) {
|
|
@@ -1097,7 +2228,12 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1097
2228
|
}
|
|
1098
2229
|
}
|
|
1099
2230
|
for (const msg of chunk.messages) {
|
|
1100
|
-
if (msg.role === "assistant") {
|
|
2231
|
+
if (msg.role === "assistant" && !msg.tool_calls?.length) {
|
|
2232
|
+
continue;
|
|
2233
|
+
}
|
|
2234
|
+
if (msg.role === "assistant" && msg.tool_calls?.length && msg.tool_calls.every(
|
|
2235
|
+
(toolCall) => currentStreamToolCallIds.has(toolCall.id)
|
|
2236
|
+
)) {
|
|
1101
2237
|
continue;
|
|
1102
2238
|
}
|
|
1103
2239
|
let metadata;
|
|
@@ -1113,7 +2249,60 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1113
2249
|
createdAt: /* @__PURE__ */ new Date(),
|
|
1114
2250
|
metadata
|
|
1115
2251
|
};
|
|
1116
|
-
|
|
2252
|
+
messagesToInsert.push(message);
|
|
2253
|
+
}
|
|
2254
|
+
if (messagesToInsert.length > 0) {
|
|
2255
|
+
const currentMessages = this.state.messages;
|
|
2256
|
+
const currentStreamIndex = this.streamState ? currentMessages.findIndex(
|
|
2257
|
+
(message) => message.id === this.streamState.messageId
|
|
2258
|
+
) : -1;
|
|
2259
|
+
if (currentStreamIndex === -1) {
|
|
2260
|
+
this.state.setMessages([...currentMessages, ...messagesToInsert]);
|
|
2261
|
+
} else {
|
|
2262
|
+
this.state.setMessages([
|
|
2263
|
+
...currentMessages.slice(0, currentStreamIndex),
|
|
2264
|
+
...messagesToInsert,
|
|
2265
|
+
...currentMessages.slice(currentStreamIndex)
|
|
2266
|
+
]);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
this.debug("requiresAction check", {
|
|
2270
|
+
requiresAction: chunk.requiresAction,
|
|
2271
|
+
toolCallsEmitted,
|
|
2272
|
+
updatedMessageToolCallsCount: updatedMessage.toolCalls?.length ?? 0,
|
|
2273
|
+
messagesToInsertCount: messagesToInsert.length
|
|
2274
|
+
});
|
|
2275
|
+
if (chunk.requiresAction && !toolCallsEmitted) {
|
|
2276
|
+
let clientToolCalls = updatedMessage.toolCalls;
|
|
2277
|
+
if (!clientToolCalls?.length && messagesToInsert.length > 0) {
|
|
2278
|
+
for (let i = messagesToInsert.length - 1; i >= 0; i--) {
|
|
2279
|
+
const m = messagesToInsert[i];
|
|
2280
|
+
if (m.role === "assistant" && m.toolCalls?.length) {
|
|
2281
|
+
clientToolCalls = m.toolCalls;
|
|
2282
|
+
this.debug("clientToolCalls from messagesToInsert", {
|
|
2283
|
+
index: i,
|
|
2284
|
+
count: clientToolCalls?.length
|
|
2285
|
+
});
|
|
2286
|
+
break;
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
if (clientToolCalls?.length) {
|
|
2291
|
+
toolCallsEmitted = true;
|
|
2292
|
+
this.debug("emit toolCalls (normal done path)", {
|
|
2293
|
+
count: clientToolCalls.length,
|
|
2294
|
+
names: clientToolCalls.map((tc) => tc.function?.name ?? tc.name)
|
|
2295
|
+
});
|
|
2296
|
+
this.emit("toolCalls", { toolCalls: clientToolCalls });
|
|
2297
|
+
} else {
|
|
2298
|
+
this.debug("requiresAction=true but no clientToolCalls found", {
|
|
2299
|
+
updatedMessageToolCalls: updatedMessage.toolCalls,
|
|
2300
|
+
messagesToInsert: messagesToInsert.map((m) => ({
|
|
2301
|
+
role: m.role,
|
|
2302
|
+
hasToolCalls: !!m.toolCalls?.length
|
|
2303
|
+
}))
|
|
2304
|
+
});
|
|
2305
|
+
}
|
|
1117
2306
|
}
|
|
1118
2307
|
}
|
|
1119
2308
|
break;
|
|
@@ -1138,15 +2327,22 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1138
2327
|
toolCallsHidden
|
|
1139
2328
|
};
|
|
1140
2329
|
}
|
|
1141
|
-
this.state.updateMessageById(
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
2330
|
+
this.state.updateMessageById(this.streamState.messageId, (existing) => ({
|
|
2331
|
+
...finalMessage,
|
|
2332
|
+
...existing.parentId !== void 0 ? { parentId: existing.parentId } : {},
|
|
2333
|
+
...existing.childrenIds !== void 0 ? { childrenIds: existing.childrenIds } : {}
|
|
2334
|
+
}));
|
|
1145
2335
|
if (!finalMessage.content && (!finalMessage.toolCalls || finalMessage.toolCalls.length === 0)) {
|
|
1146
2336
|
this.debug("warning", "Empty response - no content and no tool calls");
|
|
1147
2337
|
}
|
|
1148
2338
|
}
|
|
1149
|
-
this.callbacks.onMessagesChange?.(this.
|
|
2339
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
2340
|
+
this.debugGroupEnd();
|
|
2341
|
+
this.debug("stream end", {
|
|
2342
|
+
toolCallsEmitted,
|
|
2343
|
+
totalChunks: chunkCount,
|
|
2344
|
+
messagesInState: this.state.messages.length
|
|
2345
|
+
});
|
|
1150
2346
|
if (!toolCallsEmitted) {
|
|
1151
2347
|
this.state.status = "ready";
|
|
1152
2348
|
this.callbacks.onStatusChange?.("ready");
|
|
@@ -1167,6 +2363,7 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1167
2363
|
}
|
|
1168
2364
|
}
|
|
1169
2365
|
}
|
|
2366
|
+
let currentParentId = this.state.messages.length > 0 ? this.state.messages[this.state.messages.length - 1].id : void 0;
|
|
1170
2367
|
for (const msg of response.messages ?? []) {
|
|
1171
2368
|
let metadata;
|
|
1172
2369
|
if (msg.role === "assistant" && msg.tool_calls && toolCallHiddenMap.size > 0) {
|
|
@@ -1189,11 +2386,15 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1189
2386
|
// CRITICAL: Preserve toolCallId for tool messages (fixes Anthropic API errors)
|
|
1190
2387
|
toolCallId: msg.tool_call_id,
|
|
1191
2388
|
createdAt: /* @__PURE__ */ new Date(),
|
|
1192
|
-
metadata
|
|
2389
|
+
metadata,
|
|
2390
|
+
// Preserve branch tree structure: each message is a child of the
|
|
2391
|
+
// current leaf so the tree is not corrupted for non-streaming mode.
|
|
2392
|
+
...currentParentId !== void 0 ? { parentId: currentParentId } : {}
|
|
1193
2393
|
};
|
|
1194
2394
|
this.state.pushMessage(message);
|
|
2395
|
+
currentParentId = message.id;
|
|
1195
2396
|
}
|
|
1196
|
-
this.callbacks.onMessagesChange?.(this.
|
|
2397
|
+
this.callbacks.onMessagesChange?.(this._allMessages());
|
|
1197
2398
|
const hasToolCalls = response.requiresAction && this.state.messages.length > 0 && this.state.messages[this.state.messages.length - 1]?.toolCalls?.length;
|
|
1198
2399
|
if (hasToolCalls) {
|
|
1199
2400
|
const lastMessage = this.state.messages[this.state.messages.length - 1];
|
|
@@ -1216,14 +2417,25 @@ ${this.dynamicContext}`.trim() : this.config.systemPrompt,
|
|
|
1216
2417
|
this.callbacks.onStatusChange?.("error");
|
|
1217
2418
|
this.emit("error", { error });
|
|
1218
2419
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
2420
|
+
get log() {
|
|
2421
|
+
if (!this._log) {
|
|
2422
|
+
this._log = chunkI3SQUNTT_cjs.createLogger("streaming", () => this.config.debug ?? false);
|
|
2423
|
+
}
|
|
2424
|
+
return this._log;
|
|
2425
|
+
}
|
|
1222
2426
|
debug(action, data) {
|
|
1223
|
-
|
|
1224
|
-
|
|
2427
|
+
this.log(action, data);
|
|
2428
|
+
}
|
|
2429
|
+
debugGroup(label, collapsed = true) {
|
|
2430
|
+
if (collapsed) {
|
|
2431
|
+
this.log.groupCollapsed(label);
|
|
2432
|
+
} else {
|
|
2433
|
+
this.log.group(label);
|
|
1225
2434
|
}
|
|
1226
2435
|
}
|
|
2436
|
+
debugGroupEnd() {
|
|
2437
|
+
this.log.groupEnd();
|
|
2438
|
+
}
|
|
1227
2439
|
/**
|
|
1228
2440
|
* Type guard for async iterable
|
|
1229
2441
|
*/
|
|
@@ -1434,19 +2646,18 @@ var AbstractAgentLoop = class {
|
|
|
1434
2646
|
this._isCancelled = false;
|
|
1435
2647
|
this._isProcessing = true;
|
|
1436
2648
|
this.setIteration(this._iteration + 1);
|
|
1437
|
-
const results =
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
2649
|
+
const results = await Promise.all(
|
|
2650
|
+
toolCalls.map((toolCall) => {
|
|
2651
|
+
if (this._isCancelled || this.abortController.signal.aborted) {
|
|
2652
|
+
return Promise.resolve({
|
|
2653
|
+
toolCallId: toolCall.id,
|
|
2654
|
+
success: false,
|
|
2655
|
+
error: "Tool execution cancelled"
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
return this.executeSingleTool(toolCall);
|
|
2659
|
+
})
|
|
2660
|
+
);
|
|
1450
2661
|
this._isProcessing = false;
|
|
1451
2662
|
return results;
|
|
1452
2663
|
}
|
|
@@ -1764,6 +2975,7 @@ var ChatWithTools = class {
|
|
|
1764
2975
|
streaming: config.streaming,
|
|
1765
2976
|
headers: config.headers,
|
|
1766
2977
|
body: config.body,
|
|
2978
|
+
optimization: config.optimization,
|
|
1767
2979
|
threadId: config.threadId,
|
|
1768
2980
|
debug: config.debug,
|
|
1769
2981
|
initialMessages: config.initialMessages,
|
|
@@ -1778,6 +2990,7 @@ var ChatWithTools = class {
|
|
|
1778
2990
|
onMessageFinish: callbacks.onMessageFinish,
|
|
1779
2991
|
onToolCalls: callbacks.onToolCalls,
|
|
1780
2992
|
onFinish: callbacks.onFinish,
|
|
2993
|
+
onContextUsageChange: callbacks.onContextUsageChange,
|
|
1781
2994
|
// Server-side tool callbacks - track in agentLoop for UI display
|
|
1782
2995
|
// IMPORTANT: Only track tools that are NOT registered client-side
|
|
1783
2996
|
// Client-side tools are tracked via executeToolCalls() path
|
|
@@ -1945,14 +3158,17 @@ var ChatWithTools = class {
|
|
|
1945
3158
|
/**
|
|
1946
3159
|
* Send a message
|
|
1947
3160
|
* Returns false if a request is already in progress
|
|
3161
|
+
*
|
|
3162
|
+
* @param options.editMessageId - Edit flow: new message branches from the
|
|
3163
|
+
* same parent as this message ID
|
|
1948
3164
|
*/
|
|
1949
|
-
async sendMessage(content, attachments) {
|
|
3165
|
+
async sendMessage(content, attachments, options) {
|
|
1950
3166
|
if (this.isLoading) {
|
|
1951
3167
|
this.debug("sendMessage blocked - request already in progress");
|
|
1952
3168
|
return false;
|
|
1953
3169
|
}
|
|
1954
3170
|
this.agentLoop.resetIterations();
|
|
1955
|
-
return await this.chat.sendMessage(content, attachments);
|
|
3171
|
+
return await this.chat.sendMessage(content, attachments, options);
|
|
1956
3172
|
}
|
|
1957
3173
|
/**
|
|
1958
3174
|
* Stop generation and cancel any running tools
|
|
@@ -1987,6 +3203,25 @@ var ChatWithTools = class {
|
|
|
1987
3203
|
setTools(tools) {
|
|
1988
3204
|
this.chat.setTools(tools);
|
|
1989
3205
|
}
|
|
3206
|
+
/**
|
|
3207
|
+
* Update prompt/tool optimization controls.
|
|
3208
|
+
*/
|
|
3209
|
+
setOptimizationConfig(config) {
|
|
3210
|
+
this.config.optimization = config;
|
|
3211
|
+
this.chat.setOptimizationConfig(config);
|
|
3212
|
+
}
|
|
3213
|
+
/**
|
|
3214
|
+
* Set the active tool profile used for request-time tool selection.
|
|
3215
|
+
*/
|
|
3216
|
+
setToolProfile(profile) {
|
|
3217
|
+
this.chat.setToolProfile(profile);
|
|
3218
|
+
}
|
|
3219
|
+
/**
|
|
3220
|
+
* Get the most recent prompt context usage snapshot.
|
|
3221
|
+
*/
|
|
3222
|
+
getContextUsage() {
|
|
3223
|
+
return this.chat.getContextUsage();
|
|
3224
|
+
}
|
|
1990
3225
|
/**
|
|
1991
3226
|
* Set dynamic context (from useAIContext hook)
|
|
1992
3227
|
*/
|
|
@@ -2020,6 +3255,15 @@ var ChatWithTools = class {
|
|
|
2020
3255
|
setBody(body) {
|
|
2021
3256
|
this.chat.setBody(body);
|
|
2022
3257
|
}
|
|
3258
|
+
setRequestMessageTransform(fn) {
|
|
3259
|
+
this.chat.setRequestMessageTransform(fn);
|
|
3260
|
+
}
|
|
3261
|
+
/**
|
|
3262
|
+
* Set inline skills (forwarded to underlying chat instance)
|
|
3263
|
+
*/
|
|
3264
|
+
setInlineSkills(skills) {
|
|
3265
|
+
this.chat.setInlineSkills(skills);
|
|
3266
|
+
}
|
|
2023
3267
|
// ============================================
|
|
2024
3268
|
// Tool Registration
|
|
2025
3269
|
// ============================================
|
|
@@ -2096,16 +3340,307 @@ var ChatWithTools = class {
|
|
|
2096
3340
|
// Private
|
|
2097
3341
|
// ============================================
|
|
2098
3342
|
debug(message, ...args) {
|
|
2099
|
-
|
|
2100
|
-
|
|
3343
|
+
chunkI3SQUNTT_cjs.createLogger("tools", () => this.config.debug ?? false)(
|
|
3344
|
+
message,
|
|
3345
|
+
args.length === 1 ? args[0] : args.length > 1 ? args : void 0
|
|
3346
|
+
);
|
|
3347
|
+
}
|
|
3348
|
+
};
|
|
3349
|
+
|
|
3350
|
+
// src/chat/branching/MessageTree.ts
|
|
3351
|
+
var _MessageTree = class _MessageTree {
|
|
3352
|
+
constructor(messages) {
|
|
3353
|
+
/** All messages by ID */
|
|
3354
|
+
this.nodeMap = /* @__PURE__ */ new Map();
|
|
3355
|
+
/** parentKey → ordered list of child IDs (insertion order = oldest-first) */
|
|
3356
|
+
this.childrenOf = /* @__PURE__ */ new Map();
|
|
3357
|
+
/** parentKey → currently-active child ID */
|
|
3358
|
+
this.activeChildMap = /* @__PURE__ */ new Map();
|
|
3359
|
+
/** Current leaf message ID (tip of the active path) */
|
|
3360
|
+
this._currentLeafId = null;
|
|
3361
|
+
/** Cached visible messages — invalidated on every mutation */
|
|
3362
|
+
this._visibleCache = null;
|
|
3363
|
+
if (messages?.length) {
|
|
3364
|
+
this._buildFromMessages(messages);
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
// ============================================
|
|
3368
|
+
// Static Migration Helpers
|
|
3369
|
+
// ============================================
|
|
3370
|
+
/**
|
|
3371
|
+
* Convert a legacy flat array (no parentId) to a tree-linked array.
|
|
3372
|
+
*
|
|
3373
|
+
* Rules:
|
|
3374
|
+
* - Tool messages get parentId = the owning assistant message's id
|
|
3375
|
+
* (matched via toolCallId → toolCall.id).
|
|
3376
|
+
* - All other messages get parentId of the previous non-tool message
|
|
3377
|
+
* (or null for the first message).
|
|
3378
|
+
*
|
|
3379
|
+
* Returns a new array with parentId/childrenIds filled in.
|
|
3380
|
+
* Does NOT mutate the original messages.
|
|
3381
|
+
*/
|
|
3382
|
+
static fromFlatArray(messages) {
|
|
3383
|
+
if (messages.length === 0) return messages;
|
|
3384
|
+
const alreadyLinked = messages.some((m) => m.parentId !== void 0);
|
|
3385
|
+
if (alreadyLinked) return messages;
|
|
3386
|
+
const result = [];
|
|
3387
|
+
let prevNonToolId = null;
|
|
3388
|
+
const assistantById = /* @__PURE__ */ new Map();
|
|
3389
|
+
for (const msg of messages) {
|
|
3390
|
+
if (msg.role === "assistant") {
|
|
3391
|
+
assistantById.set(msg.id, msg);
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
for (const msg of messages) {
|
|
3395
|
+
if (msg.role === "tool" && msg.toolCallId) {
|
|
3396
|
+
let ownerAssistantId = null;
|
|
3397
|
+
for (const [, assistant] of assistantById) {
|
|
3398
|
+
if (assistant.toolCalls?.some((tc) => tc.id === msg.toolCallId)) {
|
|
3399
|
+
ownerAssistantId = assistant.id;
|
|
3400
|
+
break;
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
result.push({
|
|
3404
|
+
...msg,
|
|
3405
|
+
parentId: ownerAssistantId ?? prevNonToolId,
|
|
3406
|
+
childrenIds: []
|
|
3407
|
+
});
|
|
3408
|
+
} else {
|
|
3409
|
+
result.push({
|
|
3410
|
+
...msg,
|
|
3411
|
+
parentId: prevNonToolId,
|
|
3412
|
+
childrenIds: []
|
|
3413
|
+
});
|
|
3414
|
+
prevNonToolId = msg.id;
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
const childrenMap = /* @__PURE__ */ new Map();
|
|
3418
|
+
for (const msg of result) {
|
|
3419
|
+
const parentKey = msg.parentId == null ? _MessageTree.ROOT_KEY : msg.parentId;
|
|
3420
|
+
if (!childrenMap.has(parentKey)) {
|
|
3421
|
+
childrenMap.set(parentKey, []);
|
|
3422
|
+
}
|
|
3423
|
+
childrenMap.get(parentKey).push(msg.id);
|
|
3424
|
+
}
|
|
3425
|
+
return result.map((msg) => ({
|
|
3426
|
+
...msg,
|
|
3427
|
+
childrenIds: childrenMap.get(msg.id) ?? []
|
|
3428
|
+
}));
|
|
3429
|
+
}
|
|
3430
|
+
// ============================================
|
|
3431
|
+
// Core Queries
|
|
3432
|
+
// ============================================
|
|
3433
|
+
/**
|
|
3434
|
+
* Returns the visible path (root → current leaf) — what the UI renders
|
|
3435
|
+
* and what gets sent to the API.
|
|
3436
|
+
*
|
|
3437
|
+
* Backward-compat: if NO message has parentId set (all undefined),
|
|
3438
|
+
* falls back to insertion order (legacy linear mode).
|
|
3439
|
+
*/
|
|
3440
|
+
getVisibleMessages() {
|
|
3441
|
+
if (this._visibleCache !== null) return this._visibleCache;
|
|
3442
|
+
if (this.nodeMap.size === 0) {
|
|
3443
|
+
this._visibleCache = [];
|
|
3444
|
+
return this._visibleCache;
|
|
3445
|
+
}
|
|
3446
|
+
const hasTreeStructure = Array.from(this.nodeMap.values()).some(
|
|
3447
|
+
(m) => m.parentId !== void 0
|
|
3448
|
+
);
|
|
3449
|
+
this._visibleCache = hasTreeStructure ? this._getActivePath().map((id) => this.nodeMap.get(id)) : Array.from(this.nodeMap.values());
|
|
3450
|
+
return this._visibleCache;
|
|
3451
|
+
}
|
|
3452
|
+
_invalidateCache() {
|
|
3453
|
+
this._visibleCache = null;
|
|
3454
|
+
}
|
|
3455
|
+
/**
|
|
3456
|
+
* Returns ALL messages across every branch (for persistence / ThreadManager).
|
|
3457
|
+
*/
|
|
3458
|
+
getAllMessages() {
|
|
3459
|
+
return Array.from(this.nodeMap.values());
|
|
3460
|
+
}
|
|
3461
|
+
/**
|
|
3462
|
+
* Branch navigation info for the UI navigator.
|
|
3463
|
+
* Returns null if the message has no siblings (only child).
|
|
3464
|
+
*/
|
|
3465
|
+
getBranchInfo(messageId) {
|
|
3466
|
+
const msg = this.nodeMap.get(messageId);
|
|
3467
|
+
if (!msg) return null;
|
|
3468
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3469
|
+
const siblings = this.childrenOf.get(parentKey) ?? [];
|
|
3470
|
+
if (siblings.length <= 1) return null;
|
|
3471
|
+
const siblingIndex = siblings.indexOf(messageId);
|
|
3472
|
+
return {
|
|
3473
|
+
siblingIndex,
|
|
3474
|
+
totalSiblings: siblings.length,
|
|
3475
|
+
siblingIds: [...siblings],
|
|
3476
|
+
hasPrevious: siblingIndex > 0,
|
|
3477
|
+
hasNext: siblingIndex < siblings.length - 1
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
get currentLeafId() {
|
|
3481
|
+
return this._currentLeafId;
|
|
3482
|
+
}
|
|
3483
|
+
get hasBranches() {
|
|
3484
|
+
for (const children of this.childrenOf.values()) {
|
|
3485
|
+
if (children.length > 1) return true;
|
|
3486
|
+
}
|
|
3487
|
+
return false;
|
|
3488
|
+
}
|
|
3489
|
+
// ============================================
|
|
3490
|
+
// Mutations
|
|
3491
|
+
// ============================================
|
|
3492
|
+
/**
|
|
3493
|
+
* Insert a new message.
|
|
3494
|
+
* - Updates childrenOf and nodeMap.
|
|
3495
|
+
* - New branch becomes active (activeChildMap updated).
|
|
3496
|
+
* - Updates current leaf.
|
|
3497
|
+
*/
|
|
3498
|
+
addMessage(message) {
|
|
3499
|
+
this.nodeMap.set(message.id, message);
|
|
3500
|
+
const parentKey = this._parentKey(message.parentId);
|
|
3501
|
+
if (!this.childrenOf.has(parentKey)) {
|
|
3502
|
+
this.childrenOf.set(parentKey, []);
|
|
3503
|
+
}
|
|
3504
|
+
const siblings = this.childrenOf.get(parentKey);
|
|
3505
|
+
if (!siblings.includes(message.id)) {
|
|
3506
|
+
siblings.push(message.id);
|
|
3507
|
+
}
|
|
3508
|
+
this.activeChildMap.set(parentKey, message.id);
|
|
3509
|
+
this._currentLeafId = this._walkToLeaf(message.id);
|
|
3510
|
+
this._invalidateCache();
|
|
3511
|
+
return message;
|
|
3512
|
+
}
|
|
3513
|
+
/**
|
|
3514
|
+
* Navigate: make messageId the active child at its parent fork,
|
|
3515
|
+
* then walk to its leaf and update currentLeafId.
|
|
3516
|
+
*/
|
|
3517
|
+
switchBranch(messageId) {
|
|
3518
|
+
const msg = this.nodeMap.get(messageId);
|
|
3519
|
+
if (!msg) return;
|
|
3520
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3521
|
+
this.activeChildMap.set(parentKey, messageId);
|
|
3522
|
+
this._currentLeafId = this._walkToLeaf(messageId);
|
|
3523
|
+
this._invalidateCache();
|
|
3524
|
+
}
|
|
3525
|
+
/**
|
|
3526
|
+
* Update message content in-place (streaming updates).
|
|
3527
|
+
* No tree structure change.
|
|
3528
|
+
*/
|
|
3529
|
+
updateMessage(id, updater) {
|
|
3530
|
+
const existing = this.nodeMap.get(id);
|
|
3531
|
+
if (!existing) return false;
|
|
3532
|
+
this.nodeMap.set(id, updater(existing));
|
|
3533
|
+
this._invalidateCache();
|
|
3534
|
+
return true;
|
|
3535
|
+
}
|
|
3536
|
+
/**
|
|
3537
|
+
* Set current leaf explicitly.
|
|
3538
|
+
* Used by regenerate() to rewind the active path before pushing a new message.
|
|
3539
|
+
*/
|
|
3540
|
+
setCurrentLeaf(leafId) {
|
|
3541
|
+
this._currentLeafId = leafId;
|
|
3542
|
+
if (leafId === null) return;
|
|
3543
|
+
const msg = this.nodeMap.get(leafId);
|
|
3544
|
+
if (!msg) return;
|
|
3545
|
+
let current = msg;
|
|
3546
|
+
while (current) {
|
|
3547
|
+
const parentKey = this._parentKey(current.parentId);
|
|
3548
|
+
this.activeChildMap.set(parentKey, current.id);
|
|
3549
|
+
if (current.parentId == null || current.parentId === void 0) break;
|
|
3550
|
+
current = this.nodeMap.get(current.parentId);
|
|
3551
|
+
}
|
|
3552
|
+
this._invalidateCache();
|
|
3553
|
+
}
|
|
3554
|
+
/**
|
|
3555
|
+
* Rebuild entire tree from a message array.
|
|
3556
|
+
* Used by setMessages().
|
|
3557
|
+
*/
|
|
3558
|
+
reset(messages) {
|
|
3559
|
+
this.nodeMap.clear();
|
|
3560
|
+
this.childrenOf.clear();
|
|
3561
|
+
this.activeChildMap.clear();
|
|
3562
|
+
this._currentLeafId = null;
|
|
3563
|
+
this._invalidateCache();
|
|
3564
|
+
if (messages.length > 0) {
|
|
3565
|
+
this._buildFromMessages(messages);
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
// ============================================
|
|
3569
|
+
// Private Helpers
|
|
3570
|
+
// ============================================
|
|
3571
|
+
_buildFromMessages(messages) {
|
|
3572
|
+
const linked = messages.some((m) => m.parentId !== void 0) ? messages : _MessageTree.fromFlatArray(messages);
|
|
3573
|
+
for (const msg of linked) {
|
|
3574
|
+
this.nodeMap.set(msg.id, msg);
|
|
3575
|
+
const parentKey = this._parentKey(msg.parentId);
|
|
3576
|
+
if (!this.childrenOf.has(parentKey)) {
|
|
3577
|
+
this.childrenOf.set(parentKey, []);
|
|
3578
|
+
}
|
|
3579
|
+
const siblings = this.childrenOf.get(parentKey);
|
|
3580
|
+
if (!siblings.includes(msg.id)) {
|
|
3581
|
+
siblings.push(msg.id);
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
for (const [parentKey, children] of this.childrenOf) {
|
|
3585
|
+
if (children.length > 0) {
|
|
3586
|
+
this.activeChildMap.set(parentKey, children[children.length - 1]);
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
const path = this._getActivePath();
|
|
3590
|
+
this._currentLeafId = path.length > 0 ? path[path.length - 1] : null;
|
|
3591
|
+
}
|
|
3592
|
+
_parentKey(parentId) {
|
|
3593
|
+
if (parentId == null || parentId === void 0) {
|
|
3594
|
+
return _MessageTree.ROOT_KEY;
|
|
3595
|
+
}
|
|
3596
|
+
return parentId;
|
|
3597
|
+
}
|
|
3598
|
+
/**
|
|
3599
|
+
* Walk forward from a message along active children to find the leaf.
|
|
3600
|
+
*/
|
|
3601
|
+
_walkToLeaf(fromId) {
|
|
3602
|
+
let current = fromId;
|
|
3603
|
+
while (true) {
|
|
3604
|
+
const children = this.childrenOf.get(current);
|
|
3605
|
+
if (!children || children.length === 0) break;
|
|
3606
|
+
const activeChild = this.activeChildMap.get(current);
|
|
3607
|
+
if (!activeChild) break;
|
|
3608
|
+
if (!this.nodeMap.has(activeChild)) break;
|
|
3609
|
+
current = activeChild;
|
|
3610
|
+
}
|
|
3611
|
+
return current;
|
|
3612
|
+
}
|
|
3613
|
+
/**
|
|
3614
|
+
* Walk the active path from root to the current leaf.
|
|
3615
|
+
*/
|
|
3616
|
+
_getActivePath() {
|
|
3617
|
+
const path = [];
|
|
3618
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3619
|
+
const rootChildren = this.childrenOf.get(_MessageTree.ROOT_KEY) ?? [];
|
|
3620
|
+
if (rootChildren.length === 0) return path;
|
|
3621
|
+
let activeId = this.activeChildMap.get(_MessageTree.ROOT_KEY);
|
|
3622
|
+
if (!activeId) {
|
|
3623
|
+
activeId = rootChildren[rootChildren.length - 1];
|
|
3624
|
+
}
|
|
3625
|
+
let current = activeId;
|
|
3626
|
+
while (current && !visited.has(current)) {
|
|
3627
|
+
if (!this.nodeMap.has(current)) break;
|
|
3628
|
+
visited.add(current);
|
|
3629
|
+
path.push(current);
|
|
3630
|
+
const activeChild = this.activeChildMap.get(current);
|
|
3631
|
+
if (!activeChild || !this.nodeMap.has(activeChild)) break;
|
|
3632
|
+
current = activeChild;
|
|
2101
3633
|
}
|
|
3634
|
+
return path;
|
|
2102
3635
|
}
|
|
2103
3636
|
};
|
|
3637
|
+
/** Sentinel key used for root-level messages (parentId === null) */
|
|
3638
|
+
_MessageTree.ROOT_KEY = "__root__";
|
|
3639
|
+
var MessageTree = _MessageTree;
|
|
2104
3640
|
|
|
2105
3641
|
// src/react/internal/ReactChatState.ts
|
|
2106
3642
|
var ReactChatState = class {
|
|
2107
3643
|
constructor(initialMessages) {
|
|
2108
|
-
this._messages = [];
|
|
2109
3644
|
this._status = "ready";
|
|
2110
3645
|
this._error = void 0;
|
|
2111
3646
|
// Callbacks for React subscriptions (useSyncExternalStore)
|
|
@@ -2131,15 +3666,19 @@ var ReactChatState = class {
|
|
|
2131
3666
|
this.subscribers.delete(callback);
|
|
2132
3667
|
};
|
|
2133
3668
|
};
|
|
2134
|
-
|
|
2135
|
-
this._messages = initialMessages;
|
|
2136
|
-
}
|
|
3669
|
+
this.tree = new MessageTree(initialMessages);
|
|
2137
3670
|
}
|
|
2138
3671
|
// ============================================
|
|
2139
|
-
// Getters
|
|
3672
|
+
// Getters — visible path only
|
|
2140
3673
|
// ============================================
|
|
3674
|
+
/**
|
|
3675
|
+
* Returns the VISIBLE PATH (active branch) — what the UI renders
|
|
3676
|
+
* and what gets sent to the API.
|
|
3677
|
+
*
|
|
3678
|
+
* For all messages across all branches, use getAllMessages().
|
|
3679
|
+
*/
|
|
2141
3680
|
get messages() {
|
|
2142
|
-
return this.
|
|
3681
|
+
return this.tree.getVisibleMessages();
|
|
2143
3682
|
}
|
|
2144
3683
|
get status() {
|
|
2145
3684
|
return this._status;
|
|
@@ -2151,7 +3690,7 @@ var ReactChatState = class {
|
|
|
2151
3690
|
// Setters (trigger reactivity)
|
|
2152
3691
|
// ============================================
|
|
2153
3692
|
set messages(value) {
|
|
2154
|
-
this.
|
|
3693
|
+
this.tree.reset(value);
|
|
2155
3694
|
this.notify();
|
|
2156
3695
|
}
|
|
2157
3696
|
set status(value) {
|
|
@@ -2166,66 +3705,105 @@ var ReactChatState = class {
|
|
|
2166
3705
|
// Mutations
|
|
2167
3706
|
// ============================================
|
|
2168
3707
|
pushMessage(message) {
|
|
2169
|
-
this.
|
|
3708
|
+
this.tree.addMessage(message);
|
|
2170
3709
|
this.notify();
|
|
2171
3710
|
}
|
|
2172
3711
|
popMessage() {
|
|
2173
|
-
|
|
3712
|
+
const leafId = this.tree.currentLeafId;
|
|
3713
|
+
if (!leafId) return;
|
|
3714
|
+
const allMessages = this.tree.getAllMessages().filter((m) => m.id !== leafId);
|
|
3715
|
+
const leaf = this.tree.getAllMessages().find((m) => m.id === leafId);
|
|
3716
|
+
const newLeafId = leaf && leaf.parentId !== void 0 && leaf.parentId !== null ? leaf.parentId : null;
|
|
3717
|
+
this.tree.reset(allMessages);
|
|
3718
|
+
if (newLeafId) {
|
|
3719
|
+
this.tree.setCurrentLeaf(newLeafId);
|
|
3720
|
+
}
|
|
2174
3721
|
this.notify();
|
|
2175
3722
|
}
|
|
2176
3723
|
replaceMessage(index, message) {
|
|
2177
|
-
|
|
3724
|
+
const visible = this.tree.getVisibleMessages();
|
|
3725
|
+
const target = visible[index];
|
|
3726
|
+
if (!target) return;
|
|
3727
|
+
this.tree.updateMessage(target.id, () => message);
|
|
2178
3728
|
this.notify();
|
|
2179
3729
|
}
|
|
2180
3730
|
updateLastMessage(updater) {
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
this._messages = [
|
|
2185
|
-
...this._messages.slice(0, lastIndex),
|
|
2186
|
-
updater(lastMessage)
|
|
2187
|
-
];
|
|
3731
|
+
const leafId = this.tree.currentLeafId;
|
|
3732
|
+
if (!leafId) return;
|
|
3733
|
+
this.tree.updateMessage(leafId, updater);
|
|
2188
3734
|
this.notify();
|
|
2189
3735
|
}
|
|
2190
3736
|
updateMessageById(id, updater) {
|
|
2191
|
-
const
|
|
2192
|
-
if (
|
|
2193
|
-
|
|
2194
|
-
(m, i) => i === index ? updater(m) : m
|
|
2195
|
-
);
|
|
2196
|
-
this.notify();
|
|
2197
|
-
return true;
|
|
3737
|
+
const updated = this.tree.updateMessage(id, updater);
|
|
3738
|
+
if (updated) this.notify();
|
|
3739
|
+
return updated;
|
|
2198
3740
|
}
|
|
2199
3741
|
setMessages(messages) {
|
|
2200
|
-
this.
|
|
3742
|
+
this.tree.reset(messages);
|
|
2201
3743
|
this.notify();
|
|
2202
3744
|
}
|
|
2203
3745
|
clearMessages() {
|
|
2204
|
-
this.
|
|
3746
|
+
this.tree.reset([]);
|
|
2205
3747
|
this.notify();
|
|
2206
3748
|
}
|
|
2207
3749
|
// ============================================
|
|
2208
|
-
//
|
|
3750
|
+
// Branching API
|
|
2209
3751
|
// ============================================
|
|
2210
|
-
|
|
2211
|
-
|
|
3752
|
+
/**
|
|
3753
|
+
* Returns ALL messages across all branches.
|
|
3754
|
+
* Use this for persistence (ThreadManager save).
|
|
3755
|
+
*/
|
|
3756
|
+
getAllMessages() {
|
|
3757
|
+
return this.tree.getAllMessages();
|
|
2212
3758
|
}
|
|
2213
3759
|
/**
|
|
2214
|
-
*
|
|
3760
|
+
* Get branch navigation info for a message.
|
|
3761
|
+
* Returns null if the message has no siblings.
|
|
2215
3762
|
*/
|
|
2216
|
-
|
|
2217
|
-
this.
|
|
3763
|
+
getBranchInfo(messageId) {
|
|
3764
|
+
return this.tree.getBranchInfo(messageId);
|
|
2218
3765
|
}
|
|
2219
3766
|
/**
|
|
2220
|
-
*
|
|
2221
|
-
*
|
|
3767
|
+
* Navigate to a sibling branch.
|
|
3768
|
+
* Triggers re-render via notify().
|
|
2222
3769
|
*/
|
|
2223
|
-
|
|
3770
|
+
switchBranch(messageId) {
|
|
3771
|
+
this.tree.switchBranch(messageId);
|
|
3772
|
+
this.notify();
|
|
2224
3773
|
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
3774
|
+
/**
|
|
3775
|
+
* Set the current leaf (used by regenerate() to rewind active path).
|
|
3776
|
+
* Triggers re-render via notify().
|
|
3777
|
+
*/
|
|
3778
|
+
setCurrentLeaf(leafId) {
|
|
3779
|
+
this.tree.setCurrentLeaf(leafId);
|
|
3780
|
+
this.notify();
|
|
3781
|
+
}
|
|
3782
|
+
get hasBranches() {
|
|
3783
|
+
return this.tree.hasBranches;
|
|
3784
|
+
}
|
|
3785
|
+
// ============================================
|
|
3786
|
+
// Private Methods
|
|
3787
|
+
// ============================================
|
|
3788
|
+
notify() {
|
|
3789
|
+
this.subscribers.forEach((cb) => cb());
|
|
3790
|
+
}
|
|
3791
|
+
/**
|
|
3792
|
+
* Cleanup subscriptions
|
|
3793
|
+
*/
|
|
3794
|
+
dispose() {
|
|
3795
|
+
this.subscribers.clear();
|
|
3796
|
+
}
|
|
3797
|
+
/**
|
|
3798
|
+
* Revive after dispose (for React StrictMode compatibility)
|
|
3799
|
+
* Subscribers will be re-added automatically via useSyncExternalStore
|
|
3800
|
+
*/
|
|
3801
|
+
revive() {
|
|
3802
|
+
}
|
|
3803
|
+
};
|
|
3804
|
+
function createReactChatState(initialMessages) {
|
|
3805
|
+
return new ReactChatState(initialMessages);
|
|
3806
|
+
}
|
|
2229
3807
|
|
|
2230
3808
|
// src/react/internal/ReactChatWithTools.ts
|
|
2231
3809
|
var ReactChatWithTools = class extends ChatWithTools {
|
|
@@ -2240,6 +3818,33 @@ var ReactChatWithTools = class extends ChatWithTools {
|
|
|
2240
3818
|
};
|
|
2241
3819
|
this.reactState = reactState;
|
|
2242
3820
|
}
|
|
3821
|
+
// ============================================
|
|
3822
|
+
// Branching API — pass-throughs to ReactChatState
|
|
3823
|
+
// ============================================
|
|
3824
|
+
/**
|
|
3825
|
+
* Navigate to a sibling branch.
|
|
3826
|
+
*/
|
|
3827
|
+
switchBranch(messageId) {
|
|
3828
|
+
this.reactState.switchBranch(messageId);
|
|
3829
|
+
}
|
|
3830
|
+
/**
|
|
3831
|
+
* Get branch navigation info for a message.
|
|
3832
|
+
*/
|
|
3833
|
+
getBranchInfo(messageId) {
|
|
3834
|
+
return this.reactState.getBranchInfo(messageId);
|
|
3835
|
+
}
|
|
3836
|
+
/**
|
|
3837
|
+
* Get all messages across all branches (for persistence).
|
|
3838
|
+
*/
|
|
3839
|
+
getAllMessages() {
|
|
3840
|
+
return this.reactState.getAllMessages();
|
|
3841
|
+
}
|
|
3842
|
+
/**
|
|
3843
|
+
* Whether any message has siblings (branching has occurred).
|
|
3844
|
+
*/
|
|
3845
|
+
get hasBranches() {
|
|
3846
|
+
return this.reactState.hasBranches;
|
|
3847
|
+
}
|
|
2243
3848
|
/**
|
|
2244
3849
|
* Dispose and cleanup
|
|
2245
3850
|
*/
|
|
@@ -2337,13 +3942,13 @@ function useMCPClient(config) {
|
|
|
2337
3942
|
onNotification,
|
|
2338
3943
|
...clientConfig
|
|
2339
3944
|
} = config;
|
|
2340
|
-
const clientRef =
|
|
2341
|
-
const mountedRef =
|
|
2342
|
-
const [state, setState] =
|
|
3945
|
+
const clientRef = React2.useRef(null);
|
|
3946
|
+
const mountedRef = React2.useRef(true);
|
|
3947
|
+
const [state, setState] = React2.useState({
|
|
2343
3948
|
connectionState: "disconnected",
|
|
2344
3949
|
tools: []
|
|
2345
3950
|
});
|
|
2346
|
-
const getClient =
|
|
3951
|
+
const getClient = React2.useCallback(() => {
|
|
2347
3952
|
if (!clientRef.current) {
|
|
2348
3953
|
clientRef.current = chunkJGPDQDY4_cjs.createMCPClient(clientConfig, {
|
|
2349
3954
|
onConnectionStateChange: (newState) => {
|
|
@@ -2377,7 +3982,7 @@ function useMCPClient(config) {
|
|
|
2377
3982
|
onError,
|
|
2378
3983
|
onNotification
|
|
2379
3984
|
]);
|
|
2380
|
-
const connect =
|
|
3985
|
+
const connect = React2.useCallback(async () => {
|
|
2381
3986
|
const client = getClient();
|
|
2382
3987
|
try {
|
|
2383
3988
|
setState((prev) => ({
|
|
@@ -2408,7 +4013,7 @@ function useMCPClient(config) {
|
|
|
2408
4013
|
throw error;
|
|
2409
4014
|
}
|
|
2410
4015
|
}, [getClient]);
|
|
2411
|
-
const disconnect =
|
|
4016
|
+
const disconnect = React2.useCallback(async () => {
|
|
2412
4017
|
const client = clientRef.current;
|
|
2413
4018
|
if (client) {
|
|
2414
4019
|
await client.disconnect();
|
|
@@ -2420,7 +4025,7 @@ function useMCPClient(config) {
|
|
|
2420
4025
|
}
|
|
2421
4026
|
}
|
|
2422
4027
|
}, []);
|
|
2423
|
-
const callTool =
|
|
4028
|
+
const callTool = React2.useCallback(
|
|
2424
4029
|
async (name, args) => {
|
|
2425
4030
|
const client = clientRef.current;
|
|
2426
4031
|
if (!client || !client.isConnected()) {
|
|
@@ -2434,7 +4039,7 @@ function useMCPClient(config) {
|
|
|
2434
4039
|
},
|
|
2435
4040
|
[]
|
|
2436
4041
|
);
|
|
2437
|
-
const refreshTools =
|
|
4042
|
+
const refreshTools = React2.useCallback(async () => {
|
|
2438
4043
|
const client = clientRef.current;
|
|
2439
4044
|
if (!client || !client.isConnected()) {
|
|
2440
4045
|
throw new Error("MCP client not connected");
|
|
@@ -2445,7 +4050,7 @@ function useMCPClient(config) {
|
|
|
2445
4050
|
}
|
|
2446
4051
|
return tools;
|
|
2447
4052
|
}, []);
|
|
2448
|
-
|
|
4053
|
+
React2.useEffect(() => {
|
|
2449
4054
|
mountedRef.current = true;
|
|
2450
4055
|
if (autoConnect) {
|
|
2451
4056
|
connect().catch(() => {
|
|
@@ -2484,13 +4089,13 @@ function useMCPTools(config) {
|
|
|
2484
4089
|
...clientConfig
|
|
2485
4090
|
} = config;
|
|
2486
4091
|
const { registerTool, unregisterTool } = useCopilot();
|
|
2487
|
-
const registeredToolsRef =
|
|
4092
|
+
const registeredToolsRef = React2.useRef([]);
|
|
2488
4093
|
const mcpClient = useMCPClient(clientConfig);
|
|
2489
|
-
const toolAdapter =
|
|
4094
|
+
const toolAdapter = React2.useMemo(
|
|
2490
4095
|
() => new chunkJGPDQDY4_cjs.MCPToolAdapter(clientConfig.name),
|
|
2491
4096
|
[clientConfig.name]
|
|
2492
4097
|
);
|
|
2493
|
-
const toolDefinitions =
|
|
4098
|
+
const toolDefinitions = React2.useMemo(() => {
|
|
2494
4099
|
if (!mcpClient.isConnected || mcpClient.state.tools.length === 0) {
|
|
2495
4100
|
return [];
|
|
2496
4101
|
}
|
|
@@ -2515,7 +4120,7 @@ function useMCPTools(config) {
|
|
|
2515
4120
|
hidden,
|
|
2516
4121
|
source
|
|
2517
4122
|
]);
|
|
2518
|
-
|
|
4123
|
+
React2.useEffect(() => {
|
|
2519
4124
|
if (!autoRegister) {
|
|
2520
4125
|
return;
|
|
2521
4126
|
}
|
|
@@ -2530,22 +4135,919 @@ function useMCPTools(config) {
|
|
|
2530
4135
|
}
|
|
2531
4136
|
}
|
|
2532
4137
|
return () => {
|
|
2533
|
-
for (const toolName of registeredToolsRef.current) {
|
|
2534
|
-
unregisterTool(toolName);
|
|
4138
|
+
for (const toolName of registeredToolsRef.current) {
|
|
4139
|
+
unregisterTool(toolName);
|
|
4140
|
+
}
|
|
4141
|
+
registeredToolsRef.current = [];
|
|
4142
|
+
};
|
|
4143
|
+
}, [
|
|
4144
|
+
autoRegister,
|
|
4145
|
+
mcpClient.isConnected,
|
|
4146
|
+
toolDefinitions,
|
|
4147
|
+
registerTool,
|
|
4148
|
+
unregisterTool
|
|
4149
|
+
]);
|
|
4150
|
+
return {
|
|
4151
|
+
...mcpClient,
|
|
4152
|
+
toolDefinitions
|
|
4153
|
+
};
|
|
4154
|
+
}
|
|
4155
|
+
var defaultTokenUsage = {
|
|
4156
|
+
current: 0,
|
|
4157
|
+
max: 128e3,
|
|
4158
|
+
percentage: 0,
|
|
4159
|
+
isApproaching: false
|
|
4160
|
+
};
|
|
4161
|
+
var defaultCompactionState = {
|
|
4162
|
+
rollingSummary: null,
|
|
4163
|
+
lastCompactionAt: null,
|
|
4164
|
+
compactionCount: 0,
|
|
4165
|
+
totalTokensSaved: 0,
|
|
4166
|
+
workingMemory: [],
|
|
4167
|
+
displayMessageCount: 0,
|
|
4168
|
+
llmMessageCount: 0
|
|
4169
|
+
};
|
|
4170
|
+
var defaultMessageHistoryConfig = {
|
|
4171
|
+
strategy: "none",
|
|
4172
|
+
maxContextTokens: 128e3,
|
|
4173
|
+
reserveForResponse: 4096,
|
|
4174
|
+
compactionThreshold: 0.75,
|
|
4175
|
+
recentBuffer: 10,
|
|
4176
|
+
toolResultMaxChars: 1e4,
|
|
4177
|
+
persistSession: false,
|
|
4178
|
+
storageKey: "copilot-session"
|
|
4179
|
+
};
|
|
4180
|
+
var MessageHistoryContext = React2.createContext({
|
|
4181
|
+
config: defaultMessageHistoryConfig,
|
|
4182
|
+
tokenUsage: defaultTokenUsage,
|
|
4183
|
+
compactionState: defaultCompactionState
|
|
4184
|
+
});
|
|
4185
|
+
function useMessageHistoryContext() {
|
|
4186
|
+
return React2.useContext(MessageHistoryContext);
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4189
|
+
// src/react/message-history/message-utils.ts
|
|
4190
|
+
function toDisplayMessage(msg) {
|
|
4191
|
+
return {
|
|
4192
|
+
...msg,
|
|
4193
|
+
timestamp: msg.createdAt instanceof Date ? msg.createdAt.getTime() : Date.now()
|
|
4194
|
+
};
|
|
4195
|
+
}
|
|
4196
|
+
function toLLMMessage(msg) {
|
|
4197
|
+
if (isCompactionMarker(msg)) {
|
|
4198
|
+
return {
|
|
4199
|
+
role: "system",
|
|
4200
|
+
content: `[Previous conversation summary]
|
|
4201
|
+
${msg.content}`
|
|
4202
|
+
};
|
|
4203
|
+
}
|
|
4204
|
+
const base = {
|
|
4205
|
+
role: msg.role,
|
|
4206
|
+
content: msg.content
|
|
4207
|
+
};
|
|
4208
|
+
if (msg.toolCalls?.length) {
|
|
4209
|
+
base.tool_calls = msg.toolCalls;
|
|
4210
|
+
}
|
|
4211
|
+
if (msg.toolCallId) {
|
|
4212
|
+
base.tool_call_id = msg.toolCallId;
|
|
4213
|
+
}
|
|
4214
|
+
return base;
|
|
4215
|
+
}
|
|
4216
|
+
function toLLMMessages(messages) {
|
|
4217
|
+
return messages.map(toLLMMessage);
|
|
4218
|
+
}
|
|
4219
|
+
function keepToolPairsAtomic(messages) {
|
|
4220
|
+
if (messages.length === 0) return messages;
|
|
4221
|
+
const firstIdx = messages.findIndex((msg, i) => {
|
|
4222
|
+
if (msg.role !== "assistant" || !msg.toolCalls?.length) return false;
|
|
4223
|
+
const toolCallIds = new Set(msg.toolCalls.map((tc) => tc.id));
|
|
4224
|
+
const resultIds = new Set(
|
|
4225
|
+
messages.slice(i + 1).filter((m) => m.role === "tool" && m.toolCallId).map((m) => m.toolCallId)
|
|
4226
|
+
);
|
|
4227
|
+
return [...toolCallIds].some((id) => !resultIds.has(id));
|
|
4228
|
+
});
|
|
4229
|
+
if (firstIdx === -1) return messages;
|
|
4230
|
+
return messages.slice(firstIdx);
|
|
4231
|
+
}
|
|
4232
|
+
function findSafeWindowStart(messages, desiredStart) {
|
|
4233
|
+
for (let i = desiredStart; i < messages.length; i++) {
|
|
4234
|
+
const msg = messages[i];
|
|
4235
|
+
if (msg.role === "user" || msg.role === "assistant" && !msg.toolCalls?.length) {
|
|
4236
|
+
return i;
|
|
4237
|
+
}
|
|
4238
|
+
}
|
|
4239
|
+
return desiredStart;
|
|
4240
|
+
}
|
|
4241
|
+
function isCompactionMarker(msg) {
|
|
4242
|
+
return msg.role === "system" && msg.type === "compaction-marker";
|
|
4243
|
+
}
|
|
4244
|
+
|
|
4245
|
+
// src/react/message-history/token-counter.ts
|
|
4246
|
+
function estimateMessageTokens2(msg) {
|
|
4247
|
+
let chars = msg.content?.length ?? 0;
|
|
4248
|
+
if (msg.tool_calls?.length) {
|
|
4249
|
+
for (const tc of msg.tool_calls) {
|
|
4250
|
+
chars += JSON.stringify(tc).length;
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
return Math.ceil(chars / 3.5) + 4;
|
|
4254
|
+
}
|
|
4255
|
+
function estimateMessagesTokens(messages) {
|
|
4256
|
+
return messages.reduce((sum, msg) => sum + estimateMessageTokens2(msg), 0);
|
|
4257
|
+
}
|
|
4258
|
+
function estimateTokens2(messages, mode = "fast") {
|
|
4259
|
+
if (mode === "off") return 0;
|
|
4260
|
+
return estimateMessagesTokens(messages);
|
|
4261
|
+
}
|
|
4262
|
+
|
|
4263
|
+
// src/react/message-history/strategies/sliding-window.ts
|
|
4264
|
+
function applySlidingWindow(messages, options) {
|
|
4265
|
+
const { tokenBudget, recentBuffer } = options;
|
|
4266
|
+
if (messages.length === 0) return messages;
|
|
4267
|
+
const systemMessages = messages.filter(
|
|
4268
|
+
(m) => m.role === "system" || isCompactionMarker(m)
|
|
4269
|
+
);
|
|
4270
|
+
const conversationMessages = messages.filter(
|
|
4271
|
+
(m) => m.role !== "system" && !isCompactionMarker(m)
|
|
4272
|
+
);
|
|
4273
|
+
const systemTokens = estimateMessagesTokens(toLLMMessages(systemMessages));
|
|
4274
|
+
const remainingBudget = tokenBudget - systemTokens;
|
|
4275
|
+
if (conversationMessages.length === 0) return systemMessages;
|
|
4276
|
+
const recentStart = Math.max(0, conversationMessages.length - recentBuffer);
|
|
4277
|
+
const recent = conversationMessages.slice(recentStart);
|
|
4278
|
+
const older = conversationMessages.slice(0, recentStart);
|
|
4279
|
+
const allTokens = estimateMessagesTokens(toLLMMessages(conversationMessages));
|
|
4280
|
+
if (allTokens <= remainingBudget) {
|
|
4281
|
+
return messages;
|
|
4282
|
+
}
|
|
4283
|
+
const recentTokens = estimateMessagesTokens(toLLMMessages(recent));
|
|
4284
|
+
let available = remainingBudget - recentTokens;
|
|
4285
|
+
const included = [];
|
|
4286
|
+
for (let i = older.length - 1; i >= 0; i--) {
|
|
4287
|
+
const msgTokens = estimateMessagesTokens(toLLMMessages([older[i]]));
|
|
4288
|
+
if (available - msgTokens < 0) break;
|
|
4289
|
+
included.unshift(older[i]);
|
|
4290
|
+
available -= msgTokens;
|
|
4291
|
+
}
|
|
4292
|
+
const combined = [...included, ...recent];
|
|
4293
|
+
const safeStart = findSafeWindowStart(combined, 0);
|
|
4294
|
+
const safeWindow = combined.slice(safeStart);
|
|
4295
|
+
return [...systemMessages, ...safeWindow];
|
|
4296
|
+
}
|
|
4297
|
+
function truncateToolResults(messages, maxChars) {
|
|
4298
|
+
if (maxChars === 0) return messages;
|
|
4299
|
+
return messages.map((msg) => {
|
|
4300
|
+
if (msg.role !== "tool") return msg;
|
|
4301
|
+
if (!msg.content || msg.content.length <= maxChars) return msg;
|
|
4302
|
+
return {
|
|
4303
|
+
...msg,
|
|
4304
|
+
content: msg.content.slice(0, maxChars) + `
|
|
4305
|
+
[truncated \u2014 original ${msg.content.length} chars, limit ${maxChars}]`
|
|
4306
|
+
};
|
|
4307
|
+
});
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4310
|
+
// src/react/message-history/strategies/selective-prune.ts
|
|
4311
|
+
function applySelectivePrune(displayMessages, recentBuffer, options = {}) {
|
|
4312
|
+
const {
|
|
4313
|
+
toolResultAgeTurns = 3,
|
|
4314
|
+
stripOldReasoning = true,
|
|
4315
|
+
deduplicateSkills = true
|
|
4316
|
+
} = options;
|
|
4317
|
+
const cutoff = Math.max(0, displayMessages.length - recentBuffer);
|
|
4318
|
+
const seenSkillContent = /* @__PURE__ */ new Set();
|
|
4319
|
+
return displayMessages.map((msg, idx) => {
|
|
4320
|
+
const llm = toLLMMessage(msg);
|
|
4321
|
+
const isOld = idx < cutoff;
|
|
4322
|
+
if (deduplicateSkills && msg.role === "system" && llm.content) {
|
|
4323
|
+
const key = llm.content.slice(0, 100);
|
|
4324
|
+
if (seenSkillContent.has(key)) {
|
|
4325
|
+
return { ...llm, content: "[skill instruction \u2014 deduplicated]" };
|
|
4326
|
+
}
|
|
4327
|
+
seenSkillContent.add(key);
|
|
4328
|
+
}
|
|
4329
|
+
if (!isOld) return llm;
|
|
4330
|
+
if (stripOldReasoning && msg.role === "assistant" && msg.thinking) {
|
|
4331
|
+
llm.content = llm.content;
|
|
4332
|
+
}
|
|
4333
|
+
if (msg.role === "tool" && llm.content) {
|
|
4334
|
+
const originalSize = llm.content.length;
|
|
4335
|
+
if (originalSize > 500) {
|
|
4336
|
+
const stub = buildToolResultStub(msg, llm.content);
|
|
4337
|
+
return {
|
|
4338
|
+
role: "tool",
|
|
4339
|
+
tool_call_id: llm.tool_call_id,
|
|
4340
|
+
content: JSON.stringify(stub)
|
|
4341
|
+
};
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4344
|
+
return llm;
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
function buildToolResultStub(msg, content) {
|
|
4348
|
+
return {
|
|
4349
|
+
type: "compacted-tool-result",
|
|
4350
|
+
toolName: msg.metadata?.toolName ?? "tool",
|
|
4351
|
+
toolCallId: msg.toolCallId ?? "",
|
|
4352
|
+
args: msg.metadata?.toolArgs ?? {},
|
|
4353
|
+
executedAt: msg.timestamp,
|
|
4354
|
+
status: content.includes('"error"') ? "error" : "success",
|
|
4355
|
+
originalSize: content.length,
|
|
4356
|
+
summary: buildSummary(content),
|
|
4357
|
+
extract: content.slice(0, 200)
|
|
4358
|
+
};
|
|
4359
|
+
}
|
|
4360
|
+
function buildSummary(content) {
|
|
4361
|
+
try {
|
|
4362
|
+
const parsed = JSON.parse(content);
|
|
4363
|
+
if (parsed?.message) return String(parsed.message).slice(0, 120);
|
|
4364
|
+
if (parsed?.error) return `Error: ${String(parsed.error).slice(0, 100)}`;
|
|
4365
|
+
if (Array.isArray(parsed)) return `Array result \u2014 ${parsed.length} items`;
|
|
4366
|
+
const keys = Object.keys(parsed).slice(0, 3).join(", ");
|
|
4367
|
+
return `Object result \u2014 keys: ${keys}`;
|
|
4368
|
+
} catch {
|
|
4369
|
+
return content.slice(0, 120);
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
|
|
4373
|
+
// src/react/message-history/strategies/summary-buffer.ts
|
|
4374
|
+
function buildSummaryBufferContext(displayMessages, compactionState, options) {
|
|
4375
|
+
const { recentBuffer } = options;
|
|
4376
|
+
const systemMessages = displayMessages.filter(
|
|
4377
|
+
(m) => m.role === "system" || isCompactionMarker(m)
|
|
4378
|
+
);
|
|
4379
|
+
const conversationMessages = displayMessages.filter(
|
|
4380
|
+
(m) => m.role !== "system" && !isCompactionMarker(m)
|
|
4381
|
+
);
|
|
4382
|
+
const recentStart = Math.max(0, conversationMessages.length - recentBuffer);
|
|
4383
|
+
const recentMessages = conversationMessages.slice(recentStart);
|
|
4384
|
+
const result = [];
|
|
4385
|
+
if (compactionState.workingMemory.length > 0) {
|
|
4386
|
+
result.push({
|
|
4387
|
+
role: "system",
|
|
4388
|
+
content: `[Working memory \u2014 always active]
|
|
4389
|
+
${compactionState.workingMemory.join("\n")}`
|
|
4390
|
+
});
|
|
4391
|
+
}
|
|
4392
|
+
if (compactionState.rollingSummary) {
|
|
4393
|
+
result.push({
|
|
4394
|
+
role: "system",
|
|
4395
|
+
content: `[Previous conversation summary]
|
|
4396
|
+
${compactionState.rollingSummary}`
|
|
4397
|
+
});
|
|
4398
|
+
}
|
|
4399
|
+
result.push(...toLLMMessages(systemMessages));
|
|
4400
|
+
result.push(...toLLMMessages(recentMessages));
|
|
4401
|
+
return result;
|
|
4402
|
+
}
|
|
4403
|
+
async function runCompaction(displayMessages, compactionState, options) {
|
|
4404
|
+
const { recentBuffer, compactionUrl, summarizer } = options;
|
|
4405
|
+
const conversationMessages = displayMessages.filter(
|
|
4406
|
+
(m) => m.role !== "system" && !isCompactionMarker(m)
|
|
4407
|
+
);
|
|
4408
|
+
const cutoff = Math.max(0, conversationMessages.length - recentBuffer);
|
|
4409
|
+
const toSummarize = conversationMessages.slice(0, cutoff);
|
|
4410
|
+
if (toSummarize.length === 0) {
|
|
4411
|
+
return { llmMessages: toLLMMessages(displayMessages) };
|
|
4412
|
+
}
|
|
4413
|
+
const llmToSummarize = toLLMMessages(toSummarize);
|
|
4414
|
+
const originalTokens = estimateMessagesTokens(llmToSummarize);
|
|
4415
|
+
let newSummary;
|
|
4416
|
+
if (summarizer) {
|
|
4417
|
+
newSummary = await summarizer(llmToSummarize);
|
|
4418
|
+
} else if (compactionUrl) {
|
|
4419
|
+
newSummary = await fetchSummary(compactionUrl, {
|
|
4420
|
+
messages: llmToSummarize,
|
|
4421
|
+
existingSummary: compactionState.rollingSummary,
|
|
4422
|
+
workingMemory: compactionState.workingMemory
|
|
4423
|
+
});
|
|
4424
|
+
} else {
|
|
4425
|
+
newSummary = buildFallbackSummary(
|
|
4426
|
+
llmToSummarize,
|
|
4427
|
+
compactionState.rollingSummary
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
const summaryTokens = Math.ceil(newSummary.length / 3.5);
|
|
4431
|
+
const tokensSaved = Math.max(0, originalTokens - summaryTokens);
|
|
4432
|
+
return {
|
|
4433
|
+
llmMessages: buildSummaryBufferContext(
|
|
4434
|
+
displayMessages,
|
|
4435
|
+
{ ...compactionState, rollingSummary: newSummary },
|
|
4436
|
+
options
|
|
4437
|
+
),
|
|
4438
|
+
newSummary,
|
|
4439
|
+
tokensSaved,
|
|
4440
|
+
messagesSummarized: toSummarize.length
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
async function fetchSummary(url, body) {
|
|
4444
|
+
const res = await fetch(url, {
|
|
4445
|
+
method: "POST",
|
|
4446
|
+
headers: { "Content-Type": "application/json" },
|
|
4447
|
+
body: JSON.stringify(body)
|
|
4448
|
+
});
|
|
4449
|
+
if (!res.ok) {
|
|
4450
|
+
throw new Error(`Compaction endpoint returned ${res.status}`);
|
|
4451
|
+
}
|
|
4452
|
+
const data = await res.json();
|
|
4453
|
+
if (!data?.summary) {
|
|
4454
|
+
throw new Error("Compaction endpoint did not return { summary: string }");
|
|
4455
|
+
}
|
|
4456
|
+
return data.summary;
|
|
4457
|
+
}
|
|
4458
|
+
function buildFallbackSummary(messages, existingSummary) {
|
|
4459
|
+
const lines = messages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => `${m.role}: ${(m.content ?? "").slice(0, 200)}`).join("\n");
|
|
4460
|
+
return existingSummary ? `${existingSummary}
|
|
4461
|
+
|
|
4462
|
+
[Additional context]
|
|
4463
|
+
${lines}` : lines;
|
|
4464
|
+
}
|
|
4465
|
+
|
|
4466
|
+
// src/react/message-history/session-persistence.ts
|
|
4467
|
+
var IDB_DB_NAME = "copilot-sdk";
|
|
4468
|
+
var IDB_STORE = "sessions";
|
|
4469
|
+
var IDB_VERSION = 1;
|
|
4470
|
+
function saveCompactionState(storageKey, state) {
|
|
4471
|
+
try {
|
|
4472
|
+
localStorage.setItem(
|
|
4473
|
+
`${storageKey}-state`,
|
|
4474
|
+
JSON.stringify({ ...state, _savedAt: Date.now() })
|
|
4475
|
+
);
|
|
4476
|
+
} catch {
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
function loadCompactionState(storageKey) {
|
|
4480
|
+
try {
|
|
4481
|
+
const raw = localStorage.getItem(`${storageKey}-state`);
|
|
4482
|
+
if (!raw) return null;
|
|
4483
|
+
const parsed = JSON.parse(raw);
|
|
4484
|
+
delete parsed._savedAt;
|
|
4485
|
+
return parsed;
|
|
4486
|
+
} catch {
|
|
4487
|
+
return null;
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
function clearCompactionState(storageKey) {
|
|
4491
|
+
try {
|
|
4492
|
+
localStorage.removeItem(`${storageKey}-state`);
|
|
4493
|
+
} catch {
|
|
4494
|
+
}
|
|
4495
|
+
}
|
|
4496
|
+
function openDB() {
|
|
4497
|
+
return new Promise((resolve, reject) => {
|
|
4498
|
+
const req = indexedDB.open(IDB_DB_NAME, IDB_VERSION);
|
|
4499
|
+
req.onupgradeneeded = () => {
|
|
4500
|
+
req.result.createObjectStore(IDB_STORE, { keyPath: "sessionId" });
|
|
4501
|
+
};
|
|
4502
|
+
req.onsuccess = () => resolve(req.result);
|
|
4503
|
+
req.onerror = () => reject(req.error);
|
|
4504
|
+
});
|
|
4505
|
+
}
|
|
4506
|
+
async function saveDisplayMessages(storageKey, messages) {
|
|
4507
|
+
try {
|
|
4508
|
+
const db = await openDB();
|
|
4509
|
+
const tx = db.transaction(IDB_STORE, "readwrite");
|
|
4510
|
+
tx.objectStore(IDB_STORE).put({
|
|
4511
|
+
sessionId: storageKey,
|
|
4512
|
+
messages,
|
|
4513
|
+
savedAt: Date.now()
|
|
4514
|
+
});
|
|
4515
|
+
await new Promise((res, rej) => {
|
|
4516
|
+
tx.oncomplete = () => res();
|
|
4517
|
+
tx.onerror = () => rej(tx.error);
|
|
4518
|
+
});
|
|
4519
|
+
db.close();
|
|
4520
|
+
} catch {
|
|
4521
|
+
}
|
|
4522
|
+
}
|
|
4523
|
+
async function loadDisplayMessages(storageKey) {
|
|
4524
|
+
try {
|
|
4525
|
+
const db = await openDB();
|
|
4526
|
+
const tx = db.transaction(IDB_STORE, "readonly");
|
|
4527
|
+
const req = tx.objectStore(IDB_STORE).get(storageKey);
|
|
4528
|
+
const result = await new Promise((res, rej) => {
|
|
4529
|
+
req.onsuccess = () => res(req.result);
|
|
4530
|
+
req.onerror = () => rej(req.error);
|
|
4531
|
+
});
|
|
4532
|
+
db.close();
|
|
4533
|
+
return result?.messages ?? null;
|
|
4534
|
+
} catch {
|
|
4535
|
+
return null;
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
async function clearDisplayMessages(storageKey) {
|
|
4539
|
+
try {
|
|
4540
|
+
const db = await openDB();
|
|
4541
|
+
const tx = db.transaction(IDB_STORE, "readwrite");
|
|
4542
|
+
tx.objectStore(IDB_STORE).delete(storageKey);
|
|
4543
|
+
await new Promise((res, rej) => {
|
|
4544
|
+
tx.oncomplete = () => res();
|
|
4545
|
+
tx.onerror = () => rej(tx.error);
|
|
4546
|
+
});
|
|
4547
|
+
db.close();
|
|
4548
|
+
} catch {
|
|
4549
|
+
}
|
|
4550
|
+
}
|
|
4551
|
+
async function clearSession(storageKey) {
|
|
4552
|
+
clearCompactionState(storageKey);
|
|
4553
|
+
await clearDisplayMessages(storageKey);
|
|
4554
|
+
}
|
|
4555
|
+
|
|
4556
|
+
// src/react/message-history/useMessageHistory.ts
|
|
4557
|
+
var DEFAULT_COMPACTION_STATE = {
|
|
4558
|
+
rollingSummary: null,
|
|
4559
|
+
lastCompactionAt: null,
|
|
4560
|
+
compactionCount: 0,
|
|
4561
|
+
totalTokensSaved: 0,
|
|
4562
|
+
workingMemory: [],
|
|
4563
|
+
displayMessageCount: 0,
|
|
4564
|
+
llmMessageCount: 0
|
|
4565
|
+
};
|
|
4566
|
+
function useMessageHistory(options = {}) {
|
|
4567
|
+
const { messages } = useCopilot();
|
|
4568
|
+
const ctx = useMessageHistoryContext();
|
|
4569
|
+
const config = React2.useMemo(
|
|
4570
|
+
() => ({ ...defaultMessageHistoryConfig, ...ctx.config, ...options }),
|
|
4571
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4572
|
+
[
|
|
4573
|
+
ctx.config,
|
|
4574
|
+
options.strategy,
|
|
4575
|
+
options.maxContextTokens,
|
|
4576
|
+
options.recentBuffer,
|
|
4577
|
+
options.compactionThreshold
|
|
4578
|
+
]
|
|
4579
|
+
);
|
|
4580
|
+
const storageKey = config.storageKey ?? "copilot-session";
|
|
4581
|
+
const strategy = options.skipCompaction ? "none" : config.strategy ?? "none";
|
|
4582
|
+
const [compactionState, setCompactionState] = React2.useState(() => {
|
|
4583
|
+
if (config.persistSession) {
|
|
4584
|
+
return loadCompactionState(storageKey) ?? DEFAULT_COMPACTION_STATE;
|
|
4585
|
+
}
|
|
4586
|
+
return DEFAULT_COMPACTION_STATE;
|
|
4587
|
+
});
|
|
4588
|
+
const displayMessages = React2.useMemo(
|
|
4589
|
+
() => messages.map(toDisplayMessage),
|
|
4590
|
+
[messages]
|
|
4591
|
+
);
|
|
4592
|
+
const restoredRef = React2.useRef(false);
|
|
4593
|
+
React2.useEffect(() => {
|
|
4594
|
+
if (!config.persistSession || restoredRef.current) return;
|
|
4595
|
+
restoredRef.current = true;
|
|
4596
|
+
loadDisplayMessages(storageKey).then((saved) => {
|
|
4597
|
+
if (saved?.length && messages.length === 0) ;
|
|
4598
|
+
});
|
|
4599
|
+
}, [config.persistSession, storageKey, messages.length]);
|
|
4600
|
+
React2.useEffect(() => {
|
|
4601
|
+
if (!config.persistSession || displayMessages.length === 0) return;
|
|
4602
|
+
saveDisplayMessages(storageKey, displayMessages);
|
|
4603
|
+
}, [config.persistSession, storageKey, displayMessages]);
|
|
4604
|
+
const llmMessages = React2.useMemo(() => {
|
|
4605
|
+
const maxTokens = config.maxContextTokens ?? 128e3;
|
|
4606
|
+
const reserve = config.reserveForResponse ?? 4096;
|
|
4607
|
+
const tokenBudget = maxTokens - reserve;
|
|
4608
|
+
const recentBuffer = config.recentBuffer ?? 10;
|
|
4609
|
+
const maxChars = config.toolResultMaxChars ?? 1e4;
|
|
4610
|
+
let result;
|
|
4611
|
+
switch (strategy) {
|
|
4612
|
+
case "sliding-window": {
|
|
4613
|
+
const windowed = applySlidingWindow(displayMessages, {
|
|
4614
|
+
tokenBudget,
|
|
4615
|
+
recentBuffer
|
|
4616
|
+
});
|
|
4617
|
+
result = truncateToolResults(toLLMMessages(windowed), maxChars);
|
|
4618
|
+
break;
|
|
4619
|
+
}
|
|
4620
|
+
case "selective-prune": {
|
|
4621
|
+
result = truncateToolResults(
|
|
4622
|
+
applySelectivePrune(displayMessages, recentBuffer),
|
|
4623
|
+
maxChars
|
|
4624
|
+
);
|
|
4625
|
+
break;
|
|
4626
|
+
}
|
|
4627
|
+
case "summary-buffer": {
|
|
4628
|
+
result = truncateToolResults(
|
|
4629
|
+
buildSummaryBufferContext(displayMessages, compactionState, {
|
|
4630
|
+
recentBuffer,
|
|
4631
|
+
compactionThreshold: config.compactionThreshold ?? 0.75,
|
|
4632
|
+
compactionUrl: config.compactionUrl,
|
|
4633
|
+
summarizer: options.summarizer
|
|
4634
|
+
}),
|
|
4635
|
+
maxChars
|
|
4636
|
+
);
|
|
4637
|
+
break;
|
|
4638
|
+
}
|
|
4639
|
+
default:
|
|
4640
|
+
result = truncateToolResults(toLLMMessages(displayMessages), maxChars);
|
|
4641
|
+
}
|
|
4642
|
+
return result;
|
|
4643
|
+
}, [displayMessages, compactionState, strategy, config, options.summarizer]);
|
|
4644
|
+
const tokenUsage = React2.useMemo(() => {
|
|
4645
|
+
const mode = options.tokenEstimation ?? "fast";
|
|
4646
|
+
const current = estimateTokens2(toLLMMessages(displayMessages), mode);
|
|
4647
|
+
const max = config.maxContextTokens ?? 128e3;
|
|
4648
|
+
const threshold = config.compactionThreshold ?? 0.75;
|
|
4649
|
+
const percentage = current / max;
|
|
4650
|
+
return { current, max, percentage, isApproaching: percentage >= threshold };
|
|
4651
|
+
}, [
|
|
4652
|
+
displayMessages,
|
|
4653
|
+
config.maxContextTokens,
|
|
4654
|
+
config.compactionThreshold,
|
|
4655
|
+
options.tokenEstimation
|
|
4656
|
+
]);
|
|
4657
|
+
React2.useEffect(() => {
|
|
4658
|
+
if (config.onTokenUsage && tokenUsage.current > 0) {
|
|
4659
|
+
config.onTokenUsage(tokenUsage);
|
|
4660
|
+
}
|
|
4661
|
+
}, [tokenUsage, config.onTokenUsage]);
|
|
4662
|
+
React2.useEffect(() => {
|
|
4663
|
+
if (config.persistSession) {
|
|
4664
|
+
saveCompactionState(storageKey, {
|
|
4665
|
+
...compactionState,
|
|
4666
|
+
displayMessageCount: displayMessages.length,
|
|
4667
|
+
llmMessageCount: llmMessages.length
|
|
4668
|
+
});
|
|
4669
|
+
}
|
|
4670
|
+
}, [
|
|
4671
|
+
config.persistSession,
|
|
4672
|
+
storageKey,
|
|
4673
|
+
compactionState,
|
|
4674
|
+
displayMessages.length,
|
|
4675
|
+
llmMessages.length
|
|
4676
|
+
]);
|
|
4677
|
+
const isCompactingRef = React2.useRef(false);
|
|
4678
|
+
const [isCompacting, setIsCompacting] = React2.useState(false);
|
|
4679
|
+
React2.useEffect(() => {
|
|
4680
|
+
if (strategy !== "summary-buffer" || options.skipCompaction || isCompactingRef.current || !tokenUsage.isApproaching)
|
|
4681
|
+
return;
|
|
4682
|
+
isCompactingRef.current = true;
|
|
4683
|
+
setIsCompacting(true);
|
|
4684
|
+
runCompaction(displayMessages, compactionState, {
|
|
4685
|
+
recentBuffer: config.recentBuffer ?? 10,
|
|
4686
|
+
tokenBudget: (config.maxContextTokens ?? 128e3) - (config.reserveForResponse ?? 4096),
|
|
4687
|
+
compactionThreshold: config.compactionThreshold ?? 0.75,
|
|
4688
|
+
compactionUrl: config.compactionUrl,
|
|
4689
|
+
summarizer: options.summarizer
|
|
4690
|
+
}).then((result) => {
|
|
4691
|
+
if (result.newSummary) {
|
|
4692
|
+
const event = {
|
|
4693
|
+
type: "auto",
|
|
4694
|
+
compactionCount: compactionState.compactionCount + 1,
|
|
4695
|
+
messagesSummarized: result.messagesSummarized ?? 0,
|
|
4696
|
+
tokensSaved: result.tokensSaved ?? 0,
|
|
4697
|
+
timestamp: Date.now()
|
|
4698
|
+
};
|
|
4699
|
+
setCompactionState((prev) => ({
|
|
4700
|
+
...prev,
|
|
4701
|
+
rollingSummary: result.newSummary,
|
|
4702
|
+
lastCompactionAt: Date.now(),
|
|
4703
|
+
compactionCount: prev.compactionCount + 1,
|
|
4704
|
+
totalTokensSaved: prev.totalTokensSaved + (result.tokensSaved ?? 0)
|
|
4705
|
+
}));
|
|
4706
|
+
config.onCompaction?.(event);
|
|
4707
|
+
}
|
|
4708
|
+
}).finally(() => {
|
|
4709
|
+
isCompactingRef.current = false;
|
|
4710
|
+
setIsCompacting(false);
|
|
4711
|
+
});
|
|
4712
|
+
}, [tokenUsage.isApproaching, strategy]);
|
|
4713
|
+
const compactSession = React2.useCallback(
|
|
4714
|
+
async (instructions) => {
|
|
4715
|
+
if (strategy !== "summary-buffer") return;
|
|
4716
|
+
const result = await runCompaction(displayMessages, compactionState, {
|
|
4717
|
+
recentBuffer: config.recentBuffer ?? 10,
|
|
4718
|
+
tokenBudget: (config.maxContextTokens ?? 128e3) - (config.reserveForResponse ?? 4096),
|
|
4719
|
+
compactionThreshold: config.compactionThreshold ?? 0.75,
|
|
4720
|
+
compactionUrl: config.compactionUrl,
|
|
4721
|
+
summarizer: options.summarizer ? (msgs) => options.summarizer(msgs) : instructions ? (msgs) => fetchWithInstructions(config.compactionUrl, msgs, instructions) : void 0
|
|
4722
|
+
});
|
|
4723
|
+
if (result.newSummary) {
|
|
4724
|
+
const event = {
|
|
4725
|
+
type: "manual",
|
|
4726
|
+
compactionCount: compactionState.compactionCount + 1,
|
|
4727
|
+
messagesSummarized: result.messagesSummarized ?? 0,
|
|
4728
|
+
tokensSaved: result.tokensSaved ?? 0,
|
|
4729
|
+
timestamp: Date.now()
|
|
4730
|
+
};
|
|
4731
|
+
setCompactionState((prev) => ({
|
|
4732
|
+
...prev,
|
|
4733
|
+
rollingSummary: result.newSummary,
|
|
4734
|
+
lastCompactionAt: Date.now(),
|
|
4735
|
+
compactionCount: prev.compactionCount + 1,
|
|
4736
|
+
totalTokensSaved: prev.totalTokensSaved + (result.tokensSaved ?? 0)
|
|
4737
|
+
}));
|
|
4738
|
+
config.onCompaction?.(event);
|
|
4739
|
+
}
|
|
4740
|
+
},
|
|
4741
|
+
[displayMessages, compactionState, config, strategy, options.summarizer]
|
|
4742
|
+
);
|
|
4743
|
+
const addToWorkingMemory = React2.useCallback((fact) => {
|
|
4744
|
+
setCompactionState((prev) => ({
|
|
4745
|
+
...prev,
|
|
4746
|
+
workingMemory: [...prev.workingMemory, fact]
|
|
4747
|
+
}));
|
|
4748
|
+
}, []);
|
|
4749
|
+
const clearWorkingMemory = React2.useCallback(() => {
|
|
4750
|
+
setCompactionState((prev) => ({ ...prev, workingMemory: [] }));
|
|
4751
|
+
}, []);
|
|
4752
|
+
const resetSession = React2.useCallback(async () => {
|
|
4753
|
+
setCompactionState(DEFAULT_COMPACTION_STATE);
|
|
4754
|
+
if (config.persistSession) {
|
|
4755
|
+
await clearSession(storageKey);
|
|
4756
|
+
}
|
|
4757
|
+
}, [config.persistSession, storageKey]);
|
|
4758
|
+
return {
|
|
4759
|
+
displayMessages,
|
|
4760
|
+
llmMessages,
|
|
4761
|
+
tokenUsage,
|
|
4762
|
+
isCompacting,
|
|
4763
|
+
compactionState: {
|
|
4764
|
+
...compactionState,
|
|
4765
|
+
displayMessageCount: displayMessages.length,
|
|
4766
|
+
llmMessageCount: llmMessages.length
|
|
4767
|
+
},
|
|
4768
|
+
compactSession,
|
|
4769
|
+
addToWorkingMemory,
|
|
4770
|
+
clearWorkingMemory,
|
|
4771
|
+
resetSession
|
|
4772
|
+
};
|
|
4773
|
+
}
|
|
4774
|
+
async function fetchWithInstructions(url, messages, instructions) {
|
|
4775
|
+
const res = await fetch(url, {
|
|
4776
|
+
method: "POST",
|
|
4777
|
+
headers: { "Content-Type": "application/json" },
|
|
4778
|
+
body: JSON.stringify({ messages, instructions })
|
|
4779
|
+
});
|
|
4780
|
+
const data = await res.json();
|
|
4781
|
+
return data.summary;
|
|
4782
|
+
}
|
|
4783
|
+
var SkillContext = React2.createContext(null);
|
|
4784
|
+
function useSkillContext() {
|
|
4785
|
+
const ctx = React2.useContext(SkillContext);
|
|
4786
|
+
if (!ctx) {
|
|
4787
|
+
throw new Error("useSkillContext must be used within <SkillProvider>");
|
|
4788
|
+
}
|
|
4789
|
+
return ctx;
|
|
4790
|
+
}
|
|
4791
|
+
function useAIContext(item) {
|
|
4792
|
+
const { addContext, removeContext } = useCopilot();
|
|
4793
|
+
const contextIdRef = React2.useRef(null);
|
|
4794
|
+
const serializedData = typeof item.data === "string" ? item.data : JSON.stringify(item.data);
|
|
4795
|
+
React2.useEffect(() => {
|
|
4796
|
+
const formattedValue = typeof item.data === "string" ? item.data : JSON.stringify(item.data, null, 2);
|
|
4797
|
+
const contextString = item.description ? `${item.description}:
|
|
4798
|
+
${formattedValue}` : `${item.key}:
|
|
4799
|
+
${formattedValue}`;
|
|
4800
|
+
contextIdRef.current = addContext(contextString, item.parentId);
|
|
4801
|
+
return () => {
|
|
4802
|
+
if (contextIdRef.current) {
|
|
4803
|
+
removeContext(contextIdRef.current);
|
|
4804
|
+
contextIdRef.current = null;
|
|
4805
|
+
}
|
|
4806
|
+
};
|
|
4807
|
+
}, [
|
|
4808
|
+
item.key,
|
|
4809
|
+
serializedData,
|
|
4810
|
+
item.description,
|
|
4811
|
+
item.parentId,
|
|
4812
|
+
addContext,
|
|
4813
|
+
removeContext
|
|
4814
|
+
]);
|
|
4815
|
+
return contextIdRef.current ?? void 0;
|
|
4816
|
+
}
|
|
4817
|
+
function useAIContexts(items) {
|
|
4818
|
+
const { addContext, removeContext } = useCopilot();
|
|
4819
|
+
const contextIdsRef = React2.useRef([]);
|
|
4820
|
+
const serializedItems = JSON.stringify(
|
|
4821
|
+
items.map((item) => ({
|
|
4822
|
+
key: item.key,
|
|
4823
|
+
data: item.data,
|
|
4824
|
+
description: item.description,
|
|
4825
|
+
parentId: item.parentId
|
|
4826
|
+
}))
|
|
4827
|
+
);
|
|
4828
|
+
React2.useEffect(() => {
|
|
4829
|
+
contextIdsRef.current.forEach((id) => removeContext(id));
|
|
4830
|
+
contextIdsRef.current = [];
|
|
4831
|
+
const parsedItems = JSON.parse(serializedItems);
|
|
4832
|
+
for (const item of parsedItems) {
|
|
4833
|
+
const formattedValue = typeof item.data === "string" ? item.data : JSON.stringify(item.data, null, 2);
|
|
4834
|
+
const contextString = item.description ? `${item.description}:
|
|
4835
|
+
${formattedValue}` : `${item.key}:
|
|
4836
|
+
${formattedValue}`;
|
|
4837
|
+
const id = addContext(contextString, item.parentId);
|
|
4838
|
+
contextIdsRef.current.push(id);
|
|
4839
|
+
}
|
|
4840
|
+
return () => {
|
|
4841
|
+
contextIdsRef.current.forEach((id) => removeContext(id));
|
|
4842
|
+
contextIdsRef.current = [];
|
|
4843
|
+
};
|
|
4844
|
+
}, [serializedItems, addContext, removeContext]);
|
|
4845
|
+
}
|
|
4846
|
+
function isZodSchema(value) {
|
|
4847
|
+
if (value === null || typeof value !== "object") return false;
|
|
4848
|
+
const obj = value;
|
|
4849
|
+
return "_def" in obj && typeof obj._def === "object" || "_zod" in obj && typeof obj._zod === "object" || "~standard" in obj;
|
|
4850
|
+
}
|
|
4851
|
+
function useTool(config, dependencies = []) {
|
|
4852
|
+
const { registerTool, unregisterTool } = useCopilot();
|
|
4853
|
+
const configRef = React2.useRef(config);
|
|
4854
|
+
configRef.current = config;
|
|
4855
|
+
const inputSchema = React2.useMemo(() => {
|
|
4856
|
+
if (isZodSchema(config.inputSchema)) {
|
|
4857
|
+
return chunkI3SQUNTT_cjs.zodToJsonSchema(config.inputSchema);
|
|
4858
|
+
}
|
|
4859
|
+
return config.inputSchema;
|
|
4860
|
+
}, [config.inputSchema]);
|
|
4861
|
+
React2.useEffect(() => {
|
|
4862
|
+
const tool2 = {
|
|
4863
|
+
name: config.name,
|
|
4864
|
+
description: config.description,
|
|
4865
|
+
location: "client",
|
|
4866
|
+
inputSchema,
|
|
4867
|
+
handler: async (params, context) => {
|
|
4868
|
+
return configRef.current.handler(params, context);
|
|
4869
|
+
},
|
|
4870
|
+
render: config.render,
|
|
4871
|
+
available: config.available ?? true,
|
|
4872
|
+
needsApproval: config.needsApproval,
|
|
4873
|
+
approvalMessage: config.approvalMessage,
|
|
4874
|
+
hidden: config.hidden,
|
|
4875
|
+
deferLoading: config.deferLoading,
|
|
4876
|
+
profiles: config.profiles,
|
|
4877
|
+
searchKeywords: config.searchKeywords,
|
|
4878
|
+
group: config.group,
|
|
4879
|
+
category: config.category,
|
|
4880
|
+
resultConfig: config.resultConfig,
|
|
4881
|
+
title: config.title,
|
|
4882
|
+
executingTitle: config.executingTitle,
|
|
4883
|
+
completedTitle: config.completedTitle,
|
|
4884
|
+
aiResponseMode: config.aiResponseMode,
|
|
4885
|
+
aiContext: config.aiContext
|
|
4886
|
+
};
|
|
4887
|
+
registerTool(tool2);
|
|
4888
|
+
return () => {
|
|
4889
|
+
unregisterTool(config.name);
|
|
4890
|
+
};
|
|
4891
|
+
}, [config.name, inputSchema, ...dependencies]);
|
|
4892
|
+
}
|
|
4893
|
+
function useTools(tools) {
|
|
4894
|
+
const { registerTool, unregisterTool } = useCopilot();
|
|
4895
|
+
const registeredToolsRef = React2.useRef([]);
|
|
4896
|
+
const toolsRef = React2.useRef(tools);
|
|
4897
|
+
toolsRef.current = tools;
|
|
4898
|
+
const toolsKey = Object.keys(tools).sort().join(",");
|
|
4899
|
+
React2.useEffect(() => {
|
|
4900
|
+
const currentTools = toolsRef.current;
|
|
4901
|
+
const toolNames = [];
|
|
4902
|
+
for (const [name, toolDef] of Object.entries(currentTools)) {
|
|
4903
|
+
const fullTool = {
|
|
4904
|
+
...toolDef,
|
|
4905
|
+
name
|
|
4906
|
+
// Use the key as the name
|
|
4907
|
+
};
|
|
4908
|
+
registerTool(fullTool);
|
|
4909
|
+
toolNames.push(name);
|
|
4910
|
+
}
|
|
4911
|
+
registeredToolsRef.current = toolNames;
|
|
4912
|
+
return () => {
|
|
4913
|
+
for (const name of registeredToolsRef.current) {
|
|
4914
|
+
unregisterTool(name);
|
|
4915
|
+
}
|
|
4916
|
+
registeredToolsRef.current = [];
|
|
4917
|
+
};
|
|
4918
|
+
}, [toolsKey]);
|
|
4919
|
+
}
|
|
4920
|
+
function SkillContextInjector({
|
|
4921
|
+
registry,
|
|
4922
|
+
skills
|
|
4923
|
+
}) {
|
|
4924
|
+
const catalog = React2.useMemo(() => registry.buildCatalog(), [skills]);
|
|
4925
|
+
const eagerContent = React2.useMemo(() => registry.buildEagerContent(), [skills]);
|
|
4926
|
+
useAIContext({
|
|
4927
|
+
key: "__skill_catalog__",
|
|
4928
|
+
description: "Skills the AI can load on demand",
|
|
4929
|
+
data: catalog ? `You have access to specialized skills. Call load_skill({ name }) when relevant.
|
|
4930
|
+
|
|
4931
|
+
${catalog}` : ""
|
|
4932
|
+
});
|
|
4933
|
+
useAIContext({
|
|
4934
|
+
key: "__skill_eager__",
|
|
4935
|
+
description: "Always-active skill instructions",
|
|
4936
|
+
data: eagerContent
|
|
4937
|
+
});
|
|
4938
|
+
return null;
|
|
4939
|
+
}
|
|
4940
|
+
function SkillRequestSync({ skills }) {
|
|
4941
|
+
const { setInlineSkills } = useCopilot();
|
|
4942
|
+
React2.useEffect(() => {
|
|
4943
|
+
const inlineSkills = skills.filter((s) => s.source.type === "inline").map((s) => ({
|
|
4944
|
+
name: s.name,
|
|
4945
|
+
description: s.description,
|
|
4946
|
+
content: s.content,
|
|
4947
|
+
strategy: s.strategy
|
|
4948
|
+
}));
|
|
4949
|
+
setInlineSkills(inlineSkills);
|
|
4950
|
+
}, [skills, setInlineSkills]);
|
|
4951
|
+
return null;
|
|
4952
|
+
}
|
|
4953
|
+
function SkillToolRegistrar({
|
|
4954
|
+
registry,
|
|
4955
|
+
skills
|
|
4956
|
+
}) {
|
|
4957
|
+
useTool(
|
|
4958
|
+
{
|
|
4959
|
+
name: "load_skill",
|
|
4960
|
+
description: "Load a skill by name to get full instructions for a specialized task.",
|
|
4961
|
+
inputSchema: {
|
|
4962
|
+
type: "object",
|
|
4963
|
+
properties: {
|
|
4964
|
+
name: {
|
|
4965
|
+
type: "string",
|
|
4966
|
+
description: "The name of the skill to load."
|
|
4967
|
+
}
|
|
4968
|
+
},
|
|
4969
|
+
required: ["name"]
|
|
4970
|
+
},
|
|
4971
|
+
handler: async ({ name }) => {
|
|
4972
|
+
const skill = registry.get(name);
|
|
4973
|
+
if (!skill) {
|
|
4974
|
+
const available = registry.getAuto().map((s) => s.name).join(", ") || "none";
|
|
4975
|
+
return {
|
|
4976
|
+
success: false,
|
|
4977
|
+
error: `Skill "${name}" not found. Available skills: ${available}`
|
|
4978
|
+
};
|
|
4979
|
+
}
|
|
4980
|
+
const sourceTypeMap = {
|
|
4981
|
+
inline: "client-inline",
|
|
4982
|
+
url: "remote-url",
|
|
4983
|
+
file: "server-dir"
|
|
4984
|
+
};
|
|
4985
|
+
return {
|
|
4986
|
+
success: true,
|
|
4987
|
+
name: skill.name,
|
|
4988
|
+
description: skill.description,
|
|
4989
|
+
strategy: skill.strategy ?? "auto",
|
|
4990
|
+
content: skill.content,
|
|
4991
|
+
source: sourceTypeMap[skill.source.type]
|
|
4992
|
+
};
|
|
4993
|
+
}
|
|
4994
|
+
},
|
|
4995
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4996
|
+
[skills]
|
|
4997
|
+
);
|
|
4998
|
+
return null;
|
|
4999
|
+
}
|
|
5000
|
+
function SkillProvider({
|
|
5001
|
+
children,
|
|
5002
|
+
skills: skillsProp
|
|
5003
|
+
}) {
|
|
5004
|
+
const registryRef = React2.useRef(null);
|
|
5005
|
+
if (registryRef.current === null) {
|
|
5006
|
+
registryRef.current = new chunkBJYA5NDL_cjs.SkillRegistry();
|
|
5007
|
+
}
|
|
5008
|
+
const registry = registryRef.current;
|
|
5009
|
+
const [skills, setSkills] = React2.useState([]);
|
|
5010
|
+
React2.useEffect(() => {
|
|
5011
|
+
if (!skillsProp?.length) return;
|
|
5012
|
+
for (const def of skillsProp) {
|
|
5013
|
+
if (def.source.type !== "inline") {
|
|
5014
|
+
console.warn(
|
|
5015
|
+
`[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.`
|
|
5016
|
+
);
|
|
5017
|
+
continue;
|
|
5018
|
+
}
|
|
5019
|
+
const resolved = {
|
|
5020
|
+
...def,
|
|
5021
|
+
content: def.source.content
|
|
5022
|
+
};
|
|
5023
|
+
registry.register(resolved);
|
|
5024
|
+
}
|
|
5025
|
+
setSkills(registry.getAll());
|
|
5026
|
+
return () => {
|
|
5027
|
+
for (const def of skillsProp ?? []) {
|
|
5028
|
+
registry.unregister(def.name);
|
|
2535
5029
|
}
|
|
2536
|
-
|
|
5030
|
+
setSkills(registry.getAll());
|
|
2537
5031
|
};
|
|
2538
|
-
}, [
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
5032
|
+
}, [skillsProp]);
|
|
5033
|
+
const register = React2.useCallback((skill) => {
|
|
5034
|
+
registry.register(skill);
|
|
5035
|
+
setSkills(registry.getAll());
|
|
5036
|
+
}, []);
|
|
5037
|
+
const unregister = React2.useCallback((name) => {
|
|
5038
|
+
registry.unregister(name);
|
|
5039
|
+
setSkills(registry.getAll());
|
|
5040
|
+
}, []);
|
|
5041
|
+
const contextValue = React2.useMemo(
|
|
5042
|
+
() => ({ registry, register, unregister, skills }),
|
|
5043
|
+
[register, unregister, skills]
|
|
5044
|
+
);
|
|
5045
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(SkillContext.Provider, { value: contextValue, children: [
|
|
5046
|
+
/* @__PURE__ */ jsxRuntime.jsx(SkillContextInjector, { registry, skills }),
|
|
5047
|
+
/* @__PURE__ */ jsxRuntime.jsx(SkillToolRegistrar, { registry, skills }),
|
|
5048
|
+
/* @__PURE__ */ jsxRuntime.jsx(SkillRequestSync, { skills }),
|
|
5049
|
+
children
|
|
5050
|
+
] });
|
|
2549
5051
|
}
|
|
2550
5052
|
function MCPConnection({ config }) {
|
|
2551
5053
|
useMCPTools({
|
|
@@ -2559,9 +5061,126 @@ function MCPConnection({ config }) {
|
|
|
2559
5061
|
});
|
|
2560
5062
|
return null;
|
|
2561
5063
|
}
|
|
2562
|
-
var
|
|
5064
|
+
var COMPACTING_MARKER_ID = "__compacting-in-progress__";
|
|
5065
|
+
function MessageHistoryBridge({
|
|
5066
|
+
chatRef
|
|
5067
|
+
}) {
|
|
5068
|
+
const { compactionState, tokenUsage } = useMessageHistory();
|
|
5069
|
+
const ctx = useMessageHistoryContext();
|
|
5070
|
+
const loaderAddedRef = React2.useRef(false);
|
|
5071
|
+
const prevCompactionCountRef = React2.useRef(compactionState.compactionCount);
|
|
5072
|
+
React2.useEffect(() => {
|
|
5073
|
+
if (!tokenUsage.isApproaching) {
|
|
5074
|
+
loaderAddedRef.current = false;
|
|
5075
|
+
return;
|
|
5076
|
+
}
|
|
5077
|
+
if (loaderAddedRef.current) return;
|
|
5078
|
+
const chat = chatRef.current;
|
|
5079
|
+
if (!chat) return;
|
|
5080
|
+
const alreadyAdded = chat.messages.some(
|
|
5081
|
+
(m) => m.id === COMPACTING_MARKER_ID
|
|
5082
|
+
);
|
|
5083
|
+
if (alreadyAdded) return;
|
|
5084
|
+
loaderAddedRef.current = true;
|
|
5085
|
+
const loading = {
|
|
5086
|
+
id: COMPACTING_MARKER_ID,
|
|
5087
|
+
role: "system",
|
|
5088
|
+
content: "Compacting conversation\u2026",
|
|
5089
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
5090
|
+
metadata: { type: "compaction-marker", compacting: true }
|
|
5091
|
+
};
|
|
5092
|
+
chat.setMessages([...chat.messages, loading]);
|
|
5093
|
+
}, [tokenUsage.isApproaching]);
|
|
5094
|
+
React2.useEffect(() => {
|
|
5095
|
+
if (compactionState.compactionCount <= prevCompactionCountRef.current)
|
|
5096
|
+
return;
|
|
5097
|
+
prevCompactionCountRef.current = compactionState.compactionCount;
|
|
5098
|
+
loaderAddedRef.current = false;
|
|
5099
|
+
const chat = chatRef.current;
|
|
5100
|
+
if (!chat) return;
|
|
5101
|
+
const hasLoader = chat.messages.some((m) => m.id === COMPACTING_MARKER_ID);
|
|
5102
|
+
const base = hasLoader ? chat.messages.map(
|
|
5103
|
+
(m) => m.id === COMPACTING_MARKER_ID ? {
|
|
5104
|
+
...m,
|
|
5105
|
+
id: `compaction-marker-${compactionState.compactionCount}`,
|
|
5106
|
+
content: `Conversation compacted \u2014 context window refreshed`,
|
|
5107
|
+
metadata: { type: "compaction-marker", compacting: false }
|
|
5108
|
+
} : m
|
|
5109
|
+
) : [
|
|
5110
|
+
...chat.messages,
|
|
5111
|
+
{
|
|
5112
|
+
id: `compaction-marker-${compactionState.compactionCount}`,
|
|
5113
|
+
role: "system",
|
|
5114
|
+
content: `Conversation compacted \u2014 context window refreshed`,
|
|
5115
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
5116
|
+
metadata: { type: "compaction-marker", compacting: false }
|
|
5117
|
+
}
|
|
5118
|
+
];
|
|
5119
|
+
chat.setMessages(base);
|
|
5120
|
+
}, [compactionState.compactionCount]);
|
|
5121
|
+
const compactionStateRef = React2.useRef(compactionState);
|
|
5122
|
+
compactionStateRef.current = compactionState;
|
|
5123
|
+
const configRef = React2.useRef(ctx.config);
|
|
5124
|
+
configRef.current = ctx.config;
|
|
5125
|
+
React2.useEffect(() => {
|
|
5126
|
+
const chat = chatRef.current;
|
|
5127
|
+
if (!chat) return;
|
|
5128
|
+
chat.setRequestMessageTransform((allMessages) => {
|
|
5129
|
+
if (allMessages.length === 0) return allMessages;
|
|
5130
|
+
let lastUserIdx = -1;
|
|
5131
|
+
for (let i = allMessages.length - 1; i >= 0; i--) {
|
|
5132
|
+
if (allMessages[i].role === "user") {
|
|
5133
|
+
lastUserIdx = i;
|
|
5134
|
+
break;
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
if (lastUserIdx === -1) return allMessages;
|
|
5138
|
+
const historyMessages = allMessages.slice(0, lastUserIdx);
|
|
5139
|
+
const currentTurn = allMessages.slice(lastUserIdx);
|
|
5140
|
+
if (historyMessages.length === 0) return allMessages;
|
|
5141
|
+
const cfg = configRef.current;
|
|
5142
|
+
const cs = compactionStateRef.current;
|
|
5143
|
+
const recentBuffer = cfg.recentBuffer ?? 10;
|
|
5144
|
+
const isCompactionMsg = (m) => m.metadata?.["type"] === "compaction-marker";
|
|
5145
|
+
const windowedHistory = [];
|
|
5146
|
+
if (cs.workingMemory.length > 0) {
|
|
5147
|
+
windowedHistory.push({
|
|
5148
|
+
id: "working-memory",
|
|
5149
|
+
role: "system",
|
|
5150
|
+
content: `[Working memory \u2014 always active]
|
|
5151
|
+
${cs.workingMemory.join("\n")}`,
|
|
5152
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
5153
|
+
});
|
|
5154
|
+
}
|
|
5155
|
+
if (cs.rollingSummary) {
|
|
5156
|
+
windowedHistory.push({
|
|
5157
|
+
id: "rolling-summary",
|
|
5158
|
+
role: "system",
|
|
5159
|
+
content: `[Previous conversation summary]
|
|
5160
|
+
${cs.rollingSummary}`,
|
|
5161
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
5162
|
+
});
|
|
5163
|
+
}
|
|
5164
|
+
const systemMsgs = historyMessages.filter(
|
|
5165
|
+
(m) => m.role === "system" && !isCompactionMsg(m)
|
|
5166
|
+
);
|
|
5167
|
+
windowedHistory.push(...systemMsgs);
|
|
5168
|
+
const conversationMsgs = historyMessages.filter(
|
|
5169
|
+
(m) => m.role !== "system"
|
|
5170
|
+
);
|
|
5171
|
+
const recentStart = Math.max(0, conversationMsgs.length - recentBuffer);
|
|
5172
|
+
windowedHistory.push(...conversationMsgs.slice(recentStart));
|
|
5173
|
+
return [...windowedHistory, ...currentTurn];
|
|
5174
|
+
});
|
|
5175
|
+
return () => {
|
|
5176
|
+
chatRef.current?.setRequestMessageTransform(null);
|
|
5177
|
+
};
|
|
5178
|
+
}, []);
|
|
5179
|
+
return null;
|
|
5180
|
+
}
|
|
5181
|
+
var CopilotContext = React2.createContext(null);
|
|
2563
5182
|
function useCopilot() {
|
|
2564
|
-
const context =
|
|
5183
|
+
const context = React2.useContext(CopilotContext);
|
|
2565
5184
|
if (!context) {
|
|
2566
5185
|
throw new Error("useCopilot must be used within CopilotProvider");
|
|
2567
5186
|
}
|
|
@@ -2582,23 +5201,26 @@ function CopilotProvider({
|
|
|
2582
5201
|
debug = false,
|
|
2583
5202
|
maxIterations,
|
|
2584
5203
|
maxIterationsMessage,
|
|
2585
|
-
mcpServers
|
|
5204
|
+
mcpServers,
|
|
5205
|
+
optimization,
|
|
5206
|
+
messageHistory,
|
|
5207
|
+
skills
|
|
2586
5208
|
}) {
|
|
2587
|
-
const debugLog =
|
|
2588
|
-
(
|
|
2589
|
-
|
|
5209
|
+
const debugLog = React2.useCallback(
|
|
5210
|
+
(action, data) => {
|
|
5211
|
+
chunkI3SQUNTT_cjs.createLogger("provider", () => debug ?? false)(action, data);
|
|
2590
5212
|
},
|
|
2591
5213
|
[debug]
|
|
2592
5214
|
);
|
|
2593
|
-
|
|
5215
|
+
React2.useEffect(() => {
|
|
2594
5216
|
if (toolsConfig && (toolsConfig.screenshot || toolsConfig.console || toolsConfig.network)) {
|
|
2595
5217
|
console.warn(
|
|
2596
5218
|
"[Copilot SDK] The `tools` prop is deprecated. Use the `useTools` hook instead."
|
|
2597
5219
|
);
|
|
2598
5220
|
}
|
|
2599
5221
|
}, [toolsConfig]);
|
|
2600
|
-
const [toolExecutions, setToolExecutions] =
|
|
2601
|
-
const chatRef =
|
|
5222
|
+
const [toolExecutions, setToolExecutions] = React2.useState([]);
|
|
5223
|
+
const chatRef = React2.useRef(null);
|
|
2602
5224
|
if (chatRef.current !== null && chatRef.current.disposed) {
|
|
2603
5225
|
chatRef.current.revive();
|
|
2604
5226
|
debugLog("Revived disposed instance (React StrictMode)");
|
|
@@ -2626,7 +5248,8 @@ function CopilotProvider({
|
|
|
2626
5248
|
body,
|
|
2627
5249
|
debug,
|
|
2628
5250
|
maxIterations,
|
|
2629
|
-
maxIterationsMessage
|
|
5251
|
+
maxIterationsMessage,
|
|
5252
|
+
optimization
|
|
2630
5253
|
},
|
|
2631
5254
|
{
|
|
2632
5255
|
onToolExecutionsChange: (executions) => {
|
|
@@ -2636,92 +5259,111 @@ function CopilotProvider({
|
|
|
2636
5259
|
onApprovalRequired: (execution) => {
|
|
2637
5260
|
debugLog("Tool approval required:", execution.name);
|
|
2638
5261
|
},
|
|
5262
|
+
onContextUsageChange: (usage) => {
|
|
5263
|
+
setContextUsage(usage);
|
|
5264
|
+
},
|
|
2639
5265
|
onError: (error2) => {
|
|
2640
5266
|
if (error2) onError?.(error2);
|
|
2641
5267
|
}
|
|
2642
5268
|
}
|
|
2643
5269
|
);
|
|
2644
5270
|
}
|
|
2645
|
-
|
|
5271
|
+
React2.useEffect(() => {
|
|
2646
5272
|
if (chatRef.current && systemPrompt !== void 0) {
|
|
2647
5273
|
chatRef.current.setSystemPrompt(systemPrompt);
|
|
2648
5274
|
debugLog("System prompt updated from prop");
|
|
2649
5275
|
}
|
|
2650
5276
|
}, [systemPrompt, debugLog]);
|
|
2651
|
-
|
|
5277
|
+
React2.useEffect(() => {
|
|
2652
5278
|
if (chatRef.current && headers !== void 0) {
|
|
2653
5279
|
chatRef.current.setHeaders(headers);
|
|
2654
5280
|
debugLog("Headers config updated from prop");
|
|
2655
5281
|
}
|
|
2656
5282
|
}, [headers, debugLog]);
|
|
2657
|
-
|
|
5283
|
+
React2.useEffect(() => {
|
|
2658
5284
|
if (chatRef.current && body !== void 0) {
|
|
2659
5285
|
chatRef.current.setBody(body);
|
|
2660
5286
|
debugLog("Body config updated from prop");
|
|
2661
5287
|
}
|
|
2662
5288
|
}, [body, debugLog]);
|
|
2663
|
-
|
|
5289
|
+
React2.useEffect(() => {
|
|
2664
5290
|
if (chatRef.current && runtimeUrl !== void 0) {
|
|
2665
5291
|
chatRef.current.setUrl(runtimeUrl);
|
|
2666
5292
|
debugLog("URL config updated from prop");
|
|
2667
5293
|
}
|
|
2668
5294
|
}, [runtimeUrl, debugLog]);
|
|
2669
|
-
const
|
|
5295
|
+
const EMPTY_MESSAGES = React2.useRef([]);
|
|
5296
|
+
const getMessagesSnapshot = React2.useCallback(() => chatRef.current.messages, []);
|
|
5297
|
+
const getServerMessagesSnapshot = React2.useCallback(
|
|
5298
|
+
() => EMPTY_MESSAGES.current,
|
|
5299
|
+
[]
|
|
5300
|
+
);
|
|
5301
|
+
const getStatusSnapshot = React2.useCallback(() => chatRef.current.status, []);
|
|
5302
|
+
const getErrorSnapshot = React2.useCallback(() => chatRef.current.error, []);
|
|
5303
|
+
const messages = React2.useSyncExternalStore(
|
|
2670
5304
|
chatRef.current.subscribe,
|
|
2671
|
-
|
|
2672
|
-
|
|
5305
|
+
getMessagesSnapshot,
|
|
5306
|
+
getServerMessagesSnapshot
|
|
2673
5307
|
);
|
|
2674
|
-
const status =
|
|
5308
|
+
const status = React2.useSyncExternalStore(
|
|
2675
5309
|
chatRef.current.subscribe,
|
|
2676
|
-
|
|
5310
|
+
getStatusSnapshot,
|
|
2677
5311
|
() => "ready"
|
|
2678
5312
|
);
|
|
2679
|
-
const errorFromChat =
|
|
5313
|
+
const errorFromChat = React2.useSyncExternalStore(
|
|
2680
5314
|
chatRef.current.subscribe,
|
|
2681
|
-
|
|
5315
|
+
getErrorSnapshot,
|
|
2682
5316
|
() => void 0
|
|
2683
5317
|
);
|
|
2684
5318
|
const error = errorFromChat ?? null;
|
|
2685
5319
|
const isLoading = status === "streaming" || status === "submitted";
|
|
2686
|
-
const registerTool =
|
|
5320
|
+
const registerTool = React2.useCallback((tool2) => {
|
|
2687
5321
|
chatRef.current?.registerTool(tool2);
|
|
2688
5322
|
}, []);
|
|
2689
|
-
const unregisterTool =
|
|
5323
|
+
const unregisterTool = React2.useCallback((name) => {
|
|
2690
5324
|
chatRef.current?.unregisterTool(name);
|
|
2691
5325
|
}, []);
|
|
2692
|
-
const approveToolExecution =
|
|
5326
|
+
const approveToolExecution = React2.useCallback(
|
|
2693
5327
|
(id, extraData, permissionLevel) => {
|
|
2694
5328
|
chatRef.current?.approveToolExecution(id, extraData, permissionLevel);
|
|
2695
5329
|
},
|
|
2696
5330
|
[]
|
|
2697
5331
|
);
|
|
2698
|
-
const rejectToolExecution =
|
|
5332
|
+
const rejectToolExecution = React2.useCallback(
|
|
2699
5333
|
(id, reason, permissionLevel) => {
|
|
2700
5334
|
chatRef.current?.rejectToolExecution(id, reason, permissionLevel);
|
|
2701
5335
|
},
|
|
2702
5336
|
[]
|
|
2703
5337
|
);
|
|
2704
|
-
const registeredTools =
|
|
2705
|
-
|
|
2706
|
-
|
|
5338
|
+
const registeredTools = React2.useMemo(
|
|
5339
|
+
() => chatRef.current?.tools ?? [],
|
|
5340
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
5341
|
+
[toolExecutions]
|
|
5342
|
+
// re-derive when tool executions change (tools change alongside)
|
|
2707
5343
|
);
|
|
2708
|
-
const
|
|
2709
|
-
|
|
2710
|
-
|
|
5344
|
+
const pendingApprovals = React2.useMemo(
|
|
5345
|
+
() => toolExecutions.filter((e) => e.approvalStatus === "required"),
|
|
5346
|
+
[toolExecutions]
|
|
5347
|
+
);
|
|
5348
|
+
const actionsRef = React2.useRef(/* @__PURE__ */ new Map());
|
|
5349
|
+
const [actionsVersion, setActionsVersion] = React2.useState(0);
|
|
5350
|
+
const registerAction = React2.useCallback((action) => {
|
|
2711
5351
|
actionsRef.current.set(action.name, action);
|
|
2712
5352
|
setActionsVersion((v) => v + 1);
|
|
2713
5353
|
}, []);
|
|
2714
|
-
const unregisterAction =
|
|
5354
|
+
const unregisterAction = React2.useCallback((name) => {
|
|
2715
5355
|
actionsRef.current.delete(name);
|
|
2716
5356
|
setActionsVersion((v) => v + 1);
|
|
2717
5357
|
}, []);
|
|
2718
|
-
const registeredActions =
|
|
5358
|
+
const registeredActions = React2.useMemo(
|
|
2719
5359
|
() => Array.from(actionsRef.current.values()),
|
|
2720
5360
|
[actionsVersion]
|
|
2721
5361
|
);
|
|
2722
|
-
const contextTreeRef =
|
|
2723
|
-
const contextIdCounter =
|
|
2724
|
-
const
|
|
5362
|
+
const contextTreeRef = React2.useRef([]);
|
|
5363
|
+
const contextIdCounter = React2.useRef(0);
|
|
5364
|
+
const [contextChars, setContextChars] = React2.useState(0);
|
|
5365
|
+
const [contextUsage, setContextUsage] = React2.useState(null);
|
|
5366
|
+
const addContext = React2.useCallback(
|
|
2725
5367
|
(context, parentId) => {
|
|
2726
5368
|
const id = `ctx-${++contextIdCounter.current}`;
|
|
2727
5369
|
contextTreeRef.current = addNode(
|
|
@@ -2731,55 +5373,95 @@ function CopilotProvider({
|
|
|
2731
5373
|
);
|
|
2732
5374
|
const contextString = printTree(contextTreeRef.current);
|
|
2733
5375
|
chatRef.current?.setContext(contextString);
|
|
5376
|
+
setContextChars(contextString.length);
|
|
2734
5377
|
debugLog("Context added:", id);
|
|
2735
5378
|
return id;
|
|
2736
5379
|
},
|
|
2737
5380
|
[debugLog]
|
|
2738
5381
|
);
|
|
2739
|
-
const removeContext =
|
|
5382
|
+
const removeContext = React2.useCallback(
|
|
2740
5383
|
(id) => {
|
|
2741
5384
|
contextTreeRef.current = removeNode(contextTreeRef.current, id);
|
|
2742
5385
|
const contextString = printTree(contextTreeRef.current);
|
|
2743
5386
|
chatRef.current?.setContext(contextString);
|
|
5387
|
+
setContextChars(contextString.length);
|
|
2744
5388
|
debugLog("Context removed:", id);
|
|
2745
5389
|
},
|
|
2746
5390
|
[debugLog]
|
|
2747
5391
|
);
|
|
2748
|
-
const setSystemPrompt =
|
|
5392
|
+
const setSystemPrompt = React2.useCallback(
|
|
2749
5393
|
(prompt) => {
|
|
2750
5394
|
chatRef.current?.setSystemPrompt(prompt);
|
|
2751
5395
|
debugLog("System prompt updated via function");
|
|
2752
5396
|
},
|
|
2753
5397
|
[debugLog]
|
|
2754
5398
|
);
|
|
2755
|
-
const
|
|
5399
|
+
const setInlineSkills = React2.useCallback(
|
|
5400
|
+
(skills2) => {
|
|
5401
|
+
chatRef.current?.setInlineSkills(skills2);
|
|
5402
|
+
debugLog("Inline skills updated", { count: skills2.length });
|
|
5403
|
+
},
|
|
5404
|
+
[debugLog]
|
|
5405
|
+
);
|
|
5406
|
+
const sendMessage = React2.useCallback(
|
|
2756
5407
|
async (content, attachments) => {
|
|
2757
5408
|
debugLog("Sending message:", content);
|
|
2758
5409
|
await chatRef.current?.sendMessage(content, attachments);
|
|
2759
5410
|
},
|
|
2760
5411
|
[debugLog]
|
|
2761
5412
|
);
|
|
2762
|
-
const stop =
|
|
5413
|
+
const stop = React2.useCallback(() => {
|
|
2763
5414
|
chatRef.current?.stop();
|
|
2764
5415
|
}, []);
|
|
2765
|
-
const clearMessages =
|
|
5416
|
+
const clearMessages = React2.useCallback(() => {
|
|
2766
5417
|
chatRef.current?.clearMessages();
|
|
2767
5418
|
}, []);
|
|
2768
|
-
const setMessages =
|
|
5419
|
+
const setMessages = React2.useCallback((messages2) => {
|
|
2769
5420
|
chatRef.current?.setMessages(messages2);
|
|
2770
5421
|
}, []);
|
|
2771
|
-
const regenerate =
|
|
5422
|
+
const regenerate = React2.useCallback(async (messageId) => {
|
|
2772
5423
|
await chatRef.current?.regenerate(messageId);
|
|
2773
5424
|
}, []);
|
|
2774
|
-
|
|
5425
|
+
const switchBranch = React2.useCallback((messageId) => {
|
|
5426
|
+
chatRef.current?.switchBranch(messageId);
|
|
5427
|
+
}, []);
|
|
5428
|
+
const getBranchInfo = React2.useCallback(
|
|
5429
|
+
(messageId) => chatRef.current?.getBranchInfo(messageId) ?? null,
|
|
5430
|
+
[]
|
|
5431
|
+
);
|
|
5432
|
+
const editMessage = React2.useCallback(
|
|
5433
|
+
async (messageId, newContent) => {
|
|
5434
|
+
await chatRef.current?.sendMessage(newContent, void 0, {
|
|
5435
|
+
editMessageId: messageId
|
|
5436
|
+
});
|
|
5437
|
+
},
|
|
5438
|
+
[]
|
|
5439
|
+
);
|
|
5440
|
+
const getHasBranchesSnapshot = React2.useCallback(
|
|
5441
|
+
() => chatRef.current.hasBranches,
|
|
5442
|
+
[]
|
|
5443
|
+
);
|
|
5444
|
+
const hasBranches = React2.useSyncExternalStore(
|
|
5445
|
+
chatRef.current.subscribe,
|
|
5446
|
+
getHasBranchesSnapshot,
|
|
5447
|
+
() => false
|
|
5448
|
+
);
|
|
5449
|
+
const getAllMessages = React2.useCallback(
|
|
5450
|
+
() => chatRef.current?.getAllMessages?.() ?? [],
|
|
5451
|
+
[]
|
|
5452
|
+
);
|
|
5453
|
+
React2.useEffect(() => {
|
|
2775
5454
|
if (onMessagesChange && messages.length > 0) {
|
|
2776
|
-
const
|
|
5455
|
+
const allUIMessages = chatRef.current?.getAllMessages?.() ?? messages;
|
|
5456
|
+
const coreMessages = allUIMessages.map((m) => ({
|
|
2777
5457
|
id: m.id,
|
|
2778
5458
|
role: m.role,
|
|
2779
5459
|
content: m.content,
|
|
2780
5460
|
created_at: m.createdAt,
|
|
2781
5461
|
tool_calls: m.toolCalls,
|
|
2782
5462
|
tool_call_id: m.toolCallId,
|
|
5463
|
+
parent_id: m.parentId,
|
|
5464
|
+
children_ids: m.childrenIds,
|
|
2783
5465
|
metadata: {
|
|
2784
5466
|
attachments: m.attachments,
|
|
2785
5467
|
thinking: m.thinking
|
|
@@ -2788,17 +5470,17 @@ function CopilotProvider({
|
|
|
2788
5470
|
onMessagesChange(coreMessages);
|
|
2789
5471
|
}
|
|
2790
5472
|
}, [messages, onMessagesChange]);
|
|
2791
|
-
|
|
5473
|
+
React2.useEffect(() => {
|
|
2792
5474
|
if (error && onError) {
|
|
2793
5475
|
onError(error);
|
|
2794
5476
|
}
|
|
2795
5477
|
}, [error, onError]);
|
|
2796
|
-
|
|
5478
|
+
React2.useEffect(() => {
|
|
2797
5479
|
return () => {
|
|
2798
5480
|
chatRef.current?.dispose();
|
|
2799
5481
|
};
|
|
2800
5482
|
}, []);
|
|
2801
|
-
const contextValue =
|
|
5483
|
+
const contextValue = React2.useMemo(
|
|
2802
5484
|
() => ({
|
|
2803
5485
|
// Chat state
|
|
2804
5486
|
messages,
|
|
@@ -2811,6 +5493,12 @@ function CopilotProvider({
|
|
|
2811
5493
|
clearMessages,
|
|
2812
5494
|
setMessages,
|
|
2813
5495
|
regenerate,
|
|
5496
|
+
// Branching
|
|
5497
|
+
switchBranch,
|
|
5498
|
+
getBranchInfo,
|
|
5499
|
+
editMessage,
|
|
5500
|
+
hasBranches,
|
|
5501
|
+
getAllMessages,
|
|
2814
5502
|
// Tool execution
|
|
2815
5503
|
registerTool,
|
|
2816
5504
|
unregisterTool,
|
|
@@ -2826,8 +5514,12 @@ function CopilotProvider({
|
|
|
2826
5514
|
// AI Context
|
|
2827
5515
|
addContext,
|
|
2828
5516
|
removeContext,
|
|
5517
|
+
contextChars,
|
|
5518
|
+
contextUsage,
|
|
2829
5519
|
// System Prompt
|
|
2830
5520
|
setSystemPrompt,
|
|
5521
|
+
// Skills
|
|
5522
|
+
setInlineSkills,
|
|
2831
5523
|
// Config
|
|
2832
5524
|
threadId,
|
|
2833
5525
|
runtimeUrl,
|
|
@@ -2843,6 +5535,11 @@ function CopilotProvider({
|
|
|
2843
5535
|
clearMessages,
|
|
2844
5536
|
setMessages,
|
|
2845
5537
|
regenerate,
|
|
5538
|
+
switchBranch,
|
|
5539
|
+
getBranchInfo,
|
|
5540
|
+
editMessage,
|
|
5541
|
+
hasBranches,
|
|
5542
|
+
getAllMessages,
|
|
2846
5543
|
registerTool,
|
|
2847
5544
|
unregisterTool,
|
|
2848
5545
|
registeredTools,
|
|
@@ -2855,20 +5552,45 @@ function CopilotProvider({
|
|
|
2855
5552
|
registeredActions,
|
|
2856
5553
|
addContext,
|
|
2857
5554
|
removeContext,
|
|
5555
|
+
contextChars,
|
|
5556
|
+
contextUsage,
|
|
2858
5557
|
setSystemPrompt,
|
|
5558
|
+
setInlineSkills,
|
|
2859
5559
|
threadId,
|
|
2860
5560
|
runtimeUrl,
|
|
2861
5561
|
toolsConfig
|
|
2862
5562
|
]
|
|
2863
5563
|
);
|
|
2864
|
-
|
|
5564
|
+
const messageHistoryContextValue = React2__default.default.useMemo(
|
|
5565
|
+
() => ({
|
|
5566
|
+
config: { ...defaultMessageHistoryConfig, ...messageHistory },
|
|
5567
|
+
tokenUsage: {
|
|
5568
|
+
current: 0,
|
|
5569
|
+
max: messageHistory?.maxContextTokens ?? 128e3,
|
|
5570
|
+
percentage: 0,
|
|
5571
|
+
isApproaching: false
|
|
5572
|
+
},
|
|
5573
|
+
compactionState: {
|
|
5574
|
+
rollingSummary: null,
|
|
5575
|
+
lastCompactionAt: null,
|
|
5576
|
+
compactionCount: 0,
|
|
5577
|
+
totalTokensSaved: 0,
|
|
5578
|
+
workingMemory: [],
|
|
5579
|
+
displayMessageCount: 0,
|
|
5580
|
+
llmMessageCount: 0
|
|
5581
|
+
}
|
|
5582
|
+
}),
|
|
5583
|
+
[messageHistory]
|
|
5584
|
+
);
|
|
5585
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MessageHistoryContext.Provider, { value: messageHistoryContextValue, children: /* @__PURE__ */ jsxRuntime.jsxs(CopilotContext.Provider, { value: contextValue, children: [
|
|
2865
5586
|
mcpServers?.map((config) => /* @__PURE__ */ jsxRuntime.jsx(MCPConnection, { config }, config.name)),
|
|
2866
|
-
|
|
2867
|
-
|
|
5587
|
+
messageHistory?.strategy && messageHistory.strategy !== "none" && /* @__PURE__ */ jsxRuntime.jsx(MessageHistoryBridge, { chatRef }),
|
|
5588
|
+
skills ? /* @__PURE__ */ jsxRuntime.jsx(SkillProvider, { skills, children }) : children
|
|
5589
|
+
] }) });
|
|
2868
5590
|
}
|
|
2869
5591
|
function useAIActions(actions) {
|
|
2870
5592
|
const { registerAction, unregisterAction } = useCopilot();
|
|
2871
|
-
|
|
5593
|
+
React2.useEffect(() => {
|
|
2872
5594
|
for (const action of actions) {
|
|
2873
5595
|
registerAction(action);
|
|
2874
5596
|
}
|
|
@@ -2882,61 +5604,6 @@ function useAIActions(actions) {
|
|
|
2882
5604
|
function useAIAction(action) {
|
|
2883
5605
|
useAIActions([action]);
|
|
2884
5606
|
}
|
|
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
5607
|
function useAITools(options = {}) {
|
|
2941
5608
|
const {
|
|
2942
5609
|
screenshot = false,
|
|
@@ -2949,27 +5616,27 @@ function useAITools(options = {}) {
|
|
|
2949
5616
|
onConsentRequest,
|
|
2950
5617
|
autoStart = true
|
|
2951
5618
|
} = options;
|
|
2952
|
-
const [isEnabled] =
|
|
2953
|
-
const [activeCaptures, setActiveCaptures] =
|
|
5619
|
+
const [isEnabled] = React2.useState(screenshot || consoleCapture || network);
|
|
5620
|
+
const [activeCaptures, setActiveCaptures] = React2.useState({
|
|
2954
5621
|
console: false,
|
|
2955
5622
|
network: false
|
|
2956
5623
|
});
|
|
2957
|
-
const [pendingConsent, setPendingConsent] =
|
|
2958
|
-
const consentResolverRef =
|
|
2959
|
-
const rememberedConsentRef =
|
|
2960
|
-
|
|
5624
|
+
const [pendingConsent, setPendingConsent] = React2.useState(null);
|
|
5625
|
+
const consentResolverRef = React2.useRef(null);
|
|
5626
|
+
const rememberedConsentRef = React2.useRef(/* @__PURE__ */ new Set());
|
|
5627
|
+
React2.useEffect(() => {
|
|
2961
5628
|
if (!autoStart || !isEnabled) return;
|
|
2962
|
-
if (consoleCapture && !
|
|
2963
|
-
|
|
5629
|
+
if (consoleCapture && !chunkI3SQUNTT_cjs.isConsoleCaptureActive()) {
|
|
5630
|
+
chunkI3SQUNTT_cjs.startConsoleCapture(consoleOptions);
|
|
2964
5631
|
setActiveCaptures((prev) => ({ ...prev, console: true }));
|
|
2965
5632
|
}
|
|
2966
|
-
if (network && !
|
|
2967
|
-
|
|
5633
|
+
if (network && !chunkI3SQUNTT_cjs.isNetworkCaptureActive()) {
|
|
5634
|
+
chunkI3SQUNTT_cjs.startNetworkCapture(networkOptions);
|
|
2968
5635
|
setActiveCaptures((prev) => ({ ...prev, network: true }));
|
|
2969
5636
|
}
|
|
2970
5637
|
return () => {
|
|
2971
|
-
|
|
2972
|
-
|
|
5638
|
+
chunkI3SQUNTT_cjs.stopConsoleCapture();
|
|
5639
|
+
chunkI3SQUNTT_cjs.stopNetworkCapture();
|
|
2973
5640
|
};
|
|
2974
5641
|
}, [
|
|
2975
5642
|
autoStart,
|
|
@@ -2979,39 +5646,39 @@ function useAITools(options = {}) {
|
|
|
2979
5646
|
consoleOptions,
|
|
2980
5647
|
networkOptions
|
|
2981
5648
|
]);
|
|
2982
|
-
const captureScreenshotFn =
|
|
5649
|
+
const captureScreenshotFn = React2.useCallback(
|
|
2983
5650
|
async (opts) => {
|
|
2984
5651
|
if (!screenshot) {
|
|
2985
5652
|
throw new Error("Screenshot capture is not enabled");
|
|
2986
5653
|
}
|
|
2987
|
-
if (!
|
|
5654
|
+
if (!chunkI3SQUNTT_cjs.isScreenshotSupported()) {
|
|
2988
5655
|
throw new Error(
|
|
2989
5656
|
"Screenshot capture is not supported in this environment"
|
|
2990
5657
|
);
|
|
2991
5658
|
}
|
|
2992
|
-
return
|
|
5659
|
+
return chunkI3SQUNTT_cjs.captureScreenshot({ ...screenshotOptions, ...opts });
|
|
2993
5660
|
},
|
|
2994
5661
|
[screenshot, screenshotOptions]
|
|
2995
5662
|
);
|
|
2996
|
-
const getConsoleLogsFn =
|
|
5663
|
+
const getConsoleLogsFn = React2.useCallback(
|
|
2997
5664
|
(opts) => {
|
|
2998
5665
|
if (!consoleCapture) {
|
|
2999
5666
|
return { logs: [], totalCaptured: 0 };
|
|
3000
5667
|
}
|
|
3001
|
-
return
|
|
5668
|
+
return chunkI3SQUNTT_cjs.getConsoleLogs({ ...consoleOptions, ...opts });
|
|
3002
5669
|
},
|
|
3003
5670
|
[consoleCapture, consoleOptions]
|
|
3004
5671
|
);
|
|
3005
|
-
const getNetworkRequestsFn =
|
|
5672
|
+
const getNetworkRequestsFn = React2.useCallback(
|
|
3006
5673
|
(opts) => {
|
|
3007
5674
|
if (!network) {
|
|
3008
5675
|
return { requests: [], totalCaptured: 0 };
|
|
3009
5676
|
}
|
|
3010
|
-
return
|
|
5677
|
+
return chunkI3SQUNTT_cjs.getNetworkRequests({ ...networkOptions, ...opts });
|
|
3011
5678
|
},
|
|
3012
5679
|
[network, networkOptions]
|
|
3013
5680
|
);
|
|
3014
|
-
const requestConsent =
|
|
5681
|
+
const requestConsent = React2.useCallback(
|
|
3015
5682
|
async (tools, reason = "") => {
|
|
3016
5683
|
const enabledTools = tools.filter((tool2) => {
|
|
3017
5684
|
if (tool2 === "screenshot") return screenshot;
|
|
@@ -3059,14 +5726,14 @@ function useAITools(options = {}) {
|
|
|
3059
5726
|
},
|
|
3060
5727
|
[screenshot, consoleCapture, network, requireConsent, onConsentRequest]
|
|
3061
5728
|
);
|
|
3062
|
-
const respondToConsent =
|
|
5729
|
+
const respondToConsent = React2.useCallback((response) => {
|
|
3063
5730
|
if (consentResolverRef.current) {
|
|
3064
5731
|
consentResolverRef.current(response);
|
|
3065
5732
|
consentResolverRef.current = null;
|
|
3066
5733
|
}
|
|
3067
5734
|
setPendingConsent(null);
|
|
3068
5735
|
}, []);
|
|
3069
|
-
const captureContext =
|
|
5736
|
+
const captureContext = React2.useCallback(
|
|
3070
5737
|
async (tools) => {
|
|
3071
5738
|
const toolsToCapture = tools || ["screenshot", "console", "network"];
|
|
3072
5739
|
const context = {
|
|
@@ -3099,26 +5766,26 @@ function useAITools(options = {}) {
|
|
|
3099
5766
|
getNetworkRequestsFn
|
|
3100
5767
|
]
|
|
3101
5768
|
);
|
|
3102
|
-
const startCapturing =
|
|
3103
|
-
if (consoleCapture && !
|
|
3104
|
-
|
|
5769
|
+
const startCapturing = React2.useCallback(() => {
|
|
5770
|
+
if (consoleCapture && !chunkI3SQUNTT_cjs.isConsoleCaptureActive()) {
|
|
5771
|
+
chunkI3SQUNTT_cjs.startConsoleCapture(consoleOptions);
|
|
3105
5772
|
setActiveCaptures((prev) => ({ ...prev, console: true }));
|
|
3106
5773
|
}
|
|
3107
|
-
if (network && !
|
|
3108
|
-
|
|
5774
|
+
if (network && !chunkI3SQUNTT_cjs.isNetworkCaptureActive()) {
|
|
5775
|
+
chunkI3SQUNTT_cjs.startNetworkCapture(networkOptions);
|
|
3109
5776
|
setActiveCaptures((prev) => ({ ...prev, network: true }));
|
|
3110
5777
|
}
|
|
3111
5778
|
}, [consoleCapture, network, consoleOptions, networkOptions]);
|
|
3112
|
-
const stopCapturing =
|
|
3113
|
-
|
|
3114
|
-
|
|
5779
|
+
const stopCapturing = React2.useCallback(() => {
|
|
5780
|
+
chunkI3SQUNTT_cjs.stopConsoleCapture();
|
|
5781
|
+
chunkI3SQUNTT_cjs.stopNetworkCapture();
|
|
3115
5782
|
setActiveCaptures({ console: false, network: false });
|
|
3116
5783
|
}, []);
|
|
3117
|
-
const clearCaptured =
|
|
3118
|
-
|
|
3119
|
-
|
|
5784
|
+
const clearCaptured = React2.useCallback(() => {
|
|
5785
|
+
chunkI3SQUNTT_cjs.clearConsoleLogs();
|
|
5786
|
+
chunkI3SQUNTT_cjs.clearNetworkRequests();
|
|
3120
5787
|
}, []);
|
|
3121
|
-
const formatForAI =
|
|
5788
|
+
const formatForAI = React2.useCallback((context) => {
|
|
3122
5789
|
const parts = [];
|
|
3123
5790
|
if (context.screenshot) {
|
|
3124
5791
|
parts.push(
|
|
@@ -3126,16 +5793,16 @@ function useAITools(options = {}) {
|
|
|
3126
5793
|
);
|
|
3127
5794
|
}
|
|
3128
5795
|
if (context.consoleLogs && context.consoleLogs.logs.length > 0) {
|
|
3129
|
-
parts.push(
|
|
5796
|
+
parts.push(chunkI3SQUNTT_cjs.formatLogsForAI(context.consoleLogs.logs));
|
|
3130
5797
|
}
|
|
3131
5798
|
if (context.networkRequests && context.networkRequests.requests.length > 0) {
|
|
3132
|
-
parts.push(
|
|
5799
|
+
parts.push(chunkI3SQUNTT_cjs.formatRequestsForAI(context.networkRequests.requests));
|
|
3133
5800
|
}
|
|
3134
5801
|
return parts.length > 0 ? parts.join("\n\n---\n\n") : "No context captured.";
|
|
3135
5802
|
}, []);
|
|
3136
|
-
const detectIntentFn =
|
|
5803
|
+
const detectIntentFn = React2.useCallback(
|
|
3137
5804
|
(message) => {
|
|
3138
|
-
const result =
|
|
5805
|
+
const result = chunkI3SQUNTT_cjs.detectIntent(message);
|
|
3139
5806
|
result.suggestedTools = result.suggestedTools.filter((tool2) => {
|
|
3140
5807
|
if (tool2 === "screenshot") return screenshot;
|
|
3141
5808
|
if (tool2 === "console") return consoleCapture;
|
|
@@ -3146,7 +5813,7 @@ function useAITools(options = {}) {
|
|
|
3146
5813
|
},
|
|
3147
5814
|
[screenshot, consoleCapture, network]
|
|
3148
5815
|
);
|
|
3149
|
-
return
|
|
5816
|
+
return React2.useMemo(
|
|
3150
5817
|
() => ({
|
|
3151
5818
|
isEnabled,
|
|
3152
5819
|
activeCaptures,
|
|
@@ -3181,69 +5848,6 @@ function useAITools(options = {}) {
|
|
|
3181
5848
|
]
|
|
3182
5849
|
);
|
|
3183
5850
|
}
|
|
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
5851
|
function convertZodSchema(schema, _toolName) {
|
|
3248
5852
|
try {
|
|
3249
5853
|
const zodWithJsonSchema = z__namespace;
|
|
@@ -3261,13 +5865,13 @@ function convertZodSchema(schema, _toolName) {
|
|
|
3261
5865
|
}
|
|
3262
5866
|
} catch {
|
|
3263
5867
|
}
|
|
3264
|
-
return
|
|
5868
|
+
return chunkI3SQUNTT_cjs.zodObjectToInputSchema(schema);
|
|
3265
5869
|
}
|
|
3266
5870
|
function useToolWithSchema(config, dependencies = []) {
|
|
3267
5871
|
const { registerTool, unregisterTool } = useCopilot();
|
|
3268
|
-
const configRef =
|
|
5872
|
+
const configRef = React2.useRef(config);
|
|
3269
5873
|
configRef.current = config;
|
|
3270
|
-
const inputSchema =
|
|
5874
|
+
const inputSchema = React2.useMemo(() => {
|
|
3271
5875
|
try {
|
|
3272
5876
|
return convertZodSchema(config.schema, config.name);
|
|
3273
5877
|
} catch (error) {
|
|
@@ -3281,7 +5885,7 @@ function useToolWithSchema(config, dependencies = []) {
|
|
|
3281
5885
|
};
|
|
3282
5886
|
}
|
|
3283
5887
|
}, [config.schema, config.name]);
|
|
3284
|
-
|
|
5888
|
+
React2.useEffect(() => {
|
|
3285
5889
|
const tool2 = {
|
|
3286
5890
|
name: config.name,
|
|
3287
5891
|
description: config.description,
|
|
@@ -3301,9 +5905,9 @@ function useToolWithSchema(config, dependencies = []) {
|
|
|
3301
5905
|
}
|
|
3302
5906
|
function useToolsWithSchema(tools, dependencies = []) {
|
|
3303
5907
|
const { registerTool, unregisterTool } = useCopilot();
|
|
3304
|
-
const toolsRef =
|
|
5908
|
+
const toolsRef = React2.useRef(tools);
|
|
3305
5909
|
toolsRef.current = tools;
|
|
3306
|
-
|
|
5910
|
+
React2.useEffect(() => {
|
|
3307
5911
|
const toolNames = [];
|
|
3308
5912
|
for (const config of tools) {
|
|
3309
5913
|
let inputSchema;
|
|
@@ -3342,9 +5946,9 @@ function useToolsWithSchema(tools, dependencies = []) {
|
|
|
3342
5946
|
};
|
|
3343
5947
|
}, [tools.map((t) => t.name).join(","), ...dependencies]);
|
|
3344
5948
|
}
|
|
3345
|
-
var CopilotContext2 =
|
|
5949
|
+
var CopilotContext2 = React2.createContext(null);
|
|
3346
5950
|
function useCopilotContext() {
|
|
3347
|
-
const context =
|
|
5951
|
+
const context = React2.useContext(CopilotContext2);
|
|
3348
5952
|
if (!context) {
|
|
3349
5953
|
throw new Error("useCopilotContext must be used within a CopilotProvider");
|
|
3350
5954
|
}
|
|
@@ -3360,9 +5964,9 @@ function useToolExecutor() {
|
|
|
3360
5964
|
addToolExecution,
|
|
3361
5965
|
updateToolExecution
|
|
3362
5966
|
} = useCopilotContext();
|
|
3363
|
-
const toolsRef =
|
|
5967
|
+
const toolsRef = React2.useRef(registeredTools);
|
|
3364
5968
|
toolsRef.current = registeredTools;
|
|
3365
|
-
const executeTool =
|
|
5969
|
+
const executeTool = React2.useCallback(
|
|
3366
5970
|
async (toolCall) => {
|
|
3367
5971
|
const tool2 = toolsRef.current.find((t) => t.name === toolCall.name);
|
|
3368
5972
|
if (!tool2) {
|
|
@@ -3412,7 +6016,7 @@ function useToolExecutor() {
|
|
|
3412
6016
|
},
|
|
3413
6017
|
[addToolExecution, updateToolExecution]
|
|
3414
6018
|
);
|
|
3415
|
-
const sendToolResult =
|
|
6019
|
+
const sendToolResult = React2.useCallback(
|
|
3416
6020
|
async (toolCallId, result) => {
|
|
3417
6021
|
const runtimeUrl = config.runtimeUrl || config.cloud?.endpoint;
|
|
3418
6022
|
if (!runtimeUrl) {
|
|
@@ -3446,10 +6050,10 @@ function useToolExecutor() {
|
|
|
3446
6050
|
},
|
|
3447
6051
|
[config.runtimeUrl, config.cloud?.endpoint, chat.threadId]
|
|
3448
6052
|
);
|
|
3449
|
-
const getTool =
|
|
6053
|
+
const getTool = React2.useCallback((name) => {
|
|
3450
6054
|
return toolsRef.current.find((t) => t.name === name);
|
|
3451
6055
|
}, []);
|
|
3452
|
-
const hasTool =
|
|
6056
|
+
const hasTool = React2.useCallback((name) => {
|
|
3453
6057
|
return toolsRef.current.some((t) => t.name === name);
|
|
3454
6058
|
}, []);
|
|
3455
6059
|
return {
|
|
@@ -3467,13 +6071,13 @@ function useSuggestions(options = {}) {
|
|
|
3467
6071
|
autoRefresh = true
|
|
3468
6072
|
} = options;
|
|
3469
6073
|
const { chat, actions, config } = useCopilotContext();
|
|
3470
|
-
const [suggestions, setSuggestions] =
|
|
3471
|
-
const [isLoading, setIsLoading] =
|
|
3472
|
-
const normalizedStatic =
|
|
6074
|
+
const [suggestions, setSuggestions] = React2.useState([]);
|
|
6075
|
+
const [isLoading, setIsLoading] = React2.useState(false);
|
|
6076
|
+
const normalizedStatic = React2.useMemo(
|
|
3473
6077
|
() => staticSuggestions?.map((s) => typeof s === "string" ? { text: s } : s),
|
|
3474
6078
|
[staticSuggestions]
|
|
3475
6079
|
);
|
|
3476
|
-
const refresh =
|
|
6080
|
+
const refresh = React2.useCallback(async () => {
|
|
3477
6081
|
if (normalizedStatic) {
|
|
3478
6082
|
setSuggestions(normalizedStatic.slice(0, count));
|
|
3479
6083
|
return;
|
|
@@ -3512,14 +6116,14 @@ function useSuggestions(options = {}) {
|
|
|
3512
6116
|
setIsLoading(false);
|
|
3513
6117
|
}
|
|
3514
6118
|
}, [config.cloud, count, context, chat.messages, normalizedStatic]);
|
|
3515
|
-
const select =
|
|
6119
|
+
const select = React2.useCallback(
|
|
3516
6120
|
(suggestion) => {
|
|
3517
6121
|
const text = typeof suggestion === "string" ? suggestion : suggestion.text;
|
|
3518
6122
|
actions.sendMessage(text);
|
|
3519
6123
|
},
|
|
3520
6124
|
[actions]
|
|
3521
6125
|
);
|
|
3522
|
-
|
|
6126
|
+
React2.useEffect(() => {
|
|
3523
6127
|
if (autoRefresh && chat.messages.length === 0) {
|
|
3524
6128
|
refresh();
|
|
3525
6129
|
}
|
|
@@ -3534,18 +6138,18 @@ function useSuggestions(options = {}) {
|
|
|
3534
6138
|
function useAgent(options) {
|
|
3535
6139
|
const { name, initialState = {}, onStateChange } = options;
|
|
3536
6140
|
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 =
|
|
6141
|
+
const [state, setStateInternal] = React2.useState(initialState);
|
|
6142
|
+
const [isRunning, setIsRunning] = React2.useState(false);
|
|
6143
|
+
const [nodeName, setNodeName] = React2.useState(null);
|
|
6144
|
+
const [error, setError] = React2.useState(null);
|
|
6145
|
+
const abortControllerRef = React2.useRef(null);
|
|
6146
|
+
const getEndpoint = React2.useCallback(() => {
|
|
3543
6147
|
if (config.cloud) {
|
|
3544
6148
|
return `${config.cloud.endpoint || "https://api.yourgpt.ai/v1"}/agents/${name}`;
|
|
3545
6149
|
}
|
|
3546
6150
|
return `${config.runtimeUrl || "/api"}/agents/${name}`;
|
|
3547
6151
|
}, [config, name]);
|
|
3548
|
-
const start =
|
|
6152
|
+
const start = React2.useCallback(
|
|
3549
6153
|
async (input) => {
|
|
3550
6154
|
setIsRunning(true);
|
|
3551
6155
|
setError(null);
|
|
@@ -3570,7 +6174,7 @@ function useAgent(options) {
|
|
|
3570
6174
|
if (!response.ok) {
|
|
3571
6175
|
throw new Error(`Agent error: ${response.status}`);
|
|
3572
6176
|
}
|
|
3573
|
-
for await (const event of
|
|
6177
|
+
for await (const event of chunkI3SQUNTT_cjs.streamSSE(response)) {
|
|
3574
6178
|
handleAgentEvent(event);
|
|
3575
6179
|
}
|
|
3576
6180
|
} catch (err) {
|
|
@@ -3584,7 +6188,7 @@ function useAgent(options) {
|
|
|
3584
6188
|
},
|
|
3585
6189
|
[config, getEndpoint, state]
|
|
3586
6190
|
);
|
|
3587
|
-
const handleAgentEvent =
|
|
6191
|
+
const handleAgentEvent = React2.useCallback(
|
|
3588
6192
|
(event) => {
|
|
3589
6193
|
if (event.type === "error") {
|
|
3590
6194
|
setError(new Error(event.message));
|
|
@@ -3600,10 +6204,10 @@ function useAgent(options) {
|
|
|
3600
6204
|
},
|
|
3601
6205
|
[onStateChange]
|
|
3602
6206
|
);
|
|
3603
|
-
const stop =
|
|
6207
|
+
const stop = React2.useCallback(() => {
|
|
3604
6208
|
abortControllerRef.current?.abort();
|
|
3605
6209
|
}, []);
|
|
3606
|
-
const setState =
|
|
6210
|
+
const setState = React2.useCallback(
|
|
3607
6211
|
(partialState) => {
|
|
3608
6212
|
setStateInternal((prev) => {
|
|
3609
6213
|
const newState = { ...prev, ...partialState };
|
|
@@ -3613,7 +6217,7 @@ function useAgent(options) {
|
|
|
3613
6217
|
},
|
|
3614
6218
|
[onStateChange]
|
|
3615
6219
|
);
|
|
3616
|
-
|
|
6220
|
+
React2.useEffect(() => {
|
|
3617
6221
|
return () => {
|
|
3618
6222
|
abortControllerRef.current?.abort();
|
|
3619
6223
|
};
|
|
@@ -3694,9 +6298,9 @@ function formatKnowledgeResultsForAI(results) {
|
|
|
3694
6298
|
// src/react/hooks/useKnowledgeBase.ts
|
|
3695
6299
|
function useKnowledgeBase(config) {
|
|
3696
6300
|
const { registerTool, unregisterTool } = useCopilot();
|
|
3697
|
-
const configRef =
|
|
6301
|
+
const configRef = React2.useRef(config);
|
|
3698
6302
|
configRef.current = config;
|
|
3699
|
-
const handleSearch =
|
|
6303
|
+
const handleSearch = React2.useCallback(
|
|
3700
6304
|
async (params) => {
|
|
3701
6305
|
const query = params.query;
|
|
3702
6306
|
if (!query) {
|
|
@@ -3734,7 +6338,7 @@ function useKnowledgeBase(config) {
|
|
|
3734
6338
|
},
|
|
3735
6339
|
[]
|
|
3736
6340
|
);
|
|
3737
|
-
|
|
6341
|
+
React2.useEffect(() => {
|
|
3738
6342
|
if (config.enabled === false) {
|
|
3739
6343
|
return;
|
|
3740
6344
|
}
|
|
@@ -3781,14 +6385,14 @@ var DEFAULT_CAPABILITIES = {
|
|
|
3781
6385
|
};
|
|
3782
6386
|
function useCapabilities() {
|
|
3783
6387
|
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] =
|
|
6388
|
+
const [capabilities, setCapabilities] = React2.useState(DEFAULT_CAPABILITIES);
|
|
6389
|
+
const [provider, setProvider] = React2.useState("unknown");
|
|
6390
|
+
const [model, setModel] = React2.useState("unknown");
|
|
6391
|
+
const [supportedModels, setSupportedModels] = React2.useState([]);
|
|
6392
|
+
const [isLoading, setIsLoading] = React2.useState(true);
|
|
6393
|
+
const [error, setError] = React2.useState(null);
|
|
3790
6394
|
const capabilitiesUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/chat\/?$/, "/capabilities") : null;
|
|
3791
|
-
const fetchCapabilities =
|
|
6395
|
+
const fetchCapabilities = React2.useCallback(async () => {
|
|
3792
6396
|
if (!capabilitiesUrl) {
|
|
3793
6397
|
setIsLoading(false);
|
|
3794
6398
|
return;
|
|
@@ -3811,7 +6415,7 @@ function useCapabilities() {
|
|
|
3811
6415
|
setIsLoading(false);
|
|
3812
6416
|
}
|
|
3813
6417
|
}, [capabilitiesUrl]);
|
|
3814
|
-
|
|
6418
|
+
React2.useEffect(() => {
|
|
3815
6419
|
fetchCapabilities();
|
|
3816
6420
|
}, [fetchCapabilities]);
|
|
3817
6421
|
return {
|
|
@@ -3854,7 +6458,7 @@ function useSupportedMediaTypes() {
|
|
|
3854
6458
|
}
|
|
3855
6459
|
function useDevLogger() {
|
|
3856
6460
|
const ctx = useCopilotContext();
|
|
3857
|
-
return
|
|
6461
|
+
return React2.useMemo(() => {
|
|
3858
6462
|
const toolExecutions = (ctx.agentLoop?.toolExecutions || []).map(
|
|
3859
6463
|
(exec) => ({
|
|
3860
6464
|
id: exec.id,
|
|
@@ -4064,7 +6668,7 @@ function createReactThreadManagerState(initialThreads) {
|
|
|
4064
6668
|
}
|
|
4065
6669
|
|
|
4066
6670
|
// src/react/internal/ReactThreadManager.ts
|
|
4067
|
-
var _ReactThreadManager = class _ReactThreadManager extends
|
|
6671
|
+
var _ReactThreadManager = class _ReactThreadManager extends chunkI3SQUNTT_cjs.ThreadManager {
|
|
4068
6672
|
constructor(config = {}, callbacks = {}) {
|
|
4069
6673
|
const reactState = new ReactThreadManagerState();
|
|
4070
6674
|
super({ ...config, state: reactState }, callbacks);
|
|
@@ -4200,7 +6804,7 @@ function getInternalManager(config) {
|
|
|
4200
6804
|
return internalManager;
|
|
4201
6805
|
}
|
|
4202
6806
|
function useThreadManager(config) {
|
|
4203
|
-
const manager =
|
|
6807
|
+
const manager = React2.useMemo(() => {
|
|
4204
6808
|
if (!config) {
|
|
4205
6809
|
return getDefaultManager();
|
|
4206
6810
|
}
|
|
@@ -4223,50 +6827,50 @@ function useThreadManager(config) {
|
|
|
4223
6827
|
config?.autoRestoreLastThread
|
|
4224
6828
|
// Note: callbacks are intentionally not in deps to avoid recreating manager
|
|
4225
6829
|
]);
|
|
4226
|
-
const threads =
|
|
6830
|
+
const threads = React2.useSyncExternalStore(
|
|
4227
6831
|
manager.subscribe,
|
|
4228
6832
|
manager.getThreadsSnapshot,
|
|
4229
6833
|
manager.getThreadsServerSnapshot
|
|
4230
6834
|
// SSR - always empty array
|
|
4231
6835
|
);
|
|
4232
|
-
const currentThread =
|
|
6836
|
+
const currentThread = React2.useSyncExternalStore(
|
|
4233
6837
|
manager.subscribe,
|
|
4234
6838
|
manager.getCurrentThreadSnapshot,
|
|
4235
6839
|
manager.getCurrentThreadServerSnapshot
|
|
4236
6840
|
// SSR - always null
|
|
4237
6841
|
);
|
|
4238
|
-
const currentThreadId =
|
|
6842
|
+
const currentThreadId = React2.useSyncExternalStore(
|
|
4239
6843
|
manager.subscribe,
|
|
4240
6844
|
manager.getCurrentThreadIdSnapshot,
|
|
4241
6845
|
manager.getCurrentThreadIdServerSnapshot
|
|
4242
6846
|
// SSR - always null
|
|
4243
6847
|
);
|
|
4244
|
-
const loadStatus =
|
|
6848
|
+
const loadStatus = React2.useSyncExternalStore(
|
|
4245
6849
|
manager.subscribe,
|
|
4246
6850
|
manager.getLoadStatusSnapshot,
|
|
4247
6851
|
manager.getLoadStatusServerSnapshot
|
|
4248
6852
|
// SSR - always "idle"
|
|
4249
6853
|
);
|
|
4250
|
-
const error =
|
|
6854
|
+
const error = React2.useSyncExternalStore(
|
|
4251
6855
|
manager.subscribe,
|
|
4252
6856
|
manager.getErrorSnapshot,
|
|
4253
6857
|
manager.getErrorServerSnapshot
|
|
4254
6858
|
// SSR - always undefined
|
|
4255
6859
|
);
|
|
4256
|
-
const isLoading =
|
|
6860
|
+
const isLoading = React2.useSyncExternalStore(
|
|
4257
6861
|
manager.subscribe,
|
|
4258
6862
|
manager.getIsLoadingSnapshot,
|
|
4259
6863
|
manager.getIsLoadingServerSnapshot
|
|
4260
6864
|
// SSR - always false
|
|
4261
6865
|
);
|
|
4262
|
-
|
|
6866
|
+
React2.useEffect(() => {
|
|
4263
6867
|
return () => {
|
|
4264
6868
|
if (config?.adapter && manager !== defaultManager && manager !== internalManager) {
|
|
4265
6869
|
manager.dispose();
|
|
4266
6870
|
}
|
|
4267
6871
|
};
|
|
4268
6872
|
}, [manager, config]);
|
|
4269
|
-
|
|
6873
|
+
React2.useEffect(() => {
|
|
4270
6874
|
const handleBeforeUnload = () => {
|
|
4271
6875
|
if (manager.hasPendingChanges) {
|
|
4272
6876
|
manager.saveNow().catch(() => {
|
|
@@ -4280,37 +6884,37 @@ function useThreadManager(config) {
|
|
|
4280
6884
|
};
|
|
4281
6885
|
}
|
|
4282
6886
|
}, [manager]);
|
|
4283
|
-
const createThread =
|
|
6887
|
+
const createThread = React2.useCallback(
|
|
4284
6888
|
(options) => manager.createThread(options),
|
|
4285
6889
|
[manager]
|
|
4286
6890
|
);
|
|
4287
|
-
const switchThread =
|
|
6891
|
+
const switchThread = React2.useCallback(
|
|
4288
6892
|
(id) => manager.switchThread(id),
|
|
4289
6893
|
[manager]
|
|
4290
6894
|
);
|
|
4291
|
-
const updateCurrentThread =
|
|
6895
|
+
const updateCurrentThread = React2.useCallback(
|
|
4292
6896
|
(updates) => manager.updateCurrentThread(updates),
|
|
4293
6897
|
[manager]
|
|
4294
6898
|
);
|
|
4295
|
-
const deleteThread =
|
|
6899
|
+
const deleteThread = React2.useCallback(
|
|
4296
6900
|
(id) => manager.deleteThread(id),
|
|
4297
6901
|
[manager]
|
|
4298
6902
|
);
|
|
4299
|
-
const clearCurrentThread =
|
|
6903
|
+
const clearCurrentThread = React2.useCallback(
|
|
4300
6904
|
() => manager.clearCurrentThread(),
|
|
4301
6905
|
[manager]
|
|
4302
6906
|
);
|
|
4303
|
-
const refreshThreads =
|
|
4304
|
-
const saveNow =
|
|
4305
|
-
const clearAllThreads =
|
|
6907
|
+
const refreshThreads = React2.useCallback(() => manager.loadThreads(), [manager]);
|
|
6908
|
+
const saveNow = React2.useCallback(() => manager.saveNow(), [manager]);
|
|
6909
|
+
const clearAllThreads = React2.useCallback(
|
|
4306
6910
|
() => manager.clearAllThreads(),
|
|
4307
6911
|
[manager]
|
|
4308
6912
|
);
|
|
4309
|
-
const messages =
|
|
6913
|
+
const messages = React2.useMemo(
|
|
4310
6914
|
() => currentThread?.messages ?? [],
|
|
4311
6915
|
[currentThread]
|
|
4312
6916
|
);
|
|
4313
|
-
const setMessages =
|
|
6917
|
+
const setMessages = React2.useCallback(
|
|
4314
6918
|
(newMessages) => updateCurrentThread({ messages: newMessages }),
|
|
4315
6919
|
[updateCurrentThread]
|
|
4316
6920
|
);
|
|
@@ -4347,7 +6951,7 @@ function useMCPUIIntents(config = {}) {
|
|
|
4347
6951
|
onLink,
|
|
4348
6952
|
requireConsent = { tool: false, link: true }
|
|
4349
6953
|
} = config;
|
|
4350
|
-
const handleIntent =
|
|
6954
|
+
const handleIntent = React2.useCallback(
|
|
4351
6955
|
async (intent, context) => {
|
|
4352
6956
|
switch (intent.type) {
|
|
4353
6957
|
case "tool": {
|
|
@@ -4401,7 +7005,7 @@ function useMCPUIIntents(config = {}) {
|
|
|
4401
7005
|
},
|
|
4402
7006
|
[onToolCall, onIntent, onPrompt, onNotify, onLink, requireConsent]
|
|
4403
7007
|
);
|
|
4404
|
-
return
|
|
7008
|
+
return React2.useMemo(
|
|
4405
7009
|
() => ({
|
|
4406
7010
|
handleIntent
|
|
4407
7011
|
}),
|
|
@@ -4431,6 +7035,94 @@ function createToolIntentHandler(callTool) {
|
|
|
4431
7035
|
}
|
|
4432
7036
|
};
|
|
4433
7037
|
}
|
|
7038
|
+
function getLastResponseUsage(messages) {
|
|
7039
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
7040
|
+
const msg = messages[i];
|
|
7041
|
+
if (msg.role === "assistant" && msg.metadata?.usage) {
|
|
7042
|
+
const u = msg.metadata.usage;
|
|
7043
|
+
const prompt = u.prompt_tokens ?? 0;
|
|
7044
|
+
const completion = u.completion_tokens ?? 0;
|
|
7045
|
+
return {
|
|
7046
|
+
prompt_tokens: prompt,
|
|
7047
|
+
completion_tokens: completion,
|
|
7048
|
+
total_tokens: u.total_tokens ?? prompt + completion
|
|
7049
|
+
};
|
|
7050
|
+
}
|
|
7051
|
+
}
|
|
7052
|
+
return null;
|
|
7053
|
+
}
|
|
7054
|
+
function useContextStats() {
|
|
7055
|
+
const { contextChars, contextUsage, registeredTools, messages } = useCopilot();
|
|
7056
|
+
const toolCount = React2.useMemo(() => registeredTools.length, [registeredTools]);
|
|
7057
|
+
const messageCount = React2.useMemo(
|
|
7058
|
+
() => messages.filter((m) => m.role !== "system").length,
|
|
7059
|
+
[messages]
|
|
7060
|
+
);
|
|
7061
|
+
const totalTokens = React2.useMemo(() => {
|
|
7062
|
+
if (contextUsage) return contextUsage.total.tokens;
|
|
7063
|
+
return Math.ceil(contextChars / 3.5);
|
|
7064
|
+
}, [contextUsage, contextChars]);
|
|
7065
|
+
const usagePercent = React2.useMemo(() => {
|
|
7066
|
+
if (contextUsage) return contextUsage.total.percent;
|
|
7067
|
+
return 0;
|
|
7068
|
+
}, [contextUsage]);
|
|
7069
|
+
const lastResponseUsage = React2.useMemo(
|
|
7070
|
+
() => getLastResponseUsage(messages),
|
|
7071
|
+
[messages]
|
|
7072
|
+
);
|
|
7073
|
+
return {
|
|
7074
|
+
contextUsage,
|
|
7075
|
+
totalTokens,
|
|
7076
|
+
usagePercent,
|
|
7077
|
+
contextChars,
|
|
7078
|
+
toolCount,
|
|
7079
|
+
messageCount,
|
|
7080
|
+
lastResponseUsage
|
|
7081
|
+
};
|
|
7082
|
+
}
|
|
7083
|
+
var DEV_CONTENT_WARN_THRESHOLD = 2e3;
|
|
7084
|
+
function useSkill(skill) {
|
|
7085
|
+
const { register, unregister } = useSkillContext();
|
|
7086
|
+
if (process.env.NODE_ENV !== "production" && skill.source.type === "inline" && skill.source.content.length > DEV_CONTENT_WARN_THRESHOLD) {
|
|
7087
|
+
console.warn(
|
|
7088
|
+
`[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.`
|
|
7089
|
+
);
|
|
7090
|
+
}
|
|
7091
|
+
React2.useEffect(() => {
|
|
7092
|
+
if (skill.source.type !== "inline") {
|
|
7093
|
+
console.warn(
|
|
7094
|
+
`[copilot-sdk/skills] useSkill only supports inline skills client-side. Skill "${skill.name}" has source type "${skill.source.type}" and will be skipped.`
|
|
7095
|
+
);
|
|
7096
|
+
return;
|
|
7097
|
+
}
|
|
7098
|
+
const resolved = {
|
|
7099
|
+
...skill,
|
|
7100
|
+
content: skill.source.content
|
|
7101
|
+
};
|
|
7102
|
+
register(resolved);
|
|
7103
|
+
return () => {
|
|
7104
|
+
unregister(skill.name);
|
|
7105
|
+
};
|
|
7106
|
+
}, [
|
|
7107
|
+
skill.name,
|
|
7108
|
+
skill.source.type === "inline" ? skill.source.content : "",
|
|
7109
|
+
skill.strategy,
|
|
7110
|
+
skill.description
|
|
7111
|
+
]);
|
|
7112
|
+
}
|
|
7113
|
+
function useSkillStatus() {
|
|
7114
|
+
const { skills, registry } = useSkillContext();
|
|
7115
|
+
const has = React2.useCallback(
|
|
7116
|
+
(name) => registry.has(name),
|
|
7117
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7118
|
+
[skills]
|
|
7119
|
+
);
|
|
7120
|
+
return {
|
|
7121
|
+
skills,
|
|
7122
|
+
count: skills.length,
|
|
7123
|
+
has
|
|
7124
|
+
};
|
|
7125
|
+
}
|
|
4434
7126
|
|
|
4435
7127
|
// src/react/utils/permission-storage.ts
|
|
4436
7128
|
var DEFAULT_KEY_PREFIX = "yourgpt-permissions";
|
|
@@ -4583,6 +7275,34 @@ var ReactChat = class extends AbstractChat {
|
|
|
4583
7275
|
return this.on("error", handler);
|
|
4584
7276
|
}
|
|
4585
7277
|
// ============================================
|
|
7278
|
+
// Branching API — pass-throughs to ReactChatState
|
|
7279
|
+
// ============================================
|
|
7280
|
+
/**
|
|
7281
|
+
* Navigate to a sibling branch (makes it the active path).
|
|
7282
|
+
*/
|
|
7283
|
+
switchBranch(messageId) {
|
|
7284
|
+
this.reactState.switchBranch(messageId);
|
|
7285
|
+
}
|
|
7286
|
+
/**
|
|
7287
|
+
* Get branch navigation info for a message.
|
|
7288
|
+
* Returns null if the message has no siblings.
|
|
7289
|
+
*/
|
|
7290
|
+
getBranchInfo(messageId) {
|
|
7291
|
+
return this.reactState.getBranchInfo(messageId);
|
|
7292
|
+
}
|
|
7293
|
+
/**
|
|
7294
|
+
* Get all messages across all branches (for persistence).
|
|
7295
|
+
*/
|
|
7296
|
+
getAllMessages() {
|
|
7297
|
+
return this.reactState.getAllMessages();
|
|
7298
|
+
}
|
|
7299
|
+
/**
|
|
7300
|
+
* Whether any message has siblings (branching has occurred).
|
|
7301
|
+
*/
|
|
7302
|
+
get hasBranches() {
|
|
7303
|
+
return this.reactState.hasBranches;
|
|
7304
|
+
}
|
|
7305
|
+
// ============================================
|
|
4586
7306
|
// Override dispose to clean up state
|
|
4587
7307
|
// ============================================
|
|
4588
7308
|
dispose() {
|
|
@@ -4601,8 +7321,8 @@ function createReactChat(config) {
|
|
|
4601
7321
|
return new ReactChat(config);
|
|
4602
7322
|
}
|
|
4603
7323
|
function useChat(config) {
|
|
4604
|
-
const chatRef =
|
|
4605
|
-
const [input, setInput] =
|
|
7324
|
+
const chatRef = React2.useRef(null);
|
|
7325
|
+
const [input, setInput] = React2.useState("");
|
|
4606
7326
|
if (chatRef.current !== null && chatRef.current.disposed) {
|
|
4607
7327
|
chatRef.current.revive();
|
|
4608
7328
|
}
|
|
@@ -4624,51 +7344,74 @@ function useChat(config) {
|
|
|
4624
7344
|
}
|
|
4625
7345
|
});
|
|
4626
7346
|
}
|
|
4627
|
-
const messages =
|
|
7347
|
+
const messages = React2.useSyncExternalStore(
|
|
4628
7348
|
chatRef.current.subscribe,
|
|
4629
7349
|
() => chatRef.current.messages,
|
|
4630
7350
|
() => chatRef.current.messages
|
|
4631
7351
|
// Server snapshot
|
|
4632
7352
|
);
|
|
4633
|
-
const status =
|
|
7353
|
+
const status = React2.useSyncExternalStore(
|
|
4634
7354
|
chatRef.current.subscribe,
|
|
4635
7355
|
() => chatRef.current.status,
|
|
4636
7356
|
() => "ready"
|
|
4637
7357
|
// Server snapshot
|
|
4638
7358
|
);
|
|
4639
|
-
const error =
|
|
7359
|
+
const error = React2.useSyncExternalStore(
|
|
4640
7360
|
chatRef.current.subscribe,
|
|
4641
7361
|
() => chatRef.current.error,
|
|
4642
7362
|
() => void 0
|
|
4643
7363
|
// Server snapshot
|
|
4644
7364
|
);
|
|
7365
|
+
const hasBranches = React2.useSyncExternalStore(
|
|
7366
|
+
chatRef.current.subscribe,
|
|
7367
|
+
() => chatRef.current.hasBranches,
|
|
7368
|
+
() => false
|
|
7369
|
+
);
|
|
4645
7370
|
const isLoading = status === "streaming" || status === "submitted";
|
|
4646
|
-
const sendMessage =
|
|
7371
|
+
const sendMessage = React2.useCallback(
|
|
4647
7372
|
async (content, attachments) => {
|
|
4648
7373
|
await chatRef.current?.sendMessage(content, attachments);
|
|
4649
7374
|
setInput("");
|
|
4650
7375
|
},
|
|
4651
7376
|
[]
|
|
4652
7377
|
);
|
|
4653
|
-
const stop =
|
|
7378
|
+
const stop = React2.useCallback(() => {
|
|
4654
7379
|
chatRef.current?.stop();
|
|
4655
7380
|
}, []);
|
|
4656
|
-
const clearMessages =
|
|
7381
|
+
const clearMessages = React2.useCallback(() => {
|
|
4657
7382
|
chatRef.current?.clearMessages();
|
|
4658
7383
|
}, []);
|
|
4659
|
-
const setMessages =
|
|
7384
|
+
const setMessages = React2.useCallback((messages2) => {
|
|
4660
7385
|
chatRef.current?.setMessages(messages2);
|
|
4661
7386
|
}, []);
|
|
4662
|
-
const regenerate =
|
|
7387
|
+
const regenerate = React2.useCallback(async (messageId) => {
|
|
4663
7388
|
await chatRef.current?.regenerate(messageId);
|
|
4664
7389
|
}, []);
|
|
4665
|
-
const continueWithToolResults =
|
|
7390
|
+
const continueWithToolResults = React2.useCallback(
|
|
4666
7391
|
async (toolResults) => {
|
|
4667
7392
|
await chatRef.current?.continueWithToolResults(toolResults);
|
|
4668
7393
|
},
|
|
4669
7394
|
[]
|
|
4670
7395
|
);
|
|
4671
|
-
|
|
7396
|
+
const switchBranch = React2.useCallback((messageId) => {
|
|
7397
|
+
chatRef.current?.switchBranch(messageId);
|
|
7398
|
+
}, []);
|
|
7399
|
+
const getBranchInfo = React2.useCallback(
|
|
7400
|
+
(messageId) => {
|
|
7401
|
+
return chatRef.current?.getBranchInfo(messageId) ?? null;
|
|
7402
|
+
},
|
|
7403
|
+
[]
|
|
7404
|
+
);
|
|
7405
|
+
const editMessage = React2.useCallback(
|
|
7406
|
+
async (messageId, newContent) => {
|
|
7407
|
+
await chatRef.current?.sendMessage(newContent, void 0, {
|
|
7408
|
+
editMessageId: messageId
|
|
7409
|
+
});
|
|
7410
|
+
setInput("");
|
|
7411
|
+
},
|
|
7412
|
+
[]
|
|
7413
|
+
);
|
|
7414
|
+
React2.useEffect(() => {
|
|
4672
7415
|
return () => {
|
|
4673
7416
|
chatRef.current?.dispose();
|
|
4674
7417
|
};
|
|
@@ -4686,17 +7429,30 @@ function useChat(config) {
|
|
|
4686
7429
|
setMessages,
|
|
4687
7430
|
regenerate,
|
|
4688
7431
|
continueWithToolResults,
|
|
4689
|
-
chatRef
|
|
7432
|
+
chatRef,
|
|
7433
|
+
// Branching
|
|
7434
|
+
switchBranch,
|
|
7435
|
+
getBranchInfo,
|
|
7436
|
+
editMessage,
|
|
7437
|
+
hasBranches
|
|
4690
7438
|
};
|
|
4691
7439
|
}
|
|
4692
7440
|
|
|
7441
|
+
// src/react/skill/define-skill.ts
|
|
7442
|
+
function defineSkill(def) {
|
|
7443
|
+
return def;
|
|
7444
|
+
}
|
|
7445
|
+
|
|
4693
7446
|
exports.AbstractAgentLoop = AbstractAgentLoop;
|
|
4694
7447
|
exports.AbstractChat = AbstractChat;
|
|
4695
7448
|
exports.CopilotProvider = CopilotProvider;
|
|
7449
|
+
exports.MessageHistoryContext = MessageHistoryContext;
|
|
7450
|
+
exports.MessageTree = MessageTree;
|
|
4696
7451
|
exports.ReactChat = ReactChat;
|
|
4697
7452
|
exports.ReactChatState = ReactChatState;
|
|
4698
7453
|
exports.ReactThreadManager = ReactThreadManager;
|
|
4699
7454
|
exports.ReactThreadManagerState = ReactThreadManagerState;
|
|
7455
|
+
exports.SkillProvider = SkillProvider;
|
|
4700
7456
|
exports.createMessageIntentHandler = createMessageIntentHandler;
|
|
4701
7457
|
exports.createPermissionStorage = createPermissionStorage;
|
|
4702
7458
|
exports.createReactChat = createReactChat;
|
|
@@ -4705,9 +7461,16 @@ exports.createReactThreadManager = createReactThreadManager;
|
|
|
4705
7461
|
exports.createReactThreadManagerState = createReactThreadManagerState;
|
|
4706
7462
|
exports.createSessionPermissionCache = createSessionPermissionCache;
|
|
4707
7463
|
exports.createToolIntentHandler = createToolIntentHandler;
|
|
7464
|
+
exports.defaultMessageHistoryConfig = defaultMessageHistoryConfig;
|
|
7465
|
+
exports.defineSkill = defineSkill;
|
|
4708
7466
|
exports.formatKnowledgeResultsForAI = formatKnowledgeResultsForAI;
|
|
4709
7467
|
exports.initialAgentLoopState = initialAgentLoopState;
|
|
7468
|
+
exports.isCompactionMarker = isCompactionMarker;
|
|
7469
|
+
exports.keepToolPairsAtomic = keepToolPairsAtomic;
|
|
4710
7470
|
exports.searchKnowledgeBase = searchKnowledgeBase;
|
|
7471
|
+
exports.toDisplayMessage = toDisplayMessage;
|
|
7472
|
+
exports.toLLMMessage = toLLMMessage;
|
|
7473
|
+
exports.toLLMMessages = toLLMMessages;
|
|
4711
7474
|
exports.useAIAction = useAIAction;
|
|
4712
7475
|
exports.useAIActions = useAIActions;
|
|
4713
7476
|
exports.useAIContext = useAIContext;
|
|
@@ -4716,6 +7479,7 @@ exports.useAITools = useAITools;
|
|
|
4716
7479
|
exports.useAgent = useAgent;
|
|
4717
7480
|
exports.useCapabilities = useCapabilities;
|
|
4718
7481
|
exports.useChat = useChat;
|
|
7482
|
+
exports.useContextStats = useContextStats;
|
|
4719
7483
|
exports.useCopilot = useCopilot;
|
|
4720
7484
|
exports.useDevLogger = useDevLogger;
|
|
4721
7485
|
exports.useFeatureSupport = useFeatureSupport;
|
|
@@ -4723,6 +7487,10 @@ exports.useKnowledgeBase = useKnowledgeBase;
|
|
|
4723
7487
|
exports.useMCPClient = useMCPClient;
|
|
4724
7488
|
exports.useMCPTools = useMCPTools;
|
|
4725
7489
|
exports.useMCPUIIntents = useMCPUIIntents;
|
|
7490
|
+
exports.useMessageHistory = useMessageHistory;
|
|
7491
|
+
exports.useMessageHistoryContext = useMessageHistoryContext;
|
|
7492
|
+
exports.useSkill = useSkill;
|
|
7493
|
+
exports.useSkillStatus = useSkillStatus;
|
|
4726
7494
|
exports.useSuggestions = useSuggestions;
|
|
4727
7495
|
exports.useSupportedMediaTypes = useSupportedMediaTypes;
|
|
4728
7496
|
exports.useThreadManager = useThreadManager;
|
|
@@ -4731,5 +7499,5 @@ exports.useToolExecutor = useToolExecutor;
|
|
|
4731
7499
|
exports.useToolWithSchema = useToolWithSchema;
|
|
4732
7500
|
exports.useTools = useTools;
|
|
4733
7501
|
exports.useToolsWithSchema = useToolsWithSchema;
|
|
4734
|
-
//# sourceMappingURL=chunk-
|
|
4735
|
-
//# sourceMappingURL=chunk-
|
|
7502
|
+
//# sourceMappingURL=chunk-IXWNDR7H.cjs.map
|
|
7503
|
+
//# sourceMappingURL=chunk-IXWNDR7H.cjs.map
|