@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.
Files changed (103) hide show
  1. package/dist/MessageTree-CoIt_4nB.d.cts +161 -0
  2. package/dist/MessageTree-CzaN9Eul.d.ts +161 -0
  3. package/dist/{ThreadManager-Dkp_eLty.d.ts → ThreadManager-BEAECB7Y.d.ts} +1 -1
  4. package/dist/{ThreadManager-LfFRhr4e.d.cts → ThreadManager-Cw5fwyCN.d.cts} +1 -1
  5. package/dist/{chunk-POZNNKNJ.cjs → chunk-246B6X5D.cjs} +8 -2
  6. package/dist/chunk-246B6X5D.cjs.map +1 -0
  7. package/dist/{chunk-QLH6TSCC.js → chunk-4QXY2PBG.js} +8 -2
  8. package/dist/chunk-4QXY2PBG.js.map +1 -0
  9. package/dist/{chunk-7PKGRYHY.js → chunk-5Q72LZ5H.js} +3107 -357
  10. package/dist/chunk-5Q72LZ5H.js.map +1 -0
  11. package/dist/chunk-BJYA5NDL.cjs +96 -0
  12. package/dist/chunk-BJYA5NDL.cjs.map +1 -0
  13. package/dist/{chunk-LZMBBGWH.js → chunk-ENFWM3EY.js} +4 -4
  14. package/dist/{chunk-LZMBBGWH.js.map → chunk-ENFWM3EY.js.map} +1 -1
  15. package/dist/{chunk-OQPRIB73.cjs → chunk-I3SQUNTT.cjs} +71 -25
  16. package/dist/chunk-I3SQUNTT.cjs.map +1 -0
  17. package/dist/{chunk-N6VZ7FOW.cjs → chunk-IXWNDR7H.cjs} +3290 -522
  18. package/dist/chunk-IXWNDR7H.cjs.map +1 -0
  19. package/dist/{chunk-WAPGTQDR.cjs → chunk-JKGFQUHJ.cjs} +10 -10
  20. package/dist/{chunk-WAPGTQDR.cjs.map → chunk-JKGFQUHJ.cjs.map} +1 -1
  21. package/dist/{chunk-XGITAEXU.js → chunk-LLM7AHMO.js} +2 -2
  22. package/dist/{chunk-XGITAEXU.js.map → chunk-LLM7AHMO.js.map} +1 -1
  23. package/dist/{chunk-ASV6JLYG.cjs → chunk-NUXLAZOE.cjs} +2 -2
  24. package/dist/{chunk-ASV6JLYG.cjs.map → chunk-NUXLAZOE.cjs.map} +1 -1
  25. package/dist/{chunk-37KEHUCE.js → chunk-UXJ6LIZB.js} +51 -7
  26. package/dist/chunk-UXJ6LIZB.js.map +1 -0
  27. package/dist/chunk-VNLLW3ZI.js +94 -0
  28. package/dist/chunk-VNLLW3ZI.js.map +1 -0
  29. package/dist/core/index.cjs +99 -91
  30. package/dist/core/index.d.cts +7 -7
  31. package/dist/core/index.d.ts +7 -7
  32. package/dist/core/index.js +5 -5
  33. package/dist/{index-BHkRA0mM.d.cts → index-CiExk87c.d.cts} +1 -1
  34. package/dist/{index-tB0qI8my.d.ts → index-Dwrcf-CP.d.ts} +1 -1
  35. package/dist/mcp/index.d.cts +3 -3
  36. package/dist/mcp/index.d.ts +3 -3
  37. package/dist/react/index.cjs +113 -52
  38. package/dist/react/index.d.cts +673 -77
  39. package/dist/react/index.d.ts +673 -77
  40. package/dist/react/index.js +7 -6
  41. package/dist/server/index.cjs +339 -0
  42. package/dist/server/index.cjs.map +1 -0
  43. package/dist/server/index.d.cts +171 -0
  44. package/dist/server/index.d.ts +171 -0
  45. package/dist/server/index.js +332 -0
  46. package/dist/server/index.js.map +1 -0
  47. package/dist/tools/anthropic/index.cjs +3 -3
  48. package/dist/tools/anthropic/index.d.cts +1 -1
  49. package/dist/tools/anthropic/index.d.ts +1 -1
  50. package/dist/tools/anthropic/index.js +3 -3
  51. package/dist/tools/brave/index.cjs +6 -6
  52. package/dist/tools/brave/index.d.cts +1 -1
  53. package/dist/tools/brave/index.d.ts +1 -1
  54. package/dist/tools/brave/index.js +3 -3
  55. package/dist/tools/exa/index.cjs +6 -6
  56. package/dist/tools/exa/index.d.cts +1 -1
  57. package/dist/tools/exa/index.d.ts +1 -1
  58. package/dist/tools/exa/index.js +3 -3
  59. package/dist/tools/google/index.cjs +6 -6
  60. package/dist/tools/google/index.d.cts +1 -1
  61. package/dist/tools/google/index.d.ts +1 -1
  62. package/dist/tools/google/index.js +3 -3
  63. package/dist/tools/openai/index.cjs +6 -6
  64. package/dist/tools/openai/index.d.cts +1 -1
  65. package/dist/tools/openai/index.d.ts +1 -1
  66. package/dist/tools/openai/index.js +3 -3
  67. package/dist/tools/searxng/index.cjs +6 -6
  68. package/dist/tools/searxng/index.d.cts +1 -1
  69. package/dist/tools/searxng/index.d.ts +1 -1
  70. package/dist/tools/searxng/index.js +3 -3
  71. package/dist/tools/serper/index.cjs +6 -6
  72. package/dist/tools/serper/index.d.cts +1 -1
  73. package/dist/tools/serper/index.d.ts +1 -1
  74. package/dist/tools/serper/index.js +3 -3
  75. package/dist/tools/tavily/index.cjs +6 -6
  76. package/dist/tools/tavily/index.d.cts +1 -1
  77. package/dist/tools/tavily/index.d.ts +1 -1
  78. package/dist/tools/tavily/index.js +3 -3
  79. package/dist/tools/web-search/index.cjs +7 -7
  80. package/dist/tools/web-search/index.d.cts +2 -2
  81. package/dist/tools/web-search/index.d.ts +2 -2
  82. package/dist/tools/web-search/index.js +4 -4
  83. package/dist/{tools-coIcskZ4.d.ts → tools-DHZhF5km.d.cts} +161 -1
  84. package/dist/{tools-coIcskZ4.d.cts → tools-DHZhF5km.d.ts} +161 -1
  85. package/dist/{types-rjaSVmEF.d.ts → types-BTyJu0WD.d.ts} +1 -1
  86. package/dist/types-BckL3hiw.d.cts +93 -0
  87. package/dist/types-BckL3hiw.d.ts +93 -0
  88. package/dist/{types-C8t4Ut8f.d.cts → types-BdX7uPj0.d.cts} +1 -1
  89. package/dist/{types-DG2ya08y.d.ts → types-BeFBBZ5i.d.cts} +64 -1
  90. package/dist/{types-DG2ya08y.d.cts → types-BeFBBZ5i.d.ts} +64 -1
  91. package/dist/ui/index.cjs +509 -209
  92. package/dist/ui/index.cjs.map +1 -1
  93. package/dist/ui/index.d.cts +81 -4
  94. package/dist/ui/index.d.ts +81 -4
  95. package/dist/ui/index.js +457 -158
  96. package/dist/ui/index.js.map +1 -1
  97. package/package.json +6 -1
  98. package/dist/chunk-37KEHUCE.js.map +0 -1
  99. package/dist/chunk-7PKGRYHY.js.map +0 -1
  100. package/dist/chunk-N6VZ7FOW.cjs.map +0 -1
  101. package/dist/chunk-OQPRIB73.cjs.map +0 -1
  102. package/dist/chunk-POZNNKNJ.cjs.map +0 -1
  103. package/dist/chunk-QLH6TSCC.js.map +0 -1
