@deepagents/text2sql 0.13.1 → 0.15.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 (67) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2446 -2879
  4. package/dist/index.js.map +4 -4
  5. package/dist/lib/adapters/adapter.d.ts +2 -0
  6. package/dist/lib/adapters/adapter.d.ts.map +1 -1
  7. package/dist/lib/adapters/bigquery/bigquery.d.ts +41 -0
  8. package/dist/lib/adapters/bigquery/bigquery.d.ts.map +1 -0
  9. package/dist/lib/adapters/bigquery/constraint.bigquery.grounding.d.ts +11 -0
  10. package/dist/lib/adapters/bigquery/constraint.bigquery.grounding.d.ts.map +1 -0
  11. package/dist/lib/adapters/bigquery/index.d.ts +35 -0
  12. package/dist/lib/adapters/bigquery/index.d.ts.map +1 -0
  13. package/dist/lib/adapters/bigquery/index.js +1321 -0
  14. package/dist/lib/adapters/bigquery/index.js.map +7 -0
  15. package/dist/lib/adapters/bigquery/indexes.bigquery.grounding.d.ts +14 -0
  16. package/dist/lib/adapters/bigquery/indexes.bigquery.grounding.d.ts.map +1 -0
  17. package/dist/lib/adapters/bigquery/info.bigquery.grounding.d.ts +9 -0
  18. package/dist/lib/adapters/bigquery/info.bigquery.grounding.d.ts.map +1 -0
  19. package/dist/lib/adapters/bigquery/row-count.bigquery.grounding.d.ts +14 -0
  20. package/dist/lib/adapters/bigquery/row-count.bigquery.grounding.d.ts.map +1 -0
  21. package/dist/lib/adapters/bigquery/table.bigquery.grounding.d.ts +15 -0
  22. package/dist/lib/adapters/bigquery/table.bigquery.grounding.d.ts.map +1 -0
  23. package/dist/lib/adapters/bigquery/view.bigquery.grounding.d.ts +12 -0
  24. package/dist/lib/adapters/bigquery/view.bigquery.grounding.d.ts.map +1 -0
  25. package/dist/lib/adapters/groundings/index.js +9 -2044
  26. package/dist/lib/adapters/groundings/index.js.map +4 -4
  27. package/dist/lib/adapters/mysql/index.js +9 -2044
  28. package/dist/lib/adapters/mysql/index.js.map +4 -4
  29. package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts +2 -4
  30. package/dist/lib/adapters/postgres/column-stats.postgres.grounding.d.ts.map +1 -1
  31. package/dist/lib/adapters/postgres/column-values.postgres.grounding.d.ts +0 -8
  32. package/dist/lib/adapters/postgres/column-values.postgres.grounding.d.ts.map +1 -1
  33. package/dist/lib/adapters/postgres/index.js +140 -2056
  34. package/dist/lib/adapters/postgres/index.js.map +4 -4
  35. package/dist/lib/adapters/postgres/info.postgres.grounding.d.ts.map +1 -1
  36. package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts +0 -3
  37. package/dist/lib/adapters/postgres/row-count.postgres.grounding.d.ts.map +1 -1
  38. package/dist/lib/adapters/spreadsheet/index.js +10 -35
  39. package/dist/lib/adapters/spreadsheet/index.js.map +4 -4
  40. package/dist/lib/adapters/sqlite/index.js +9 -2044
  41. package/dist/lib/adapters/sqlite/index.js.map +4 -4
  42. package/dist/lib/adapters/sqlserver/index.js +9 -2044
  43. package/dist/lib/adapters/sqlserver/index.js.map +4 -4
  44. package/dist/lib/agents/result-tools.d.ts +18 -23
  45. package/dist/lib/agents/result-tools.d.ts.map +1 -1
  46. package/dist/lib/fragments/schema.d.ts +2 -0
  47. package/dist/lib/fragments/schema.d.ts.map +1 -1
  48. package/dist/lib/fs/index.d.ts +5 -0
  49. package/dist/lib/fs/index.d.ts.map +1 -0
  50. package/dist/lib/fs/mssql/ddl.mssql-fs.d.ts +2 -0
  51. package/dist/lib/fs/mssql/ddl.mssql-fs.d.ts.map +1 -0
  52. package/dist/lib/fs/mssql/mssql-fs.d.ts +56 -0
  53. package/dist/lib/fs/mssql/mssql-fs.d.ts.map +1 -0
  54. package/dist/lib/fs/scoped-fs.d.ts +55 -0
  55. package/dist/lib/fs/scoped-fs.d.ts.map +1 -0
  56. package/dist/lib/fs/sqlite/sqlite-fs.d.ts +68 -0
  57. package/dist/lib/fs/sqlite/sqlite-fs.d.ts.map +1 -0
  58. package/dist/lib/fs/tracked-fs.d.ts +42 -0
  59. package/dist/lib/fs/tracked-fs.d.ts.map +1 -0
  60. package/dist/lib/instructions.d.ts.map +1 -1
  61. package/dist/lib/sql.d.ts +5 -6
  62. package/dist/lib/sql.d.ts.map +1 -1
  63. package/dist/lib/synthesis/index.js +223 -2088
  64. package/dist/lib/synthesis/index.js.map +4 -4
  65. package/package.json +18 -11
  66. package/dist/lib/agents/text2sql.agent.d.ts +0 -3
  67. package/dist/lib/agents/text2sql.agent.d.ts.map +0 -1
@@ -552,2054 +552,19 @@ var InfoGrounding = class extends AbstractGrounding {
552
552
  };
553
553
 
554
554
  // packages/text2sql/src/lib/adapters/groundings/report.grounding.ts
555
- import { groq as groq2 } from "@ai-sdk/groq";
555
+ import { groq } from "@ai-sdk/groq";
556
556
  import { tool } from "ai";
557
557
  import dedent from "dedent";
558
558
  import z from "zod";
559
559
  import "@deepagents/agent";