@@ -1,11 +1,14 @@
1
1
  'use strict';
2
2
 
3
- var chunkOQPRIB73_cjs = require('./chunk-OQPRIB73.cjs');
3
+ var chunkI3SQUNTT_cjs = require('./chunk-I3SQUNTT.cjs');
4
4
  var chunkJGPDQDY4_cjs = require('./chunk-JGPDQDY4.cjs');
5
- var react = require('react');
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/classes/AbstractChat.ts
547
- function buildToolResultContentForAI(result, tool2, args) {
548
- if (typeof result === "string") return result;
549
- const typedResult = result;
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(typedResult, args ?? {}) : 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
- const userMessage = createUserMessage(content, attachments);
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.state.messages);
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: generateMessageId(),
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.state.messages);
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: generateMessageId(),
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.state.messages);
1728
+ this.callbacks.onMessagesChange?.(this._allMessages());
762
1729
  this.callbacks.onStatusChange?.("submitted");
763
- await Promise.resolve();
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 targetIndex = messages.length - 1;
1770
+ let targetMessage;
797
1771
  if (messageId) {
798
- targetIndex = messages.findIndex((m) => m.id === messageId);
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
- targetIndex = i;
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.state.messages);
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
- * Set dynamic context (appended to system prompt)
1878
+ * Update prompt/tool optimization behavior.
862
1879
  */