560
-
561
- // packages/context/dist/index.js
562
- import { mergeWith } from "lodash-es";
563
- import { encode } from "gpt-tokenizer";
564
- import { generateId } from "ai";
565
- import pluralize from "pluralize";
566
- import { titlecase } from "stringcase";
567
- import chalk from "chalk";
568
- import { defineCommand } from "just-bash";
569
- import spawn from "nano-spawn";
570
- import "bash-tool";
571
- import spawn2 from "nano-spawn";
572
- import {
573
- createBashTool
574
- } from "bash-tool";
575
- import YAML from "yaml";
576
- import { DatabaseSync } from "node:sqlite";
577
- import { groq } from "@ai-sdk/groq";
578
560
  import {
579
- NoSuchToolError,
580
- Output,
581
- convertToModelMessages,
582
- createUIMessageStream,
583
- generateId as generateId2,
584
- generateText,
585
- smoothStream,
586
- stepCountIs,
587
- streamText
588
- } from "ai";
589
- import chalk2 from "chalk";
590
- import "zod";
591
- import "@deepagents/agent";
592
- var defaultTokenizer = {
593
- encode(text) {
594
- return encode(text);
595
- },
596
- count(text) {
597
- return encode(text).length;
598
- }
599
- };
600
- var ModelsRegistry = class {
601
- #cache = /* @__PURE__ */ new Map();
602
- #loaded = false;
603
- #tokenizers = /* @__PURE__ */ new Map();
604
- #defaultTokenizer = defaultTokenizer;
605
- /**
606
- * Load models data from models.dev API
607
- */
608
- async load() {
609
- if (this.#loaded) return;
610
- const response = await fetch("https://models.dev/api.json");
611
- if (!response.ok) {
612
- throw new Error(`Failed to fetch models: ${response.statusText}`);
613
- }
614
- const data = await response.json();
615
- for (const [providerId, provider] of Object.entries(data)) {
616
- for (const [modelId, model] of Object.entries(provider.models)) {
617
- const info2 = {
618
- id: model.id,
619
- name: model.name,
620
- family: model.family,
621
- cost: model.cost,
622
- limit: model.limit,
623
- provider: providerId
624
- };
625
- this.#cache.set(`${providerId}:${modelId}`, info2);
626
- }
627
- }
628
- this.#loaded = true;
629
- }
630
- /**
631
- * Get model info by ID
632
- * @param modelId - Model ID (e.g., "openai:gpt-4o")
633
- */
634
- get(modelId) {
635
- return this.#cache.get(modelId);
636
- }
637
- /**
638
- * Check if a model exists in the registry
639
- */
640
- has(modelId) {
641
- return this.#cache.has(modelId);
642
- }
643
- /**
644
- * List all available model IDs
645
- */
646
- list() {
647
- return [...this.#cache.keys()];
648
- }
649
- /**
650
- * Register a custom tokenizer for specific model families
651
- * @param family - Model family name (e.g., "llama", "claude")
652
- * @param tokenizer - Tokenizer implementation
653
- */
654
- registerTokenizer(family, tokenizer) {
655
- this.#tokenizers.set(family, tokenizer);
656
- }
657
- /**
658
- * Set the default tokenizer used when no family-specific tokenizer is registered
659
- */
660
- setDefaultTokenizer(tokenizer) {
661
- this.#defaultTokenizer = tokenizer;
662
- }
663
- /**
664
- * Get the appropriate tokenizer for a model
665
- */
666
- getTokenizer(modelId) {
667
- const model = this.get(modelId);
668
- if (model) {
669
- const familyTokenizer = this.#tokenizers.get(model.family);
670
- if (familyTokenizer) {
671
- return familyTokenizer;
672
- }
673
- }
674
- return this.#defaultTokenizer;
675
- }
676
- /**
677
- * Estimate token count and cost for given text and model
678
- * @param modelId - Model ID to use for pricing (e.g., "openai:gpt-4o")
679
- * @param input - Input text (prompt)
680
- */
681
- estimate(modelId, input) {
682
- const model = this.get(modelId);
683
- if (!model) {
684
- throw new Error(
685
- `Model "${modelId}" not found. Call load() first or check model ID.`
686
- );
687
- }
688
- const tokenizer = this.getTokenizer(modelId);
689
- const tokens = tokenizer.count(input);
690
- const cost = tokens / 1e6 * model.cost.input;
691
- return {
692
- model: model.id,
693
- provider: model.provider,
694
- tokens,
695
- cost,
696
- limits: {
697
- context: model.limit.context,
698
- output: model.limit.output,
699
- exceedsContext: tokens > model.limit.context
700
- },
701
- fragments: []
702
- };
703
- }
704
- };
705
- var _registry = null;
706
- function getModelsRegistry() {
707
- if (!_registry) {
708
- _registry = new ModelsRegistry();
709
- }
710
- return _registry;
711
- }
712
- function isFragment(data) {
713
- return typeof data === "object" && data !== null && "name" in data && "data" in data && typeof data.name === "string";
714
- }
715
- function isFragmentObject(data) {
716
- return typeof data === "object" && data !== null && !Array.isArray(data) && !isFragment(data);
717
- }
718
- function isMessageFragment(fragment2) {
719
- return fragment2.type === "message";
720
- }
721
- function fragment(name, ...children) {
722
- return {
723
- name,
724
- data: children
725
- };
726
- }
727
- function user(content) {
728
- const message2 = typeof content === "string" ? {
729
- id: generateId(),
730
- role: "user",
731
- parts: [{ type: "text", text: content }]
732
- } : content;
733
- return {
734
- id: message2.id,
735
- name: "user",
736
- data: "content",
737
- type: "message",
738
- persist: true,
739
- codec: {
740
- decode() {
741
- return message2;
742
- },
743
- encode() {
744
- return message2;
745
- }
746
- }
747
- };
748
- }
749
- function assistant(message2) {
750
- return {
751
- id: message2.id,
752
- name: "assistant",
753
- data: "content",
754
- type: "message",
755
- persist: true,
756
- codec: {
757
- decode() {
758
- return message2;
759
- },
760
- encode() {
761
- return message2;
762
- }
763
- }
764
- };
765
- }
766
- function message(content) {
767
- const message2 = typeof content === "string" ? {
768
- id: generateId(),
769
- role: "user",
770
- parts: [{ type: "text", text: content }]
771
- } : content;
772
- return {
773
- id: message2.id,
774
- name: message2.role,
775
- data: "content",
776
- type: "message",
777
- persist: true,
778
- codec: {
779
- decode() {
780
- return message2;
781
- },
782
- encode() {
783
- return message2;
784
- }
785
- }
786
- };
787
- }
788
- function assistantText(content, options) {
789
- const id = options?.id ?? crypto.randomUUID();
790
- return assistant({
791
- id,
792
- role: "assistant",
793
- parts: [{ type: "text", text: content }]
794
- });
795
- }
796
- var LAZY_ID = Symbol("lazy-id");
797
- function isLazyFragment(fragment2) {
798
- return LAZY_ID in fragment2;
799
- }
800
- function lastAssistantMessage(content) {
801
- return {
802
- name: "assistant",
803
- type: "message",
804
- persist: true,
805
- data: "content",
806
- [LAZY_ID]: {
807
- type: "last-assistant",
808
- content
809
- }
810
- };
811
- }
812
- var ContextRenderer = class {
813
- options;
814
- constructor(options = {}) {
815
- this.options = options;
816
- }
817
- /**
818
- * Check if data is a primitive (string, number, boolean).
819
- */
820
- isPrimitive(data) {
821
- return typeof data === "string" || typeof data === "number" || typeof data === "boolean";
822
- }
823
- /**
824
- * Group fragments by name for groupFragments option.
825
- */
826
- groupByName(fragments) {
827
- const groups = /* @__PURE__ */ new Map();
828
- for (const fragment2 of fragments) {
829
- const existing = groups.get(fragment2.name) ?? [];
830
- existing.push(fragment2);
831
- groups.set(fragment2.name, existing);
832
- }
833
- return groups;
834
- }
835
- /**
836
- * Remove null/undefined from fragments and fragment data recursively.
837
- * This protects renderers from nullish values and ensures they are ignored
838
- * consistently across all output formats.
839
- */
840
- sanitizeFragments(fragments) {
841
- const sanitized = [];
842
- for (const fragment2 of fragments) {
843
- const cleaned = this.sanitizeFragment(fragment2, /* @__PURE__ */ new WeakSet());
844
- if (cleaned) {
845
- sanitized.push(cleaned);
846
- }
847
- }
848
- return sanitized;
849
- }
850
- sanitizeFragment(fragment2, seen) {
851
- const data = this.sanitizeData(fragment2.data, seen);
852
- if (data == null) {
853
- return null;
854
- }
855
- return {
856
- ...fragment2,
857
- data
858
- };
859
- }
860
- sanitizeData(data, seen) {
861
- if (data == null) {
862
- return void 0;
863
- }
864
- if (isFragment(data)) {
865
- return this.sanitizeFragment(data, seen) ?? void 0;
866
- }
867
- if (Array.isArray(data)) {
868
- if (seen.has(data)) {
869
- return void 0;
870
- }
871
- seen.add(data);
872
- const cleaned = [];
873
- for (const item of data) {
874
- const sanitizedItem = this.sanitizeData(item, seen);
875
- if (sanitizedItem != null) {
876
- cleaned.push(sanitizedItem);
877
- }
878
- }
879
- return cleaned;
880
- }
881
- if (isFragmentObject(data)) {
882
- if (seen.has(data)) {
883
- return void 0;
884
- }
885
- seen.add(data);
886
- const cleaned = {};
887
- for (const [key, value] of Object.entries(data)) {
888
- const sanitizedValue = this.sanitizeData(value, seen);
889
- if (sanitizedValue != null) {
890
- cleaned[key] = sanitizedValue;
891
- }
892
- }
893
- return cleaned;
894
- }
895
- return data;
896
- }
897
- /**
898
- * Template method - dispatches value to appropriate handler.
899
- */
900
- renderValue(key, value, ctx) {
901
- if (value == null) {
902
- return "";
903
- }
904
- if (isFragment(value)) {
905
- return this.renderFragment(value, ctx);
906
- }
907
- if (Array.isArray(value)) {
908
- return this.renderArray(key, value, ctx);
909
- }
910
- if (isFragmentObject(value)) {
911
- return this.renderObject(key, value, ctx);
912
- }
913
- return this.renderPrimitive(key, String(value), ctx);
914
- }
915
- /**
916
- * Render all entries of an object.
917
- */
918
- renderEntries(data, ctx) {
919
- return Object.entries(data).map(([key, value]) => this.renderValue(key, value, ctx)).filter(Boolean);
920
- }
921
- };
922
- var XmlRenderer = class extends ContextRenderer {
923
- render(fragments) {
924
- const sanitized = this.sanitizeFragments(fragments);
925
- return sanitized.map((f) => this.#renderTopLevel(f)).filter(Boolean).join("\n");
926
- }
927
- #renderTopLevel(fragment2) {
928
- if (this.isPrimitive(fragment2.data)) {
929
- return this.#leafRoot(fragment2.name, String(fragment2.data));
930
- }
931
- if (Array.isArray(fragment2.data)) {
932
- return this.#renderArray(fragment2.name, fragment2.data, 0);
933
- }
934
- if (isFragment(fragment2.data)) {
935
- const child = this.renderFragment(fragment2.data, { depth: 1, path: [] });
936
- return this.#wrap(fragment2.name, [child]);
937
- }
938
- if (isFragmentObject(fragment2.data)) {
939
- return this.#wrap(
940
- fragment2.name,
941
- this.renderEntries(fragment2.data, { depth: 1, path: [] })
942
- );
943
- }
944
- return "";
945
- }
946
- #renderArray(name, items, depth) {
947
- const fragmentItems = items.filter(isFragment);
948
- const nonFragmentItems = items.filter((item) => !isFragment(item));
949
- const children = [];
950
- for (const item of nonFragmentItems) {
951
- if (item != null) {
952
- if (isFragmentObject(item)) {
953
- children.push(
954
- this.#wrapIndented(
955
- pluralize.singular(name),
956
- this.renderEntries(item, { depth: depth + 2, path: [] }),
957
- depth + 1
958
- )
959
- );
960
- } else {
961
- children.push(
962
- this.#leaf(pluralize.singular(name), String(item), depth + 1)
963
- );
964
- }
965
- }
966
- }
967
- if (this.options.groupFragments && fragmentItems.length > 0) {
968
- const groups = this.groupByName(fragmentItems);
969
- for (const [groupName, groupFragments] of groups) {
970
- const groupChildren = groupFragments.map(
971
- (frag) => this.renderFragment(frag, { depth: depth + 2, path: [] })
972
- );
973
- const pluralName = pluralize.plural(groupName);
974
- children.push(this.#wrapIndented(pluralName, groupChildren, depth + 1));
975
- }
976
- } else {
977
- for (const frag of fragmentItems) {
978
- children.push(
979
- this.renderFragment(frag, { depth: depth + 1, path: [] })
980
- );
981
- }
982
- }
983
- return this.#wrap(name, children);
984
- }
985
- #leafRoot(tag, value) {
986
- const safe = this.#escape(value);
987
- if (safe.includes("\n")) {
988
- return `<${tag}>
989
- ${this.#indent(safe, 2)}
990
- </${tag}>`;
991
- }
992
- return `<${tag}>${safe}</${tag}>`;
993
- }
994
- renderFragment(fragment2, ctx) {
995
- const { name, data } = fragment2;
996
- if (this.isPrimitive(data)) {
997
- return this.#leaf(name, String(data), ctx.depth);
998
- }
999
- if (isFragment(data)) {
1000
- const child = this.renderFragment(data, { ...ctx, depth: ctx.depth + 1 });
1001
- return this.#wrapIndented(name, [child], ctx.depth);
1002
- }
1003
- if (Array.isArray(data)) {
1004
- return this.#renderArrayIndented(name, data, ctx.depth);
1005
- }
1006
- if (isFragmentObject(data)) {
1007
- const children = this.renderEntries(data, {
1008
- ...ctx,
1009
- depth: ctx.depth + 1
1010
- });
1011
- return this.#wrapIndented(name, children, ctx.depth);
1012
- }
1013
- return "";
1014
- }
1015
- #renderArrayIndented(name, items, depth) {
1016
- const fragmentItems = items.filter(isFragment);
1017
- const nonFragmentItems = items.filter((item) => !isFragment(item));
1018
- const children = [];
1019
- for (const item of nonFragmentItems) {
1020
- if (item != null) {
1021
- if (isFragmentObject(item)) {
1022
- children.push(
1023
- this.#wrapIndented(
1024
- pluralize.singular(name),
1025
- this.renderEntries(item, { depth: depth + 2, path: [] }),
1026
- depth + 1
1027
- )
1028
- );
1029
- } else {
1030
- children.push(
1031
- this.#leaf(pluralize.singular(name), String(item), depth + 1)
1032
- );
1033
- }
1034
- }
1035
- }
1036
- if (this.options.groupFragments && fragmentItems.length > 0) {
1037
- const groups = this.groupByName(fragmentItems);
1038
- for (const [groupName, groupFragments] of groups) {
1039
- const groupChildren = groupFragments.map(
1040
- (frag) => this.renderFragment(frag, { depth: depth + 2, path: [] })
1041
- );
1042
- const pluralName = pluralize.plural(groupName);
1043
- children.push(this.#wrapIndented(pluralName, groupChildren, depth + 1));
1044
- }
1045
- } else {
1046
- for (const frag of fragmentItems) {
1047
- children.push(
1048
- this.renderFragment(frag, { depth: depth + 1, path: [] })
1049
- );
1050
- }
1051
- }
1052
- return this.#wrapIndented(name, children, depth);
1053
- }
1054
- renderPrimitive(key, value, ctx) {
1055
- return this.#leaf(key, value, ctx.depth);
1056
- }
1057
- renderArray(key, items, ctx) {
1058
- if (!items.length) {
1059
- return "";
1060
- }
1061
- const itemTag = pluralize.singular(key);
1062
- const children = items.filter((item) => item != null).map((item) => {
1063
- if (isFragment(item)) {
1064
- return this.renderFragment(item, { ...ctx, depth: ctx.depth + 1 });
1065
- }
1066
- if (isFragmentObject(item)) {
1067
- return this.#wrapIndented(
1068
- itemTag,
1069
- this.renderEntries(item, { ...ctx, depth: ctx.depth + 2 }),
1070
- ctx.depth + 1
1071
- );
1072
- }
1073
- return this.#leaf(itemTag, String(item), ctx.depth + 1);
1074
- });
1075
- return this.#wrapIndented(key, children, ctx.depth);
1076
- }
1077
- renderObject(key, obj, ctx) {
1078
- const children = this.renderEntries(obj, { ...ctx, depth: ctx.depth + 1 });
1079
- return this.#wrapIndented(key, children, ctx.depth);
1080
- }
1081
- #escape(value) {
1082
- if (value == null) {
1083
- return "";
1084
- }
1085
- return value.replaceAll(/&/g, "&amp;").replaceAll(/</g, "&lt;").replaceAll(/>/g, "&gt;").replaceAll(/"/g, "&quot;").replaceAll(/'/g, "&apos;");
1086
- }
1087
- #indent(text, spaces) {
1088
- if (!text.trim()) {
1089
- return "";
1090
- }
1091
- const padding = " ".repeat(spaces);
1092
- return text.split("\n").map((line) => line.length ? padding + line : padding).join("\n");
1093
- }
1094
- #leaf(tag, value, depth) {
1095
- const safe = this.#escape(value);
1096
- const pad = " ".repeat(depth);
1097
- if (safe.includes("\n")) {
1098
- return `${pad}<${tag}>
1099
- ${this.#indent(safe, (depth + 1) * 2)}
1100
- ${pad}</${tag}>`;
1101
- }
1102
- return `${pad}<${tag}>${safe}</${tag}>`;
1103
- }
1104
- #wrap(tag, children) {
1105
- const content = children.filter(Boolean).join("\n");
1106
- if (!content) {
1107
- return "";
1108
- }
1109
- return `<${tag}>
1110
- ${content}
1111
- </${tag}>`;
1112
- }
1113
- #wrapIndented(tag, children, depth) {
1114
- const content = children.filter(Boolean).join("\n");
1115
- if (!content) {
1116
- return "";
1117
- }
1118
- const pad = " ".repeat(depth);
1119
- return `${pad}<${tag}>
1120
- ${content}
1121
- ${pad}</${tag}>`;
1122
- }
1123
- };
1124
- var ContextStore = class {
1125
- };
1126
- var ContextEngine = class {
1127
- /** Non-message fragments (role, hints, etc.) - not persisted in graph */
1128
- #fragments = [];
1129
- /** Pending message fragments to be added to graph */
1130
- #pendingMessages = [];
1131
- #store;
1132
- #chatId;
1133
- #userId;
1134
- #branchName;
1135
- #branch = null;
1136
- #chatData = null;
1137
- #initialized = false;
1138
- /** Initial metadata to merge on first initialization */
1139
- #initialMetadata;
1140
- constructor(options) {
1141
- if (!options.chatId) {
1142
- throw new Error("chatId is required");
1143
- }
1144
- if (!options.userId) {
1145
- throw new Error("userId is required");
1146
- }
1147
- this.#store = options.store;
1148
- this.#chatId = options.chatId;
1149
- this.#userId = options.userId;
1150
- this.#branchName = "main";
1151
- this.#initialMetadata = options.metadata;
1152
- }
1153
- /**
1154
- * Initialize the chat and branch if they don't exist.
1155
- */
1156
- async #ensureInitialized() {
1157
- if (this.#initialized) {
1158
- return;
1159
- }
1160
- this.#chatData = await this.#store.upsertChat({
1161
- id: this.#chatId,
1162
- userId: this.#userId
1163
- });
1164
- if (this.#initialMetadata) {
1165
- this.#chatData = await this.#store.updateChat(this.#chatId, {
1166
- metadata: {
1167
- ...this.#chatData.metadata,
1168
- ...this.#initialMetadata
1169
- }
1170
- });
1171
- this.#initialMetadata = void 0;
1172
- }
1173
- this.#branch = await this.#store.getActiveBranch(this.#chatId);
1174
- this.#initialized = true;
1175
- }
1176
- /**
1177
- * Create a new branch from a specific message.
1178
- * Shared logic between rewind() and btw().
1179
- */
1180
- async #createBranchFrom(messageId, switchTo) {
1181
- const branches = await this.#store.listBranches(this.#chatId);
1182
- const samePrefix = branches.filter(
1183
- (b) => b.name === this.#branchName || b.name.startsWith(`${this.#branchName}-v`)
1184
- );
1185
- const newBranchName = `${this.#branchName}-v${samePrefix.length + 1}`;
1186
- const newBranch = {
1187
- id: crypto.randomUUID(),
1188
- chatId: this.#chatId,
1189
- name: newBranchName,
1190
- headMessageId: messageId,
1191
- isActive: false,
1192
- createdAt: Date.now()
1193
- };
1194
- await this.#store.createBranch(newBranch);
1195
- if (switchTo) {
1196
- await this.#store.setActiveBranch(this.#chatId, newBranch.id);
1197
- this.#branch = { ...newBranch, isActive: true };
1198
- this.#branchName = newBranchName;
1199
- this.#pendingMessages = [];
1200
- }
1201
- const chain = await this.#store.getMessageChain(messageId);
1202
- return {
1203
- id: newBranch.id,
1204
- name: newBranch.name,
1205
- headMessageId: newBranch.headMessageId,
1206
- isActive: switchTo,
1207
- messageCount: chain.length,
1208
- createdAt: newBranch.createdAt
1209
- };
1210
- }
1211
- /**
1212
- * Get the current chat ID.
1213
- */
1214
- get chatId() {
1215
- return this.#chatId;
1216
- }
1217
- /**
1218
- * Get the current branch name.
1219
- */
1220
- get branch() {
1221
- return this.#branchName;
1222
- }
1223
- /**
1224
- * Get metadata for the current chat.
1225
- * Returns null if the chat hasn't been initialized yet.
1226
- */
1227
- get chat() {
1228
- if (!this.#chatData) {
1229
- return null;
1230
- }
1231
- return {
1232
- id: this.#chatData.id,
1233
- userId: this.#chatData.userId,
1234
- createdAt: this.#chatData.createdAt,
1235
- updatedAt: this.#chatData.updatedAt,
1236
- title: this.#chatData.title,
1237
- metadata: this.#chatData.metadata
1238
- };
1239
- }
1240
- /**
1241
- * Add fragments to the context.
1242
- *
1243
- * - Message fragments (user/assistant) are queued for persistence
1244
- * - Non-message fragments (role/hint) are kept in memory for system prompt
1245
- */
1246
- set(...fragments) {
1247
- for (const fragment2 of fragments) {
1248
- if (isMessageFragment(fragment2)) {
1249
- this.#pendingMessages.push(fragment2);
1250
- } else {
1251
- this.#fragments.push(fragment2);
1252
- }
1253
- }
1254
- return this;
1255
- }
1256
- // Unset a fragment by ID (not implemented yet)
1257
- unset(fragmentId) {
1258
- }
1259
- /**
1260
- * Render all fragments using the provided renderer.
1261
- * @internal Use resolve() instead for public API.
1262
- */
1263
- render(renderer) {
1264
- return renderer.render(this.#fragments);
1265
- }
1266
- /**
1267
- * Resolve context into AI SDK-ready format.
1268
- *
1269
- * - Initializes chat and branch if needed
1270
- * - Loads message history from the graph (walking parent chain)
1271
- * - Separates context fragments for system prompt
1272
- * - Combines with pending messages
1273
- *
1274
- * @example
1275
- * ```ts
1276
- * const context = new ContextEngine({ store, chatId: 'chat-1', userId: 'user-1' })
1277
- * .set(role('You are helpful'), user('Hello'));
1278
- *
1279
- * const { systemPrompt, messages } = await context.resolve();
1280
- * await generateText({ system: systemPrompt, messages });
1281
- * ```
1282
- */
1283
- async resolve(options) {
1284
- await this.#ensureInitialized();
1285
- const systemPrompt = options.renderer.render(this.#fragments);
1286
- const messages = [];
1287
- if (this.#branch?.headMessageId) {
1288
- const chain = await this.#store.getMessageChain(
1289
- this.#branch.headMessageId
1290
- );
1291
- for (const msg of chain) {
1292
- messages.push(message(msg.data).codec?.decode());
1293
- }
1294
- }
1295
- for (const fragment2 of this.#pendingMessages) {
1296
- const decoded = fragment2.codec.decode();
1297
- messages.push(decoded);
1298
- }
1299
- return { systemPrompt, messages };
1300
- }
1301
- /**
1302
- * Save pending messages to the graph.
1303
- *
1304
- * Each message is added as a node with parentId pointing to the previous message.
1305
- * The branch head is updated to point to the last message.
1306
- *
1307
- * @example
1308
- * ```ts
1309
- * context.set(user('Hello'));
1310
- * // AI responds...
1311
- * context.set(assistant('Hi there!'));
1312
- * await context.save(); // Persist to graph
1313
- * ```
1314
- */
1315
- async save() {
1316
- await this.#ensureInitialized();
1317
- if (this.#pendingMessages.length === 0) {
1318
- return;
1319
- }
1320
- for (let i = 0; i < this.#pendingMessages.length; i++) {
1321
- const fragment2 = this.#pendingMessages[i];
1322
- if (isLazyFragment(fragment2)) {
1323
- this.#pendingMessages[i] = await this.#resolveLazyFragment(fragment2);
1324
- }
1325
- }
1326
- let parentId = this.#branch.headMessageId;
1327
- const now = Date.now();
1328
- for (const fragment2 of this.#pendingMessages) {
1329
- const messageData = {
1330
- id: fragment2.id ?? crypto.randomUUID(),
1331
- chatId: this.#chatId,
1332
- parentId,
1333
- name: fragment2.name,
1334
- type: fragment2.type,
1335
- data: fragment2.codec.encode(),
1336
- createdAt: now
1337
- };
1338
- await this.#store.addMessage(messageData);
1339
- parentId = messageData.id;
1340
- }
1341
- await this.#store.updateBranchHead(this.#branch.id, parentId);
1342
- this.#branch.headMessageId = parentId;
1343
- this.#pendingMessages = [];
1344
- }
1345
- /**
1346
- * Resolve a lazy fragment by finding the appropriate ID.
1347
- */
1348
- async #resolveLazyFragment(fragment2) {
1349
- const lazy = fragment2[LAZY_ID];
1350
- if (lazy.type === "last-assistant") {
1351
- const lastId = await this.#getLastAssistantId();
1352
- return assistantText(lazy.content, { id: lastId ?? crypto.randomUUID() });
1353
- }
1354
- throw new Error(`Unknown lazy fragment type: ${lazy.type}`);
1355
- }
1356
- /**
1357
- * Find the most recent assistant message ID (pending or persisted).
1358
- */
1359
- async #getLastAssistantId() {
1360
- for (let i = this.#pendingMessages.length - 1; i >= 0; i--) {
1361
- const msg = this.#pendingMessages[i];
1362
- if (msg.name === "assistant" && !isLazyFragment(msg)) {
1363
- return msg.id;
1364
- }
1365
- }
1366
- if (this.#branch?.headMessageId) {
1367
- const chain = await this.#store.getMessageChain(
1368
- this.#branch.headMessageId
1369
- );
1370
- for (let i = chain.length - 1; i >= 0; i--) {
1371
- if (chain[i].name === "assistant") {
1372
- return chain[i].id;
1373
- }
1374
- }
1375
- }
1376
- return void 0;
1377
- }
1378
- /**
1379
- * Estimate token count and cost for the full context.
1380
- *
1381
- * Includes:
1382
- * - System prompt fragments (role, hints, etc.)
1383
- * - Persisted chat messages (from store)
1384
- * - Pending messages (not yet saved)
1385
- *
1386
- * @param modelId - Model ID (e.g., "openai:gpt-4o", "anthropic:claude-3-5-sonnet")
1387
- * @param options - Optional settings
1388
- * @returns Estimate result with token counts, costs, and per-fragment breakdown
1389
- */
1390
- async estimate(modelId, options = {}) {
1391
- await this.#ensureInitialized();
1392
- const renderer = options.renderer ?? new XmlRenderer();
1393
- const registry = getModelsRegistry();
1394
- await registry.load();
1395
- const model = registry.get(modelId);
1396
- if (!model) {
1397
- throw new Error(
1398
- `Model "${modelId}" not found. Call load() first or check model ID.`
1399
- );
1400
- }
1401
- const tokenizer = registry.getTokenizer(modelId);
1402
- const fragmentEstimates = [];
1403
- for (const fragment2 of this.#fragments) {
1404
- const rendered = renderer.render([fragment2]);
1405
- const tokens = tokenizer.count(rendered);
1406
- const cost = tokens / 1e6 * model.cost.input;
1407
- fragmentEstimates.push({
1408
- id: fragment2.id,
1409
- name: fragment2.name,
1410
- tokens,
1411
- cost
1412
- });
1413
- }
1414
- if (this.#branch?.headMessageId) {
1415
- const chain = await this.#store.getMessageChain(
1416
- this.#branch.headMessageId
1417
- );
1418
- for (const msg of chain) {
1419
- const content = String(msg.data);
1420
- const tokens = tokenizer.count(content);
1421
- const cost = tokens / 1e6 * model.cost.input;
1422
- fragmentEstimates.push({
1423
- name: msg.name,
1424
- id: msg.id,
1425
- tokens,
1426
- cost
1427
- });
1428
- }
1429
- }
1430
- for (const fragment2 of this.#pendingMessages) {
1431
- const content = String(fragment2.data);
1432
- const tokens = tokenizer.count(content);
1433
- const cost = tokens / 1e6 * model.cost.input;
1434
- fragmentEstimates.push({
1435
- name: fragment2.name,
1436
- id: fragment2.id,
1437
- tokens,
1438
- cost
1439
- });
1440
- }
1441
- const totalTokens = fragmentEstimates.reduce((sum, f) => sum + f.tokens, 0);
1442
- const totalCost = fragmentEstimates.reduce((sum, f) => sum + f.cost, 0);
1443
- return {
1444
- model: model.id,
1445
- provider: model.provider,
1446
- tokens: totalTokens,
1447
- cost: totalCost,
1448
- limits: {
1449
- context: model.limit.context,
1450
- output: model.limit.output,
1451
- exceedsContext: totalTokens > model.limit.context
1452
- },
1453
- fragments: fragmentEstimates
1454
- };
1455
- }
1456
- /**
1457
- * Rewind to a specific message by ID.
1458
- *
1459
- * Creates a new branch from that message, preserving the original branch.
1460
- * The new branch becomes active.
1461
- *
1462
- * @param messageId - The message ID to rewind to
1463
- * @returns The new branch info
1464
- *
1465
- * @example
1466
- * ```ts
1467
- * context.set(user('What is 2 + 2?', { id: 'q1' }));
1468
- * context.set(assistant('The answer is 5.', { id: 'wrong' })); // Oops!
1469
- * await context.save();
1470
- *
1471
- * // Rewind to the question, creates new branch
1472
- * const newBranch = await context.rewind('q1');
1473
- *
1474
- * // Now add correct answer on new branch
1475
- * context.set(assistant('The answer is 4.'));
1476
- * await context.save();
1477
- * ```
1478
- */
1479
- async rewind(messageId) {
1480
- await this.#ensureInitialized();
1481
- const message2 = await this.#store.getMessage(messageId);
1482
- if (!message2) {
1483
- throw new Error(`Message "${messageId}" not found`);
1484
- }
1485
- if (message2.chatId !== this.#chatId) {
1486
- throw new Error(`Message "${messageId}" belongs to a different chat`);
1487
- }
1488
- return this.#createBranchFrom(messageId, true);
1489
- }
1490
- /**
1491
- * Create a checkpoint at the current position.
1492
- *
1493
- * A checkpoint is a named pointer to the current branch head.
1494
- * Use restore() to return to this point later.
1495
- *
1496
- * @param name - Name for the checkpoint
1497
- * @returns The checkpoint info
1498
- *
1499
- * @example
1500
- * ```ts
1501
- * context.set(user('I want to learn a new skill.'));
1502
- * context.set(assistant('Would you like coding or cooking?'));
1503
- * await context.save();
1504
- *
1505
- * // Save checkpoint before user's choice
1506
- * const cp = await context.checkpoint('before-choice');
1507
- * ```
1508
- */
1509
- async checkpoint(name) {
1510
- await this.#ensureInitialized();
1511
- if (!this.#branch?.headMessageId) {
1512
- throw new Error("Cannot create checkpoint: no messages in conversation");
1513
- }
1514
- const checkpoint = {
1515
- id: crypto.randomUUID(),
1516
- chatId: this.#chatId,
1517
- name,
1518
- messageId: this.#branch.headMessageId,
1519
- createdAt: Date.now()
1520
- };
1521
- await this.#store.createCheckpoint(checkpoint);
1522
- return {
1523
- id: checkpoint.id,
1524
- name: checkpoint.name,
1525
- messageId: checkpoint.messageId,
1526
- createdAt: checkpoint.createdAt
1527
- };
1528
- }
1529
- /**
1530
- * Restore to a checkpoint by creating a new branch from that point.
1531
- *
1532
- * @param name - Name of the checkpoint to restore
1533
- * @returns The new branch info
1534
- *
1535
- * @example
1536
- * ```ts
1537
- * // User chose cooking, but wants to try coding path
1538
- * await context.restore('before-choice');
1539
- *
1540
- * context.set(user('I want to learn coding.'));
1541
- * context.set(assistant('Python is a great starting language!'));
1542
- * await context.save();
1543
- * ```
1544
- */
1545
- async restore(name) {
1546
- await this.#ensureInitialized();
1547
- const checkpoint = await this.#store.getCheckpoint(this.#chatId, name);
1548
- if (!checkpoint) {
1549
- throw new Error(
1550
- `Checkpoint "${name}" not found in chat "${this.#chatId}"`
1551
- );
1552
- }
1553
- return this.rewind(checkpoint.messageId);
1554
- }
1555
- /**
1556
- * Switch to a different branch by name.
1557
- *
1558
- * @param name - Branch name to switch to
1559
- *
1560
- * @example
1561
- * ```ts
1562
- * // List branches (via store)
1563
- * const branches = await store.listBranches(context.chatId);
1564
- * console.log(branches); // [{name: 'main', ...}, {name: 'main-v2', ...}]
1565
- *
1566
- * // Switch to original branch
1567
- * await context.switchBranch('main');
1568
- * ```
1569
- */
1570
- async switchBranch(name) {
1571
- await this.#ensureInitialized();
1572
- const branch = await this.#store.getBranch(this.#chatId, name);
1573
- if (!branch) {
1574
- throw new Error(`Branch "${name}" not found in chat "${this.#chatId}"`);
1575
- }
1576
- await this.#store.setActiveBranch(this.#chatId, branch.id);
1577
- this.#branch = { ...branch, isActive: true };
1578
- this.#branchName = name;
1579
- this.#pendingMessages = [];
1580
- }
1581
- /**
1582
- * Create a parallel branch from the current position ("by the way").
1583
- *
1584
- * Use this when you want to fork the conversation without leaving
1585
- * the current branch. Common use case: user wants to ask another
1586
- * question while waiting for the model to respond.
1587
- *
1588
- * Unlike rewind(), this method:
1589
- * - Uses the current HEAD (no messageId needed)
1590
- * - Does NOT switch to the new branch
1591
- * - Keeps pending messages intact
1592
- *
1593
- * @returns The new branch info (does not switch to it)
1594
- * @throws Error if no messages exist in the conversation
1595
- *
1596
- * @example
1597
- * ```ts
1598
- * // User asked a question, model is generating...
1599
- * context.set(user('What is the weather?'));
1600
- * await context.save();
1601
- *
1602
- * // User wants to ask something else without waiting
1603
- * const newBranch = await context.btw();
1604
- * // newBranch = { name: 'main-v2', ... }
1605
- *
1606
- * // Later, switch to the new branch and add the question
1607
- * await context.switchBranch(newBranch.name);
1608
- * context.set(user('Also, what time is it?'));
1609
- * await context.save();
1610
- * ```
1611
- */
1612
- async btw() {
1613
- await this.#ensureInitialized();
1614
- if (!this.#branch?.headMessageId) {
1615
- throw new Error("Cannot create btw branch: no messages in conversation");
1616
- }
1617
- return this.#createBranchFrom(this.#branch.headMessageId, false);
1618
- }
1619
- /**
1620
- * Update metadata for the current chat.
1621
- *
1622
- * @param updates - Partial metadata to merge (title, metadata)
1623
- *
1624
- * @example
1625
- * ```ts
1626
- * await context.updateChat({
1627
- * title: 'Coding Help Session',
1628
- * metadata: { tags: ['python', 'debugging'] }
1629
- * });
1630
- * ```
1631
- */
1632
- async updateChat(updates) {
1633
- await this.#ensureInitialized();
1634
- const storeUpdates = {};
1635
- if (updates.title !== void 0) {
1636
- storeUpdates.title = updates.title;
1637
- }
1638
- if (updates.metadata !== void 0) {
1639
- storeUpdates.metadata = {
1640
- ...this.#chatData?.metadata,
1641
- ...updates.metadata
1642
- };
1643
- }
1644
- this.#chatData = await this.#store.updateChat(this.#chatId, storeUpdates);
1645
- }
1646
- /**
1647
- * Track token usage for the current chat.
1648
- * Accumulates usage metrics in chat.metadata.usage.
1649
- *
1650
- * @param usage - Token usage from AI SDK (LanguageModelUsage)
1651
- *
1652
- * @example
1653
- * ```ts
1654
- * // In onFinish callback
1655
- * const usage = await result.totalUsage;
1656
- * await context.trackUsage(usage);
1657
- * ```
1658
- */
1659
- async trackUsage(usage) {
1660
- await this.#ensureInitialized();
1661
- const freshChatData = await this.#store.getChat(this.#chatId);
1662
- const currentUsage = freshChatData?.metadata?.usage ?? {};
1663
- const updatedUsage = mergeWith(
1664
- {},
1665
- currentUsage,
1666
- usage,
1667
- (a, b) => typeof a === "number" || typeof b === "number" ? (a ?? 0) + (b ?? 0) : void 0
1668
- );
1669
- this.#chatData = await this.#store.updateChat(this.#chatId, {
1670
- metadata: {
1671
- ...freshChatData?.metadata,
1672
- usage: updatedUsage
1673
- }
1674
- });
1675
- }
1676
- /**
1677
- * Consolidate context fragments (no-op for now).
1678
- *
1679
- * This is a placeholder for future functionality that merges context fragments
1680
- * using specific rules. Currently, it does nothing.
1681
- *
1682
- * @experimental
1683
- */
1684
- consolidate() {
1685
- return void 0;
1686
- }
1687
- /**
1688
- * Extract skill path mappings from available_skills fragments.
1689
- * Returns array of { host, sandbox } for mounting in sandbox filesystem.
1690
- *
1691
- * Reads the original `paths` configuration stored in fragment metadata
1692
- * by the skills() fragment helper.
1693
- *
1694
- * @example
1695
- * ```ts
1696
- * const context = new ContextEngine({ store, chatId, userId })
1697
- * .set(skills({ paths: [{ host: './skills', sandbox: '/skills' }] }));
1698
- *
1699
- * const mounts = context.getSkillMounts();
1700
- * // [{ host: './skills', sandbox: '/skills' }]
1701
- * ```
1702
- */
1703
- getSkillMounts() {
1704
- const mounts = [];
1705
- for (const fragment2 of this.#fragments) {
1706
- if (fragment2.name === "available_skills" && fragment2.metadata && Array.isArray(fragment2.metadata.paths)) {
1707
- for (const mapping of fragment2.metadata.paths) {
1708
- if (typeof mapping === "object" && mapping !== null && typeof mapping.host === "string" && typeof mapping.sandbox === "string") {
1709
- mounts.push({ host: mapping.host, sandbox: mapping.sandbox });
1710
- }
1711
- }
1712
- }
1713
- }
1714
- return mounts;
1715
- }
1716
- /**
1717
- * Inspect the full context state for debugging.
1718
- * Returns a JSON-serializable object with context information.
1719
- *
1720
- * @param options - Inspection options (modelId and renderer required)
1721
- * @returns Complete inspection data including estimates, rendered output, fragments, and graph
1722
- *
1723
- * @example
1724
- * ```ts
1725
- * const inspection = await context.inspect({
1726
- * modelId: 'openai:gpt-4o',
1727
- * renderer: new XmlRenderer(),
1728
- * });
1729
- * console.log(JSON.stringify(inspection, null, 2));
1730
- *
1731
- * // Or write to file for analysis
1732
- * await fs.writeFile('context-debug.json', JSON.stringify(inspection, null, 2));
1733
- * ```
1734
- */
1735
- async inspect(options) {
1736
- await this.#ensureInitialized();
1737
- const { renderer } = options;
1738
- const estimateResult = await this.estimate(options.modelId, { renderer });
1739
- const rendered = renderer.render(this.#fragments);
1740
- const persistedMessages = [];
1741
- if (this.#branch?.headMessageId) {
1742
- const chain = await this.#store.getMessageChain(
1743
- this.#branch.headMessageId
1744
- );
1745
- persistedMessages.push(...chain);
1746
- }
1747
- const graph = await this.#store.getGraph(this.#chatId);
1748
- return {
1749
- estimate: estimateResult,
1750
- rendered,
1751
- fragments: {
1752
- context: [...this.#fragments],
1753
- pending: [...this.#pendingMessages],
1754
- persisted: persistedMessages
1755
- },
1756
- graph,
1757
- meta: {
1758
- chatId: this.#chatId,
1759
- branch: this.#branchName,
1760
- timestamp: Date.now()
1761
- }
1762
- };
1763
- }
1764
- };
1765
- function persona(input) {
1766
- return {
1767
- name: "persona",
1768
- data: {
1769
- name: input.name,
1770
- ...input.role && { role: input.role },
1771
- ...input.objective && { objective: input.objective },
1772
- ...input.tone && { tone: input.tone }
1773
- }
1774
- };
1775
- }
1776
- function pass(part) {
1777
- return { type: "pass", part };
1778
- }
1779
- function runGuardrailChain(part, guardrails, context) {
1780
- let currentPart = part;
1781
- for (const guardrail2 of guardrails) {
1782
- const result = guardrail2.handle(currentPart, context);
1783
- if (result.type === "fail") {
1784
- return result;
1785
- }
1786
- currentPart = result.part;
1787
- }
1788
- return pass(currentPart);
1789
- }
1790
- var STORE_DDL = `
1791
- -- Chats table
1792
- -- createdAt/updatedAt: DEFAULT for insert, inline SET for updates
1793
- CREATE TABLE IF NOT EXISTS chats (
1794
- id TEXT PRIMARY KEY,
1795
- userId TEXT NOT NULL,
1796
- title TEXT,
1797
- metadata TEXT,
1798
- createdAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000),
1799
- updatedAt INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000)
1800
- );
1801
-
1802
- CREATE INDEX IF NOT EXISTS idx_chats_updatedAt ON chats(updatedAt);
1803
- CREATE INDEX IF NOT EXISTS idx_chats_userId ON chats(userId);
1804
-
1805
- -- Messages table (nodes in the DAG)
1806
- CREATE TABLE IF NOT EXISTS messages (
1807
- id TEXT PRIMARY KEY,
1808
- chatId TEXT NOT NULL,
1809
- parentId TEXT,
1810
- name TEXT NOT NULL,
1811
- type TEXT,
1812
- data TEXT NOT NULL,
1813
- createdAt INTEGER NOT NULL,
1814
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1815
- FOREIGN KEY (parentId) REFERENCES messages(id)
1816
- );
1817
-
1818
- CREATE INDEX IF NOT EXISTS idx_messages_chatId ON messages(chatId);
1819
- CREATE INDEX IF NOT EXISTS idx_messages_parentId ON messages(parentId);
1820
-
1821
- -- Branches table (pointers to head messages)
1822
- CREATE TABLE IF NOT EXISTS branches (
1823
- id TEXT PRIMARY KEY,
1824
- chatId TEXT NOT NULL,
1825
- name TEXT NOT NULL,
1826
- headMessageId TEXT,
1827
- isActive INTEGER NOT NULL DEFAULT 0,
1828
- createdAt INTEGER NOT NULL,
1829
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1830
- FOREIGN KEY (headMessageId) REFERENCES messages(id),
1831
- UNIQUE(chatId, name)
1832
- );
1833
-
1834
- CREATE INDEX IF NOT EXISTS idx_branches_chatId ON branches(chatId);
1835
-
1836
- -- Checkpoints table (pointers to message nodes)
1837
- CREATE TABLE IF NOT EXISTS checkpoints (
1838
- id TEXT PRIMARY KEY,
1839
- chatId TEXT NOT NULL,
1840
- name TEXT NOT NULL,
1841
- messageId TEXT NOT NULL,
1842
- createdAt INTEGER NOT NULL,
1843
- FOREIGN KEY (chatId) REFERENCES chats(id) ON DELETE CASCADE,
1844
- FOREIGN KEY (messageId) REFERENCES messages(id),
1845
- UNIQUE(chatId, name)
1846
- );
1847
-
1848
- CREATE INDEX IF NOT EXISTS idx_checkpoints_chatId ON checkpoints(chatId);
1849
-
1850
- -- FTS5 virtual table for full-text search
1851
- -- messageId/chatId/name are UNINDEXED (stored but not searchable, used for filtering/joining)
1852
- -- Only 'content' is indexed for full-text search
1853
- CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
1854
- messageId UNINDEXED,
1855
- chatId UNINDEXED,
1856
- name UNINDEXED,
1857
- content,
1858
- tokenize='porter unicode61'
1859
- );
1860
- `;
1861
- var SqliteContextStore = class extends ContextStore {
1862
- #db;
1863
- constructor(path3) {
1864
- super();
1865
- this.#db = new DatabaseSync(path3);
1866
- this.#db.exec("PRAGMA foreign_keys = ON");
1867
- this.#db.exec(STORE_DDL);
1868
- }
1869
- /**
1870
- * Execute a function within a transaction.
1871
- * Automatically commits on success or rolls back on error.
1872
- */
1873
- #useTransaction(fn) {
1874
- this.#db.exec("BEGIN TRANSACTION");
1875
- try {
1876
- const result = fn();
1877
- this.#db.exec("COMMIT");
1878
- return result;
1879
- } catch (error) {
1880
- this.#db.exec("ROLLBACK");
1881
- throw error;
1882
- }
1883
- }
1884
- // ==========================================================================
1885
- // Chat Operations
1886
- // ==========================================================================
1887
- async createChat(chat) {
1888
- this.#useTransaction(() => {
1889
- this.#db.prepare(
1890
- `INSERT INTO chats (id, userId, title, metadata)
1891
- VALUES (?, ?, ?, ?)`
1892
- ).run(
1893
- chat.id,
1894
- chat.userId,
1895
- chat.title ?? null,
1896
- chat.metadata ? JSON.stringify(chat.metadata) : null
1897
- );
1898
- this.#db.prepare(
1899
- `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
1900
- VALUES (?, ?, 'main', NULL, 1, ?)`
1901
- ).run(crypto.randomUUID(), chat.id, Date.now());
1902
- });
1903
- }
1904
- async upsertChat(chat) {
1905
- return this.#useTransaction(() => {
1906
- const row = this.#db.prepare(
1907
- `INSERT INTO chats (id, userId, title, metadata)
1908
- VALUES (?, ?, ?, ?)
1909
- ON CONFLICT(id) DO UPDATE SET id = excluded.id
1910
- RETURNING *`
1911
- ).get(
1912
- chat.id,
1913
- chat.userId,
1914
- chat.title ?? null,
1915
- chat.metadata ? JSON.stringify(chat.metadata) : null
1916
- );
1917
- this.#db.prepare(
1918
- `INSERT OR IGNORE INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
1919
- VALUES (?, ?, 'main', NULL, 1, ?)`
1920
- ).run(crypto.randomUUID(), chat.id, Date.now());
1921
- return {
1922
- id: row.id,
1923
- userId: row.userId,
1924
- title: row.title ?? void 0,
1925
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1926
- createdAt: row.createdAt,
1927
- updatedAt: row.updatedAt
1928
- };
1929
- });
1930
- }
1931
- async getChat(chatId) {
1932
- const row = this.#db.prepare("SELECT * FROM chats WHERE id = ?").get(chatId);
1933
- if (!row) {
1934
- return void 0;
1935
- }
1936
- return {
1937
- id: row.id,
1938
- userId: row.userId,
1939
- title: row.title ?? void 0,
1940
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1941
- createdAt: row.createdAt,
1942
- updatedAt: row.updatedAt
1943
- };
1944
- }
1945
- async updateChat(chatId, updates) {
1946
- const setClauses = ["updatedAt = strftime('%s', 'now') * 1000"];
1947
- const params = [];
1948
- if (updates.title !== void 0) {
1949
- setClauses.push("title = ?");
1950
- params.push(updates.title ?? null);
1951
- }
1952
- if (updates.metadata !== void 0) {
1953
- setClauses.push("metadata = ?");
1954
- params.push(JSON.stringify(updates.metadata));
1955
- }
1956
- params.push(chatId);
1957
- const row = this.#db.prepare(
1958
- `UPDATE chats SET ${setClauses.join(", ")} WHERE id = ? RETURNING *`
1959
- ).get(...params);
1960
- return {
1961
- id: row.id,
1962
- userId: row.userId,
1963
- title: row.title ?? void 0,
1964
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
1965
- createdAt: row.createdAt,
1966
- updatedAt: row.updatedAt
1967
- };
1968
- }
1969
- async listChats(options) {
1970
- const params = [];
1971
- const whereClauses = [];
1972
- let limitClause = "";
1973
- if (options?.userId) {
1974
- whereClauses.push("c.userId = ?");
1975
- params.push(options.userId);
1976
- }
1977
- if (options?.metadata) {
1978
- whereClauses.push(`json_extract(c.metadata, '$.' || ?) = ?`);
1979
- params.push(options.metadata.key);
1980
- params.push(
1981
- typeof options.metadata.value === "boolean" ? options.metadata.value ? 1 : 0 : options.metadata.value
1982
- );
1983
- }
1984
- const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
1985
- if (options?.limit !== void 0) {
1986
- limitClause = " LIMIT ?";
1987
- params.push(options.limit);
1988
- if (options.offset !== void 0) {
1989
- limitClause += " OFFSET ?";
1990
- params.push(options.offset);
1991
- }
1992
- }
1993
- const rows = this.#db.prepare(
1994
- `SELECT
1995
- c.id,
1996
- c.userId,
1997
- c.title,
1998
- c.metadata,
1999
- c.createdAt,
2000
- c.updatedAt,
2001
- COUNT(DISTINCT m.id) as messageCount,
2002
- COUNT(DISTINCT b.id) as branchCount
2003
- FROM chats c
2004
- LEFT JOIN messages m ON m.chatId = c.id
2005
- LEFT JOIN branches b ON b.chatId = c.id
2006
- ${whereClause}
2007
- GROUP BY c.id
2008
- ORDER BY c.updatedAt DESC${limitClause}`
2009
- ).all(...params);
2010
- return rows.map((row) => ({
2011
- id: row.id,
2012
- userId: row.userId,
2013
- title: row.title ?? void 0,
2014
- metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
2015
- messageCount: row.messageCount,
2016
- branchCount: row.branchCount,
2017
- createdAt: row.createdAt,
2018
- updatedAt: row.updatedAt
2019
- }));
2020
- }
2021
- async deleteChat(chatId, options) {
2022
- return this.#useTransaction(() => {
2023
- const messageIds = this.#db.prepare("SELECT id FROM messages WHERE chatId = ?").all(chatId);
2024
- let sql = "DELETE FROM chats WHERE id = ?";
2025
- const params = [chatId];
2026
- if (options?.userId !== void 0) {
2027
- sql += " AND userId = ?";
2028
- params.push(options.userId);
2029
- }
2030
- const result = this.#db.prepare(sql).run(...params);
2031
- if (result.changes > 0 && messageIds.length > 0) {
2032
- const placeholders = messageIds.map(() => "?").join(", ");
2033
- this.#db.prepare(
2034
- `DELETE FROM messages_fts WHERE messageId IN (${placeholders})`
2035
- ).run(...messageIds.map((m) => m.id));
2036
- }
2037
- return result.changes > 0;
2038
- });
2039
- }
2040
- // ==========================================================================
2041
- // Message Operations (Graph Nodes)
2042
- // ==========================================================================
2043
- async addMessage(message2) {
2044
- this.#db.prepare(
2045
- `INSERT INTO messages (id, chatId, parentId, name, type, data, createdAt)
2046
- VALUES (
2047
- ?1,
2048
- ?2,
2049
- CASE WHEN ?3 = ?1 THEN (SELECT parentId FROM messages WHERE id = ?1) ELSE ?3 END,
2050
- ?4,
2051
- ?5,
2052
- ?6,
2053
- ?7
2054
- )
2055
- ON CONFLICT(id) DO UPDATE SET
2056
- name = excluded.name,
2057
- type = excluded.type,
2058
- data = excluded.data`
2059
- ).run(
2060
- message2.id,
2061
- message2.chatId,
2062
- message2.parentId,
2063
- message2.name,
2064
- message2.type ?? null,
2065
- JSON.stringify(message2.data),
2066
- message2.createdAt
2067
- );
2068
- const content = typeof message2.data === "string" ? message2.data : JSON.stringify(message2.data);
2069
- this.#db.prepare(`DELETE FROM messages_fts WHERE messageId = ?`).run(message2.id);
2070
- this.#db.prepare(
2071
- `INSERT INTO messages_fts(messageId, chatId, name, content)
2072
- VALUES (?, ?, ?, ?)`
2073
- ).run(message2.id, message2.chatId, message2.name, content);
2074
- }
2075
- async getMessage(messageId) {
2076
- const row = this.#db.prepare("SELECT * FROM messages WHERE id = ?").get(messageId);
2077
- if (!row) {
2078
- return void 0;
2079
- }
2080
- return {
2081
- id: row.id,
2082
- chatId: row.chatId,
2083
- parentId: row.parentId,
2084
- name: row.name,
2085
- type: row.type ?? void 0,
2086
- data: JSON.parse(row.data),
2087
- createdAt: row.createdAt
2088
- };
2089
- }
2090
- async getMessageChain(headId) {
2091
- const rows = this.#db.prepare(
2092
- `WITH RECURSIVE chain AS (
2093
- SELECT *, 0 as depth FROM messages WHERE id = ?
2094
- UNION ALL
2095
- SELECT m.*, c.depth + 1 FROM messages m
2096
- INNER JOIN chain c ON m.id = c.parentId
2097
- )
2098
- SELECT * FROM chain
2099
- ORDER BY depth DESC`
2100
- ).all(headId);
2101
- return rows.map((row) => ({
2102
- id: row.id,
2103
- chatId: row.chatId,
2104
- parentId: row.parentId,
2105
- name: row.name,
2106
- type: row.type ?? void 0,
2107
- data: JSON.parse(row.data),
2108
- createdAt: row.createdAt
2109
- }));
2110
- }
2111
- async hasChildren(messageId) {
2112
- const row = this.#db.prepare(
2113
- "SELECT EXISTS(SELECT 1 FROM messages WHERE parentId = ?) as hasChildren"
2114
- ).get(messageId);
2115
- return row.hasChildren === 1;
2116
- }
2117
- async getMessages(chatId) {
2118
- const chat = await this.getChat(chatId);
2119
- if (!chat) {
2120
- throw new Error(`Chat "${chatId}" not found`);
2121
- }
2122
- const activeBranch = await this.getActiveBranch(chatId);
2123
- if (!activeBranch?.headMessageId) {
2124
- return [];
2125
- }
2126
- return this.getMessageChain(activeBranch.headMessageId);
2127
- }
2128
- // ==========================================================================
2129
- // Branch Operations
2130
- // ==========================================================================
2131
- async createBranch(branch) {
2132
- this.#db.prepare(
2133
- `INSERT INTO branches (id, chatId, name, headMessageId, isActive, createdAt)
2134
- VALUES (?, ?, ?, ?, ?, ?)`
2135
- ).run(
2136
- branch.id,
2137
- branch.chatId,
2138
- branch.name,
2139
- branch.headMessageId,
2140
- branch.isActive ? 1 : 0,
2141
- branch.createdAt
2142
- );
2143
- }
2144
- async getBranch(chatId, name) {
2145
- const row = this.#db.prepare("SELECT * FROM branches WHERE chatId = ? AND name = ?").get(chatId, name);
2146
- if (!row) {
2147
- return void 0;
2148
- }
2149
- return {
2150
- id: row.id,
2151
- chatId: row.chatId,
2152
- name: row.name,
2153
- headMessageId: row.headMessageId,
2154
- isActive: row.isActive === 1,
2155
- createdAt: row.createdAt
2156
- };
2157
- }
2158
- async getActiveBranch(chatId) {
2159
- const row = this.#db.prepare("SELECT * FROM branches WHERE chatId = ? AND isActive = 1").get(chatId);
2160
- if (!row) {
2161
- return void 0;
2162
- }
2163
- return {
2164
- id: row.id,
2165
- chatId: row.chatId,
2166
- name: row.name,
2167
- headMessageId: row.headMessageId,
2168
- isActive: true,
2169
- createdAt: row.createdAt
2170
- };
2171
- }
2172
- async setActiveBranch(chatId, branchId) {
2173
- this.#db.prepare("UPDATE branches SET isActive = 0 WHERE chatId = ?").run(chatId);
2174
- this.#db.prepare("UPDATE branches SET isActive = 1 WHERE id = ?").run(branchId);
2175
- }
2176
- async updateBranchHead(branchId, messageId) {
2177
- this.#db.prepare("UPDATE branches SET headMessageId = ? WHERE id = ?").run(messageId, branchId);
2178
- }
2179
- async listBranches(chatId) {
2180
- const branches = this.#db.prepare(
2181
- `SELECT
2182
- b.id,
2183
- b.name,
2184
- b.headMessageId,
2185
- b.isActive,
2186
- b.createdAt
2187
- FROM branches b
2188
- WHERE b.chatId = ?
2189
- ORDER BY b.createdAt ASC`
2190
- ).all(chatId);
2191
- const result = [];
2192
- for (const branch of branches) {
2193
- let messageCount = 0;
2194
- if (branch.headMessageId) {
2195
- const countRow = this.#db.prepare(
2196
- `WITH RECURSIVE chain AS (
2197
- SELECT id, parentId FROM messages WHERE id = ?
2198
- UNION ALL
2199
- SELECT m.id, m.parentId FROM messages m
2200
- INNER JOIN chain c ON m.id = c.parentId
2201
- )
2202
- SELECT COUNT(*) as count FROM chain`
2203
- ).get(branch.headMessageId);
2204
- messageCount = countRow.count;
2205
- }
2206
- result.push({
2207
- id: branch.id,
2208
- name: branch.name,
2209
- headMessageId: branch.headMessageId,
2210
- isActive: branch.isActive === 1,
2211
- messageCount,
2212
- createdAt: branch.createdAt
2213
- });
2214
- }
2215
- return result;
2216
- }
2217
- // ==========================================================================
2218
- // Checkpoint Operations
2219
- // ==========================================================================
2220
- async createCheckpoint(checkpoint) {
2221
- this.#db.prepare(
2222
- `INSERT INTO checkpoints (id, chatId, name, messageId, createdAt)
2223
- VALUES (?, ?, ?, ?, ?)
2224
- ON CONFLICT(chatId, name) DO UPDATE SET
2225
- messageId = excluded.messageId,
2226
- createdAt = excluded.createdAt`
2227
- ).run(
2228
- checkpoint.id,
2229
- checkpoint.chatId,
2230
- checkpoint.name,
2231
- checkpoint.messageId,
2232
- checkpoint.createdAt
2233
- );
2234
- }
2235
- async getCheckpoint(chatId, name) {
2236
- const row = this.#db.prepare("SELECT * FROM checkpoints WHERE chatId = ? AND name = ?").get(chatId, name);
2237
- if (!row) {
2238
- return void 0;
2239
- }
2240
- return {
2241
- id: row.id,
2242
- chatId: row.chatId,
2243
- name: row.name,
2244
- messageId: row.messageId,
2245
- createdAt: row.createdAt
2246
- };
2247
- }
2248
- async listCheckpoints(chatId) {
2249
- const rows = this.#db.prepare(
2250
- `SELECT id, name, messageId, createdAt
2251
- FROM checkpoints
2252
- WHERE chatId = ?
2253
- ORDER BY createdAt DESC`
2254
- ).all(chatId);
2255
- return rows.map((row) => ({
2256
- id: row.id,
2257
- name: row.name,
2258
- messageId: row.messageId,
2259
- createdAt: row.createdAt
2260
- }));
2261
- }
2262
- async deleteCheckpoint(chatId, name) {
2263
- this.#db.prepare("DELETE FROM checkpoints WHERE chatId = ? AND name = ?").run(chatId, name);
2264
- }
2265
- // ==========================================================================
2266
- // Search Operations
2267
- // ==========================================================================
2268
- async searchMessages(chatId, query, options) {
2269
- const limit = options?.limit ?? 20;
2270
- const roles = options?.roles;
2271
- let sql = `
2272
- SELECT
2273
- m.id,
2274
- m.chatId,
2275
- m.parentId,
2276
- m.name,
2277
- m.type,
2278
- m.data,
2279
- m.createdAt,
2280
- fts.rank,
2281
- snippet(messages_fts, 3, '<mark>', '</mark>', '...', 32) as snippet
2282
- FROM messages_fts fts
2283
- JOIN messages m ON m.id = fts.messageId
2284
- WHERE messages_fts MATCH ?
2285
- AND fts.chatId = ?
2286
- `;
2287
- const params = [query, chatId];
2288
- if (roles && roles.length > 0) {
2289
- const placeholders = roles.map(() => "?").join(", ");
2290
- sql += ` AND fts.name IN (${placeholders})`;
2291
- params.push(...roles);
2292
- }
2293
- sql += " ORDER BY fts.rank LIMIT ?";
2294
- params.push(limit);
2295
- const rows = this.#db.prepare(sql).all(...params);
2296
- return rows.map((row) => ({
2297
- message: {
2298
- id: row.id,
2299
- chatId: row.chatId,
2300
- parentId: row.parentId,
2301
- name: row.name,
2302
- type: row.type ?? void 0,
2303
- data: JSON.parse(row.data),
2304
- createdAt: row.createdAt
2305
- },
2306
- rank: row.rank,
2307
- snippet: row.snippet
2308
- }));
2309
- }
2310
- // ==========================================================================
2311
- // Visualization Operations
2312
- // ==========================================================================
2313
- async getGraph(chatId) {
2314
- const messageRows = this.#db.prepare(
2315
- `SELECT id, parentId, name, data, createdAt
2316
- FROM messages
2317
- WHERE chatId = ?
2318
- ORDER BY createdAt ASC`
2319
- ).all(chatId);
2320
- const nodes = messageRows.map((row) => {
2321
- const data = JSON.parse(row.data);
2322
- const content = typeof data === "string" ? data : JSON.stringify(data);
2323
- return {
2324
- id: row.id,
2325
- parentId: row.parentId,
2326
- role: row.name,
2327
- content: content.length > 50 ? content.slice(0, 50) + "..." : content,
2328
- createdAt: row.createdAt
2329
- };
2330
- });
2331
- const branchRows = this.#db.prepare(
2332
- `SELECT name, headMessageId, isActive
2333
- FROM branches
2334
- WHERE chatId = ?
2335
- ORDER BY createdAt ASC`
2336
- ).all(chatId);
2337
- const branches = branchRows.map((row) => ({
2338
- name: row.name,
2339
- headMessageId: row.headMessageId,
2340
- isActive: row.isActive === 1
2341
- }));
2342
- const checkpointRows = this.#db.prepare(
2343
- `SELECT name, messageId
2344
- FROM checkpoints
2345
- WHERE chatId = ?
2346
- ORDER BY createdAt ASC`
2347
- ).all(chatId);
2348
- const checkpoints = checkpointRows.map((row) => ({
2349
- name: row.name,
2350
- messageId: row.messageId
2351
- }));
2352
- return {
2353
- chatId,
2354
- nodes,
2355
- branches,
2356
- checkpoints
2357
- };
2358
- }
2359
- };
2360
- var InMemoryContextStore = class extends SqliteContextStore {
2361
- constructor() {
2362
- super(":memory:");
2363
- }
2364
- };
2365
- var Agent = class _Agent {
2366
- #options;
2367
- #guardrails = [];
2368
- tools;
2369
- constructor(options) {
2370
- this.#options = options;
2371
- this.tools = options.tools || {};
2372
- this.#guardrails = options.guardrails || [];
2373
- }
2374
- async generate(contextVariables, config) {
2375
- if (!this.#options.context) {
2376
- throw new Error(`Agent ${this.#options.name} is missing a context.`);
2377
- }
2378
- if (!this.#options.model) {
2379
- throw new Error(`Agent ${this.#options.name} is missing a model.`);
2380
- }
2381
- const { messages, systemPrompt } = await this.#options.context.resolve({
2382
- renderer: new XmlRenderer()
2383
- });
2384
- return generateText({
2385
- abortSignal: config?.abortSignal,
2386
- providerOptions: this.#options.providerOptions,
2387
- model: this.#options.model,
2388
- system: systemPrompt,
2389
- messages: await convertToModelMessages(messages),
2390
- stopWhen: stepCountIs(25),
2391
- tools: this.#options.tools,
2392
- experimental_context: contextVariables,
2393
- experimental_repairToolCall: repairToolCall,
2394
- toolChoice: this.#options.toolChoice,
2395
- onStepFinish: (step) => {
2396
- const toolCall = step.toolCalls.at(-1);
2397
- if (toolCall) {
2398
- console.log(
2399
- `Debug: ${chalk2.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
2400
- );
2401
- }
2402
- }
2403
- });
2404
- }
2405
- /**
2406
- * Stream a response from the agent.
2407
- *
2408
- * When guardrails are configured, `toUIMessageStream()` is wrapped to provide
2409
- * self-correction behavior. Direct access to fullStream/textStream bypasses guardrails.
2410
- *
2411
- * @example
2412
- * ```typescript
2413
- * const stream = await agent.stream({});
2414
- *
2415
- * // With guardrails - use toUIMessageStream for protection
2416
- * await printer.readableStream(stream.toUIMessageStream());
2417
- *
2418
- * // Or use printer.stdout which uses toUIMessageStream internally
2419
- * await printer.stdout(stream);
2420
- * ```
2421
- */
2422
- async stream(contextVariables, config) {
2423
- if (!this.#options.context) {
2424
- throw new Error(`Agent ${this.#options.name} is missing a context.`);
2425
- }
2426
- if (!this.#options.model) {
2427
- throw new Error(`Agent ${this.#options.name} is missing a model.`);
2428
- }
2429
- const result = await this.#createRawStream(contextVariables, config);
2430
- if (this.#guardrails.length === 0) {
2431
- return result;
2432
- }
2433
- return this.#wrapWithGuardrails(result, contextVariables, config);
2434
- }
2435
- /**
2436
- * Create a raw stream without guardrail processing.
2437
- */
2438
- async #createRawStream(contextVariables, config) {
2439
- const { messages, systemPrompt } = await this.#options.context.resolve({
2440
- renderer: new XmlRenderer()
2441
- });
2442
- const runId = generateId2();
2443
- return streamText({
2444
- abortSignal: config?.abortSignal,
2445
- providerOptions: this.#options.providerOptions,
2446
- model: this.#options.model,
2447
- system: systemPrompt,
2448
- messages: await convertToModelMessages(messages),
2449
- experimental_repairToolCall: repairToolCall,
2450
- stopWhen: stepCountIs(50),
2451
- experimental_transform: config?.transform ?? smoothStream(),
2452
- tools: this.#options.tools,
2453
- experimental_context: contextVariables,
2454
- toolChoice: this.#options.toolChoice,
2455
- onStepFinish: (step) => {
2456
- const toolCall = step.toolCalls.at(-1);
2457
- if (toolCall) {
2458
- console.log(
2459
- `Debug: (${runId}) ${chalk2.bold.yellow("ToolCalled")}: ${toolCall.toolName}(${JSON.stringify(toolCall.input)})`
2460
- );
2461
- }
2462
- }
2463
- });
2464
- }
2465
- /**
2466
- * Wrap a StreamTextResult with guardrail protection on toUIMessageStream().
2467
- *
2468
- * When a guardrail fails:
2469
- * 1. Accumulated text + feedback is appended as the assistant's self-correction
2470
- * 2. The feedback is written to the output stream (user sees the correction)
2471
- * 3. A new stream is started and the model continues from the correction
2472
- */
2473
- #wrapWithGuardrails(result, contextVariables, config) {
2474
- const maxRetries = config?.maxRetries ?? this.#options.maxGuardrailRetries ?? 3;
2475
- const context = this.#options.context;
2476
- const originalToUIMessageStream = result.toUIMessageStream.bind(result);
2477
- result.toUIMessageStream = (options) => {
2478
- return createUIMessageStream({
2479
- generateId: generateId2,
2480
- execute: async ({ writer }) => {
2481
- let currentResult = result;
2482
- let attempt = 0;
2483
- const guardrailContext = {
2484
- availableTools: Object.keys(this.tools)
2485
- };
2486
- while (attempt < maxRetries) {
2487
- if (config?.abortSignal?.aborted) {
2488
- writer.write({ type: "finish" });
2489
- return;
2490
- }
2491
- attempt++;
2492
- let accumulatedText = "";
2493
- let guardrailFailed = false;
2494
- let failureFeedback = "";
2495
- const uiStream = currentResult === result ? originalToUIMessageStream(options) : currentResult.toUIMessageStream(options);
2496
- for await (const part of uiStream) {
2497
- const checkResult = runGuardrailChain(
2498
- part,
2499
- this.#guardrails,
2500
- guardrailContext
2501
- );
2502
- if (checkResult.type === "fail") {
2503
- guardrailFailed = true;
2504
- failureFeedback = checkResult.feedback;
2505
- console.log(
2506
- chalk2.yellow(
2507
- `[${this.#options.name}] Guardrail triggered (attempt ${attempt}/${maxRetries}): ${failureFeedback.slice(0, 50)}...`
2508
- )
2509
- );
2510
- break;
2511
- }
2512
- if (checkResult.part.type === "text-delta") {
2513
- accumulatedText += checkResult.part.delta;
2514
- }
2515
- writer.write(checkResult.part);
2516
- }
2517
- if (!guardrailFailed) {
2518
- writer.write({ type: "finish" });
2519
- return;
2520
- }
2521
- if (attempt >= maxRetries) {
2522
- console.error(
2523
- chalk2.red(
2524
- `[${this.#options.name}] Guardrail retry limit (${maxRetries}) exceeded.`
2525
- )
2526
- );
2527
- writer.write({ type: "finish" });
2528
- return;
2529
- }
2530
- writeText(writer, failureFeedback);
2531
- const selfCorrectionText = accumulatedText + " " + failureFeedback;
2532
- context.set(lastAssistantMessage(selfCorrectionText));
2533
- await context.save();
2534
- currentResult = await this.#createRawStream(
2535
- contextVariables,
2536
- config
2537
- );
2538
- }
2539
- },
2540
- onError: (error) => {
2541
- const message2 = error instanceof Error ? error.message : String(error);
2542
- return `Stream failed: ${message2}`;
2543
- }
2544
- });
2545
- };
2546
- return result;
2547
- }
2548
- clone(overrides) {
2549
- return new _Agent({
2550
- ...this.#options,
2551
- ...overrides
2552
- });
2553
- }
2554
- };
2555
- function agent(options) {
2556
- return new Agent(options);
2557
- }
2558
- var repairToolCall = async ({
2559
- toolCall,
2560
- tools,
2561
- inputSchema,
2562
- error
2563
- }) => {
2564
- console.log(
2565
- `Debug: ${chalk2.yellow("RepairingToolCall")}: ${toolCall.toolName}`,
2566
- error.name
2567
- );
2568
- if (NoSuchToolError.isInstance(error)) {
2569
- return null;
2570
- }
2571
- const tool2 = tools[toolCall.toolName];
2572
- const { output } = await generateText({
2573
- model: groq("openai/gpt-oss-20b"),
2574
- output: Output.object({ schema: tool2.inputSchema }),
2575
- prompt: [
2576
- `The model tried to call the tool "${toolCall.toolName}" with the following inputs:`,
2577
- JSON.stringify(toolCall.input),
2578
- `The tool accepts the following schema:`,
2579
- JSON.stringify(inputSchema(toolCall)),
2580
- "Please fix the inputs."
2581
- ].join("\n")
2582
- });
2583
- return { ...toolCall, input: JSON.stringify(output) };
2584
- };
2585
- function writeText(writer, text) {
2586
- const feedbackPartId = generateId2();
2587
- writer.write({
2588
- id: feedbackPartId,
2589
- type: "text-start"
2590
- });
2591
- writer.write({
2592
- id: feedbackPartId,
2593
- type: "text-delta",
2594
- delta: ` ${text}`
2595
- });
2596
- writer.write({
2597
- id: feedbackPartId,
2598
- type: "text-end"
2599
- });
2600
- }
2601
-
2602
- // packages/text2sql/src/lib/adapters/groundings/report.grounding.ts
561
+ ContextEngine,
562
+ InMemoryContextStore,
563
+ agent,
564
+ fragment,
565
+ persona,
566
+ user
567
+ } from "@deepagents/context";
2603
568
  var ReportGrounding = class extends AbstractGrounding {
2604
569
  #adapter;
2605
570
  #model;
@@ -2608,7 +573,7 @@ var ReportGrounding = class extends AbstractGrounding {
2608
573
  constructor(adapter, config = {}) {
2609
574
  super("business_context");
2610
575
  this.#adapter = adapter;
2611
- this.#model = config.model ?? groq2("openai/gpt-oss-20b");
576
+ this.#model = config.model ?? groq("openai/gpt-oss-20b");
2612
577
  this.#cache = config.cache;
2613
578
  this.#forceRefresh = config.forceRefresh ?? false;
2614
579
  }