863
- setContext(context) {
864
- this.dynamicContext = context;
865
- this.debug("Context updated", { length: context.length });
1880
+ setOptimizationConfig(config) {
1881
+ this.config.optimization = config;
1882
+ this.optimizer.updateConfig(config);
866
1883
  }
867
1884
  /**
868
- * Set system prompt dynamically
869
- * This allows updating the system prompt after initialization
1885
+ * Select the active tool profile for future requests.
870
1886
  */
871
- setSystemPrompt(prompt) {
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 tools = this.config.tools?.filter((tool2) => tool2.available !== false).map((tool2) => ({
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 : void 0
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
- const assistantMessage = createEmptyAssistantMessage();
994
- this.state.pushMessage(assistantMessage);
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.debug("handleStreamResponse", "Starting to process stream");
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
- this.debug("chunk", { count: chunkCount, type: chunk.type });
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 - finalizing current turn");
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
- () => turnMessage
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
- () => updatedMessage
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", { chunk });
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
- this.state.pushMessage(message);
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
- this.streamState.messageId,
1143
- () => finalMessage
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.state.messages);
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.state.messages);
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
- * Debug logging
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
- if (this.config.debug) {
1224
- console.log(`[AbstractChat] ${action}`, data);
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
- for (const toolCall of toolCalls) {
1439
- if (this._isCancelled || this.abortController.signal.aborted) {
1440
- results.push({
1441
- toolCallId: toolCall.id,
1442
- success: false,
1443
- error: "Tool execution cancelled"
1444
- });
1445
- continue;
1446
- }
1447
- const result = await this.executeSingleTool(toolCall);
1448
- results.push(result);
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
- if (this.config.debug) {
2100
- console.log(`[ChatWithTools] ${message}`, ...args);
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
- if (initialMessages) {
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._messages;
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._messages = value;
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._messages = [...this._messages, message];
3708
+ this.tree.addMessage(message);
2170
3709
  this.notify();
2171
3710
  }
2172
3711
  popMessage() {
2173
- this._messages = this._messages.slice(0, -1);
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
- this._messages = this._messages.map((m, i) => i === index ? message : m);
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
- if (this._messages.length === 0) return;
2182
- const lastIndex = this._messages.length - 1;
2183
- const lastMessage = this._messages[lastIndex];
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 index = this._messages.findIndex((m) => m.id === id);
2192
- if (index === -1) return false;
2193
- this._messages = this._messages.map(
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._messages = messages;
3742
+ this.tree.reset(messages);
2201
3743
  this.notify();
2202
3744
  }
2203
3745
  clearMessages() {
2204
- this._messages = [];
3746
+ this.tree.reset([]);
2205
3747
  this.notify();
2206
3748
  }
2207
3749
  // ============================================
2208
- // Private Methods
3750
+ // Branching API
2209
3751
  // ============================================
2210
- notify() {
2211
- this.subscribers.forEach((cb) => cb());
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
- * Cleanup subscriptions
3760
+ * Get branch navigation info for a message.
3761
+ * Returns null if the message has no siblings.
2215
3762
  */
2216
- dispose() {
2217
- this.subscribers.clear();
3763
+ getBranchInfo(messageId) {
3764
+ return this.tree.getBranchInfo(messageId);
2218
3765
  }
2219
3766
  /**
2220
- * Revive after dispose (for React StrictMode compatibility)
2221
- * Subscribers will be re-added automatically via useSyncExternalStore
3767
+ * Navigate to a sibling branch.
3768
+ * Triggers re-render via notify().
2222
3769
  */
2223
- revive() {
3770
+ switchBranch(messageId) {
3771
+ this.tree.switchBranch(messageId);
3772
+ this.notify();
2224
3773
  }
2225
- };
2226
- function createReactChatState(initialMessages) {
2227
- return new ReactChatState(initialMessages);
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 = react.useRef(null);
2341
- const mountedRef = react.useRef(true);
2342
- const [state, setState] = react.useState({
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 = react.useCallback(() => {
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 = react.useCallback(async () => {
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 = react.useCallback(async () => {
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 = react.useCallback(
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 = react.useCallback(async () => {
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
- react.useEffect(() => {
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 = react.useRef([]);
4092
+ const registeredToolsRef = React2.useRef([]);
2488
4093
  const mcpClient = useMCPClient(clientConfig);
2489
- const toolAdapter = react.useMemo(
4094
+ const toolAdapter = React2.useMemo(
2490
4095
  () => new chunkJGPDQDY4_cjs.MCPToolAdapter(clientConfig.name),
2491
4096
  [clientConfig.name]
2492
4097
  );
2493
- const toolDefinitions = react.useMemo(() => {
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
- react.useEffect(() => {
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
- registeredToolsRef.current = [];
5030
+ setSkills(registry.getAll());
2537
5031
  };
2538
- }, [
2539
- autoRegister,
2540
- mcpClient.isConnected,
2541
- toolDefinitions,
2542
- registerTool,
2543
- unregisterTool
2544
- ]);
2545
- return {
2546
- ...mcpClient,
2547
- toolDefinitions
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 CopilotContext = react.createContext(null);
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 = react.useContext(CopilotContext);
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 = react.useCallback(
2588
- (...args) => {
2589
- if (debug) console.log("[Copilot SDK]", ...args);
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
- react.useEffect(() => {
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] = react.useState([]);
2601
- const chatRef = react.useRef(null);
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
- react.useEffect(() => {
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
- react.useEffect(() => {
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
- react.useEffect(() => {
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
- react.useEffect(() => {
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 messages = react.useSyncExternalStore(
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
- () => chatRef.current.messages,
2672
- () => chatRef.current.messages
5305
+ getMessagesSnapshot,
5306
+ getServerMessagesSnapshot
2673
5307
  );
2674
- const status = react.useSyncExternalStore(
5308
+ const status = React2.useSyncExternalStore(
2675
5309
  chatRef.current.subscribe,
2676
- () => chatRef.current.status,
5310
+ getStatusSnapshot,
2677
5311
  () => "ready"
2678
5312
  );
2679
- const errorFromChat = react.useSyncExternalStore(
5313
+ const errorFromChat = React2.useSyncExternalStore(
2680
5314
  chatRef.current.subscribe,
2681
- () => chatRef.current.error,
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 = react.useCallback((tool2) => {
5320
+ const registerTool = React2.useCallback((tool2) => {
2687
5321
  chatRef.current?.registerTool(tool2);
2688
5322
  }, []);
2689
- const unregisterTool = react.useCallback((name) => {
5323
+ const unregisterTool = React2.useCallback((name) => {
2690
5324
  chatRef.current?.unregisterTool(name);
2691
5325
  }, []);
2692
- const approveToolExecution = react.useCallback(
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 = react.useCallback(
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 = chatRef.current?.tools ?? [];
2705
- const pendingApprovals = toolExecutions.filter(
2706
- (e) => e.approvalStatus === "required"
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 actionsRef = react.useRef(/* @__PURE__ */ new Map());
2709
- const [actionsVersion, setActionsVersion] = react.useState(0);
2710
- const registerAction = react.useCallback((action) => {
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 = react.useCallback((name) => {
5354
+ const unregisterAction = React2.useCallback((name) => {
2715
5355
  actionsRef.current.delete(name);
2716
5356
  setActionsVersion((v) => v + 1);
2717
5357
  }, []);
2718
- const registeredActions = react.useMemo(
5358
+ const registeredActions = React2.useMemo(
2719
5359
  () => Array.from(actionsRef.current.values()),
2720
5360
  [actionsVersion]
2721
5361
  );
2722
- const contextTreeRef = react.useRef([]);
2723
- const contextIdCounter = react.useRef(0);
2724
- const addContext = react.useCallback(
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 = react.useCallback(
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 = react.useCallback(
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 sendMessage = react.useCallback(
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 = react.useCallback(() => {
5413
+ const stop = React2.useCallback(() => {
2763
5414
  chatRef.current?.stop();
2764
5415
  }, []);
2765
- const clearMessages = react.useCallback(() => {
5416
+ const clearMessages = React2.useCallback(() => {
2766
5417
  chatRef.current?.clearMessages();
2767
5418
  }, []);
2768
- const setMessages = react.useCallback((messages2) => {
5419
+ const setMessages = React2.useCallback((messages2) => {
2769
5420
  chatRef.current?.setMessages(messages2);
2770
5421
  }, []);
2771
- const regenerate = react.useCallback(async (messageId) => {
5422
+ const regenerate = React2.useCallback(async (messageId) => {
2772
5423
  await chatRef.current?.regenerate(messageId);
2773
5424
  }, []);
2774
- react.useEffect(() => {
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 coreMessages = messages.map((m) => ({
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
- react.useEffect(() => {
5473
+ React2.useEffect(() => {
2792
5474
  if (error && onError) {
2793
5475
  onError(error);
2794
5476
  }
2795
5477
  }, [error, onError]);
2796
- react.useEffect(() => {
5478
+ React2.useEffect(() => {
2797
5479
  return () => {
2798
5480
  chatRef.current?.dispose();
2799
5481
  };
2800
5482
  }, []);
2801
- const contextValue = react.useMemo(
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
- return /* @__PURE__ */ jsxRuntime.jsxs(CopilotContext.Provider, { value: contextValue, children: [
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
- children
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
- react.useEffect(() => {
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] = react.useState(screenshot || consoleCapture || network);
2953
- const [activeCaptures, setActiveCaptures] = react.useState({
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] = react.useState(null);
2958
- const consentResolverRef = react.useRef(null);
2959
- const rememberedConsentRef = react.useRef(/* @__PURE__ */ new Set());
2960
- react.useEffect(() => {
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 && !chunkOQPRIB73_cjs.isConsoleCaptureActive()) {
2963
- chunkOQPRIB73_cjs.startConsoleCapture(consoleOptions);
5629
+ if (consoleCapture && !chunkI3SQUNTT_cjs.isConsoleCaptureActive()) {
5630
+ chunkI3SQUNTT_cjs.startConsoleCapture(consoleOptions);
2964
5631
  setActiveCaptures((prev) => ({ ...prev, console: true }));
2965
5632
  }
2966
- if (network && !chunkOQPRIB73_cjs.isNetworkCaptureActive()) {
2967
- chunkOQPRIB73_cjs.startNetworkCapture(networkOptions);
5633
+ if (network && !chunkI3SQUNTT_cjs.isNetworkCaptureActive()) {
5634
+ chunkI3SQUNTT_cjs.startNetworkCapture(networkOptions);
2968
5635
  setActiveCaptures((prev) => ({ ...prev, network: true }));
2969
5636
  }
2970
5637
  return () => {
2971
- chunkOQPRIB73_cjs.stopConsoleCapture();
2972
- chunkOQPRIB73_cjs.stopNetworkCapture();
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 = react.useCallback(
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 (!chunkOQPRIB73_cjs.isScreenshotSupported()) {
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 chunkOQPRIB73_cjs.captureScreenshot({ ...screenshotOptions, ...opts });
5659
+ return chunkI3SQUNTT_cjs.captureScreenshot({ ...screenshotOptions, ...opts });
2993
5660
  },
2994
5661
  [screenshot, screenshotOptions]
2995
5662
  );
2996
- const getConsoleLogsFn = react.useCallback(
5663
+ const getConsoleLogsFn = React2.useCallback(
2997
5664
  (opts) => {
2998
5665
  if (!consoleCapture) {
2999
5666
  return { logs: [], totalCaptured: 0 };
3000
5667
  }
3001
- return chunkOQPRIB73_cjs.getConsoleLogs({ ...consoleOptions, ...opts });
5668
+ return chunkI3SQUNTT_cjs.getConsoleLogs({ ...consoleOptions, ...opts });
3002
5669
  },
3003
5670
  [consoleCapture, consoleOptions]
3004
5671
  );
3005
- const getNetworkRequestsFn = react.useCallback(
5672
+ const getNetworkRequestsFn = React2.useCallback(
3006
5673
  (opts) => {
3007
5674
  if (!network) {
3008
5675
  return { requests: [], totalCaptured: 0 };
3009
5676
  }
3010
- return chunkOQPRIB73_cjs.getNetworkRequests({ ...networkOptions, ...opts });
5677
+ return chunkI3SQUNTT_cjs.getNetworkRequests({ ...networkOptions, ...opts });
3011
5678
  },
3012
5679
  [network, networkOptions]
3013
5680
  );
3014
- const requestConsent = react.useCallback(
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 = react.useCallback((response) => {
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 = react.useCallback(
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 = react.useCallback(() => {
3103
- if (consoleCapture && !chunkOQPRIB73_cjs.isConsoleCaptureActive()) {
3104
- chunkOQPRIB73_cjs.startConsoleCapture(consoleOptions);
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 && !chunkOQPRIB73_cjs.isNetworkCaptureActive()) {
3108
- chunkOQPRIB73_cjs.startNetworkCapture(networkOptions);
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 = react.useCallback(() => {
3113
- chunkOQPRIB73_cjs.stopConsoleCapture();
3114
- chunkOQPRIB73_cjs.stopNetworkCapture();
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 = react.useCallback(() => {
3118
- chunkOQPRIB73_cjs.clearConsoleLogs();
3119
- chunkOQPRIB73_cjs.clearNetworkRequests();
5784
+ const clearCaptured = React2.useCallback(() => {
5785
+ chunkI3SQUNTT_cjs.clearConsoleLogs();
5786
+ chunkI3SQUNTT_cjs.clearNetworkRequests();
3120
5787
  }, []);
3121
- const formatForAI = react.useCallback((context) => {
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(chunkOQPRIB73_cjs.formatLogsForAI(context.consoleLogs.logs));
5796
+ parts.push(chunkI3SQUNTT_cjs.formatLogsForAI(context.consoleLogs.logs));
3130
5797
  }
3131
5798
  if (context.networkRequests && context.networkRequests.requests.length > 0) {
3132
- parts.push(chunkOQPRIB73_cjs.formatRequestsForAI(context.networkRequests.requests));
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 = react.useCallback(
5803
+ const detectIntentFn = React2.useCallback(
3137
5804
  (message) => {
3138
- const result = chunkOQPRIB73_cjs.detectIntent(message);
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 react.useMemo(
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 chunkOQPRIB73_cjs.zodObjectToInputSchema(schema);
5868
+ return chunkI3SQUNTT_cjs.zodObjectToInputSchema(schema);
3265
5869
  }
3266
5870
  function useToolWithSchema(config, dependencies = []) {
3267
5871
  const { registerTool, unregisterTool } = useCopilot();
3268
- const configRef = react.useRef(config);
5872
+ const configRef = React2.useRef(config);
3269
5873
  configRef.current = config;
3270
- const inputSchema = react.useMemo(() => {
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
- react.useEffect(() => {
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 = react.useRef(tools);
5908
+ const toolsRef = React2.useRef(tools);
3305
5909
  toolsRef.current = tools;
3306
- react.useEffect(() => {
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 = react.createContext(null);
5949
+ var CopilotContext2 = React2.createContext(null);
3346
5950
  function useCopilotContext() {
3347
- const context = react.useContext(CopilotContext2);
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 = react.useRef(registeredTools);
5967
+ const toolsRef = React2.useRef(registeredTools);
3364
5968
  toolsRef.current = registeredTools;
3365
- const executeTool = react.useCallback(
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 = react.useCallback(
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 = react.useCallback((name) => {
6053
+ const getTool = React2.useCallback((name) => {
3450
6054
  return toolsRef.current.find((t) => t.name === name);
3451
6055
  }, []);
3452
- const hasTool = react.useCallback((name) => {
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] = react.useState([]);
3471
- const [isLoading, setIsLoading] = react.useState(false);
3472
- const normalizedStatic = react.useMemo(
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 = react.useCallback(async () => {
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 = react.useCallback(
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
- react.useEffect(() => {
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] = react.useState(initialState);
3538
- const [isRunning, setIsRunning] = react.useState(false);
3539
- const [nodeName, setNodeName] = react.useState(null);
3540
- const [error, setError] = react.useState(null);
3541
- const abortControllerRef = react.useRef(null);
3542
- const getEndpoint = react.useCallback(() => {
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 = react.useCallback(
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 chunkOQPRIB73_cjs.streamSSE(response)) {
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 = react.useCallback(
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 = react.useCallback(() => {
6207
+ const stop = React2.useCallback(() => {
3604
6208
  abortControllerRef.current?.abort();
3605
6209
  }, []);
3606
- const setState = react.useCallback(
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
- react.useEffect(() => {
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 = react.useRef(config);
6301
+ const configRef = React2.useRef(config);
3698
6302
  configRef.current = config;
3699
- const handleSearch = react.useCallback(
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
- react.useEffect(() => {
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] = react.useState(DEFAULT_CAPABILITIES);
3785
- const [provider, setProvider] = react.useState("unknown");
3786
- const [model, setModel] = react.useState("unknown");
3787
- const [supportedModels, setSupportedModels] = react.useState([]);
3788
- const [isLoading, setIsLoading] = react.useState(true);
3789
- const [error, setError] = react.useState(null);
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 = react.useCallback(async () => {
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
- react.useEffect(() => {
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 react.useMemo(() => {
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 chunkOQPRIB73_cjs.ThreadManager {
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 = react.useMemo(() => {
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 = react.useSyncExternalStore(
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 = react.useSyncExternalStore(
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 = react.useSyncExternalStore(
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 = react.useSyncExternalStore(
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 = react.useSyncExternalStore(
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 = react.useSyncExternalStore(
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
- react.useEffect(() => {
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
- react.useEffect(() => {
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 = react.useCallback(
6887
+ const createThread = React2.useCallback(
4284
6888
  (options) => manager.createThread(options),
4285
6889
  [manager]
4286
6890
  );
4287
- const switchThread = react.useCallback(
6891
+ const switchThread = React2.useCallback(
4288
6892
  (id) => manager.switchThread(id),
4289
6893
  [manager]
4290
6894
  );
4291
- const updateCurrentThread = react.useCallback(
6895
+ const updateCurrentThread = React2.useCallback(
4292
6896
  (updates) => manager.updateCurrentThread(updates),
4293
6897
  [manager]
4294
6898
  );
4295
- const deleteThread = react.useCallback(
6899
+ const deleteThread = React2.useCallback(
4296
6900
  (id) => manager.deleteThread(id),
4297
6901
  [manager]
4298
6902
  );
4299
- const clearCurrentThread = react.useCallback(
6903
+ const clearCurrentThread = React2.useCallback(
4300
6904
  () => manager.clearCurrentThread(),
4301
6905
  [manager]
4302
6906
  );
4303
- const refreshThreads = react.useCallback(() => manager.loadThreads(), [manager]);
4304
- const saveNow = react.useCallback(() => manager.saveNow(), [manager]);
4305
- const clearAllThreads = react.useCallback(
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 = react.useMemo(
6913
+ const messages = React2.useMemo(
4310
6914
  () => currentThread?.messages ?? [],
4311
6915
  [currentThread]
4312
6916
  );
4313
- const setMessages = react.useCallback(
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 = react.useCallback(
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 react.useMemo(
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 = react.useRef(null);
4605
- const [input, setInput] = react.useState("");
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 = react.useSyncExternalStore(
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 = react.useSyncExternalStore(
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 = react.useSyncExternalStore(
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 = react.useCallback(
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 = react.useCallback(() => {
7378
+ const stop = React2.useCallback(() => {
4654
7379
  chatRef.current?.stop();
4655
7380
  }, []);
4656
- const clearMessages = react.useCallback(() => {
7381
+ const clearMessages = React2.useCallback(() => {
4657
7382
  chatRef.current?.clearMessages();
4658
7383
  }, []);
4659
- const setMessages = react.useCallback((messages2) => {
7384
+ const setMessages = React2.useCallback((messages2) => {
4660
7385
  chatRef.current?.setMessages(messages2);
4661
7386
  }, []);
4662
- const regenerate = react.useCallback(async (messageId) => {
7387
+ const regenerate = React2.useCallback(async (messageId) => {
4663
7388
  await chatRef.current?.regenerate(messageId);
4664
7389
  }, []);
4665
- const continueWithToolResults = react.useCallback(
7390
+ const continueWithToolResults = React2.useCallback(
4666
7391
  async (toolResults) => {
4667
7392
  await chatRef.current?.continueWithToolResults(toolResults);
4668
7393
  },
4669
7394
  []
4670
7395
  );
4671
- react.useEffect(() => {
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-N6VZ7FOW.cjs.map
4735
- //# sourceMappingURL=chunk-N6VZ7FOW.cjs.map
7502
+ //# sourceMappingURL=chunk-IXWNDR7H.cjs.map
7503
+ //# sourceMappingURL=chunk-IXWNDR7H.cjs.map