@t2000/engine 0.32.0 → 0.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  import { resolveSymbol, getDecimalsForCoinType, assertAllowedAsset, getSwapQuote, SUI_TYPE } from '@t2000/sdk';
3
+ import { readdirSync, readFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import yaml from 'js-yaml';
3
6
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
4
7
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
5
8
  import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
@@ -1262,6 +1265,10 @@ var sendTransferTool = buildTool({
1262
1265
  if (input.to.startsWith("0x") && !/^0x[a-fA-F0-9]{64}$/.test(input.to)) {
1263
1266
  return { valid: false, error: `Invalid Sui address format: "${input.to}". Must be 0x followed by 64 hex characters.` };
1264
1267
  }
1268
+ const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000000000000000000000000000";
1269
+ if (input.to === ZERO_ADDRESS) {
1270
+ return { valid: false, error: "This is the zero address (burn address). Sending funds here will permanently destroy them. If you really intend to burn tokens, please confirm explicitly." };
1271
+ }
1265
1272
  if (input.amount <= 0) {
1266
1273
  return { valid: false, error: "Amount must be positive." };
1267
1274
  }
@@ -3941,6 +3948,190 @@ function extractConversationText(messages) {
3941
3948
  };
3942
3949
  }
3943
3950
 
3951
+ // src/context.ts
3952
+ var CHARS_PER_TOKEN = 4;
3953
+ var DEFAULT_CONTEXT_LIMIT = 2e5;
3954
+ function estimateTokens(messages) {
3955
+ let chars = 0;
3956
+ for (const msg of messages) {
3957
+ for (const block of msg.content) {
3958
+ chars += blockCharCount(block);
3959
+ }
3960
+ }
3961
+ return Math.ceil(chars / CHARS_PER_TOKEN);
3962
+ }
3963
+ function blockCharCount(block) {
3964
+ switch (block.type) {
3965
+ case "text":
3966
+ return block.text.length;
3967
+ case "thinking":
3968
+ return block.thinking.length;
3969
+ case "redacted_thinking":
3970
+ return block.data.length;
3971
+ case "tool_use":
3972
+ return block.name.length + JSON.stringify(block.input).length;
3973
+ case "tool_result":
3974
+ return block.content.length;
3975
+ }
3976
+ }
3977
+ var ContextBudget = class {
3978
+ estimatedTokens = 0;
3979
+ contextLimit;
3980
+ compactThreshold;
3981
+ warnThreshold;
3982
+ constructor(config = {}) {
3983
+ this.contextLimit = config.contextLimit ?? DEFAULT_CONTEXT_LIMIT;
3984
+ this.compactThreshold = config.compactThreshold ?? 0.85;
3985
+ this.warnThreshold = config.warnThreshold ?? 0.7;
3986
+ }
3987
+ /** Update with actual input_tokens from the API usage event. */
3988
+ update(inputTokens) {
3989
+ this.estimatedTokens = inputTokens;
3990
+ }
3991
+ /** True when the session should be compacted (at 85% of context limit). */
3992
+ shouldCompact() {
3993
+ return this.estimatedTokens >= this.contextLimit * this.compactThreshold;
3994
+ }
3995
+ /** True when nearing the limit (at 70% of context limit). */
3996
+ shouldWarn() {
3997
+ return this.estimatedTokens >= this.contextLimit * this.warnThreshold;
3998
+ }
3999
+ /** Current token count. */
4000
+ get tokens() {
4001
+ return this.estimatedTokens;
4002
+ }
4003
+ /** Remaining tokens before compaction triggers. */
4004
+ get remaining() {
4005
+ return Math.max(0, Math.floor(this.contextLimit * this.compactThreshold) - this.estimatedTokens);
4006
+ }
4007
+ /** Usage ratio (0..1). */
4008
+ get usage() {
4009
+ return this.estimatedTokens / this.contextLimit;
4010
+ }
4011
+ reset() {
4012
+ this.estimatedTokens = 0;
4013
+ }
4014
+ };
4015
+ async function compactMessages(messages, opts = {}) {
4016
+ const maxTokens = opts.maxTokens ?? 1e5;
4017
+ const keepRecent = opts.keepRecentCount ?? 8;
4018
+ const systemTokens = opts.systemPromptTokens ?? 500;
4019
+ const budget = maxTokens - systemTokens;
4020
+ if (messages.length === 0) return [];
4021
+ const mutable = messages.map((m) => ({
4022
+ role: m.role,
4023
+ content: m.content.map((b) => ({ ...b }))
4024
+ }));
4025
+ if (estimateTokens(mutable) <= budget) return mutable;
4026
+ const splitIdx = Math.max(0, mutable.length - keepRecent);
4027
+ const oldMessages = mutable.slice(0, splitIdx);
4028
+ const recent = mutable.slice(splitIdx);
4029
+ if (opts.summarizer && oldMessages.length > 0) {
4030
+ const strippedOld = stripThinkingBlocks(oldMessages);
4031
+ try {
4032
+ const summary = await opts.summarizer(strippedOld);
4033
+ const summaryMessages = [
4034
+ { role: "user", content: [{ type: "text", text: `[Session summary: ${summary}]` }] },
4035
+ { role: "assistant", content: [{ type: "text", text: "Understood. I have the context from our earlier conversation." }] }
4036
+ ];
4037
+ const withSummary = [...summaryMessages, ...recent];
4038
+ if (estimateTokens(withSummary) <= budget) return sanitizeMessages(withSummary);
4039
+ } catch {
4040
+ }
4041
+ }
4042
+ for (let i = 0; i < splitIdx; i++) {
4043
+ mutable[i].content = mutable[i].content.map((block) => {
4044
+ if (block.type === "tool_result" && block.content.length > 200) {
4045
+ return {
4046
+ ...block,
4047
+ content: truncateToolResult(block.content)
4048
+ };
4049
+ }
4050
+ return block;
4051
+ });
4052
+ }
4053
+ for (let i = 0; i < splitIdx; i++) {
4054
+ mutable[i].content = mutable[i].content.filter(
4055
+ (b) => b.type !== "thinking" && b.type !== "redacted_thinking"
4056
+ );
4057
+ }
4058
+ if (estimateTokens(mutable) <= budget) return mutable;
4059
+ if (splitIdx <= 1) {
4060
+ return sanitizeMessages(mutable);
4061
+ }
4062
+ const first = mutable[0];
4063
+ const recentFromMutable = mutable.slice(splitIdx);
4064
+ const oldSection = mutable.slice(1, splitIdx);
4065
+ while (oldSection.length > 0 && estimateTokens([first, ...oldSection, ...recentFromMutable]) > budget) {
4066
+ oldSection.shift();
4067
+ }
4068
+ const compacted = [first, ...oldSection, ...recentFromMutable];
4069
+ if (estimateTokens(compacted) > budget) {
4070
+ for (const msg of compacted) {
4071
+ msg.content = msg.content.map((block) => {
4072
+ if (block.type === "tool_result" && block.content.length > 100) {
4073
+ return { ...block, content: truncateToolResult(block.content) };
4074
+ }
4075
+ return block;
4076
+ });
4077
+ }
4078
+ }
4079
+ return sanitizeMessages(compacted);
4080
+ }
4081
+ function stripThinkingBlocks(messages) {
4082
+ return messages.map((m) => ({
4083
+ ...m,
4084
+ content: m.content.filter(
4085
+ (b) => b.type !== "thinking" && b.type !== "redacted_thinking"
4086
+ )
4087
+ })).filter((m) => m.content.length > 0);
4088
+ }
4089
+ function sanitizeMessages(messages) {
4090
+ const toolUseIds = /* @__PURE__ */ new Set();
4091
+ const toolResultIds = /* @__PURE__ */ new Set();
4092
+ for (const msg of messages) {
4093
+ for (const block of msg.content) {
4094
+ if (block.type === "tool_use") toolUseIds.add(block.id);
4095
+ if (block.type === "tool_result") toolResultIds.add(block.toolUseId);
4096
+ }
4097
+ }
4098
+ return messages.map((msg) => {
4099
+ const filtered = msg.content.filter((block) => {
4100
+ if (block.type === "tool_result") return toolUseIds.has(block.toolUseId);
4101
+ if (block.type === "tool_use") return toolResultIds.has(block.id);
4102
+ return true;
4103
+ });
4104
+ if (filtered.length === 0) return null;
4105
+ return { ...msg, content: filtered };
4106
+ }).filter((m) => m !== null);
4107
+ }
4108
+ function truncateToolResult(content) {
4109
+ try {
4110
+ const parsed = JSON.parse(content);
4111
+ if (parsed.error) {
4112
+ return JSON.stringify({ error: parsed.error });
4113
+ }
4114
+ if (typeof parsed === "object" && parsed !== null) {
4115
+ const summary = {};
4116
+ for (const [key, value] of Object.entries(parsed)) {
4117
+ if (typeof value === "number" || typeof value === "boolean") {
4118
+ summary[key] = value;
4119
+ } else if (typeof value === "string") {
4120
+ summary[key] = value.length > 50 ? value.slice(0, 50) + "\u2026" : value;
4121
+ } else if (Array.isArray(value)) {
4122
+ summary[key] = `[${value.length} items]`;
4123
+ } else {
4124
+ summary[key] = "{\u2026}";
4125
+ }
4126
+ }
4127
+ return JSON.stringify(summary);
4128
+ }
4129
+ return content.slice(0, 100);
4130
+ } catch {
4131
+ return content.slice(0, 100);
4132
+ }
4133
+ }
4134
+
3944
4135
  // src/engine.ts
3945
4136
  var DEFAULT_MAX_TURNS = 10;
3946
4137
  var DEFAULT_MAX_TOKENS = 4096;
@@ -3966,6 +4157,10 @@ var QueryEngine = class {
3966
4157
  costTracker;
3967
4158
  guardConfig;
3968
4159
  guardState;
4160
+ recipes;
4161
+ contextBudget;
4162
+ contextSummarizer;
4163
+ matchedRecipe = null;
3969
4164
  messages = [];
3970
4165
  abortController = null;
3971
4166
  guardEvents = [];
@@ -3989,6 +4184,9 @@ var QueryEngine = class {
3989
4184
  this.costTracker = new CostTracker(config.costTracker);
3990
4185
  this.guardConfig = config.guards;
3991
4186
  this.guardState = createGuardRunnerState();
4187
+ this.recipes = config.recipes;
4188
+ this.contextBudget = new ContextBudget(config.contextBudget);
4189
+ this.contextSummarizer = config.contextSummarizer;
3992
4190
  this.tools = config.tools ?? (config.agent ? getDefaultTools() : []);
3993
4191
  }
3994
4192
  /**
@@ -4006,6 +4204,7 @@ var QueryEngine = class {
4006
4204
  }
4007
4205
  this.abortController = new AbortController();
4008
4206
  const signal = this.abortController.signal;
4207
+ this.matchedRecipe = this.recipes?.match(prompt) ?? null;
4009
4208
  this.messages.push({
4010
4209
  role: "user",
4011
4210
  content: [{ type: "text", text: prompt }]
@@ -4064,10 +4263,18 @@ var QueryEngine = class {
4064
4263
  getMessages() {
4065
4264
  return this.messages;
4066
4265
  }
4266
+ getMatchedRecipe() {
4267
+ return this.matchedRecipe;
4268
+ }
4269
+ getContextBudget() {
4270
+ return this.contextBudget;
4271
+ }
4067
4272
  reset() {
4068
4273
  this.messages = [];
4069
4274
  this.costTracker.reset();
4275
+ this.contextBudget.reset();
4070
4276
  this.guardEvents = [];
4277
+ this.matchedRecipe = null;
4071
4278
  }
4072
4279
  getGuardEvents() {
4073
4280
  return this.guardEvents;
@@ -4117,6 +4324,13 @@ var QueryEngine = class {
4117
4324
  pendingToolCalls: []
4118
4325
  };
4119
4326
  try {
4327
+ if (this.contextBudget.shouldCompact()) {
4328
+ this.messages = await compactMessages(this.messages, {
4329
+ maxTokens: 1e5,
4330
+ keepRecentCount: 8,
4331
+ summarizer: this.contextSummarizer
4332
+ });
4333
+ }
4120
4334
  this.messages = validateHistory(this.messages);
4121
4335
  if (process.env.NODE_ENV !== "test") {
4122
4336
  const summary = this.messages.map((m, idx) => {
@@ -4134,9 +4348,23 @@ ${summary.join("\n")}`);
4134
4348
  }
4135
4349
  const thinkingEnabled = this.thinking && this.thinking.type !== "disabled";
4136
4350
  const effectiveToolChoice = thinkingEnabled ? applyToolChoice && turns === 1 ? "auto" : void 0 : applyToolChoice && turns === 1 ? this.toolChoice : void 0;
4351
+ let effectivePrompt = this.systemPrompt;
4352
+ if (this.matchedRecipe && this.recipes) {
4353
+ const recipeCtx = this.recipes.toPromptContext(this.matchedRecipe);
4354
+ if (typeof effectivePrompt === "string") {
4355
+ effectivePrompt = `${effectivePrompt}
4356
+
4357
+ ${recipeCtx}`;
4358
+ } else if (Array.isArray(effectivePrompt)) {
4359
+ effectivePrompt = [
4360
+ ...effectivePrompt,
4361
+ { type: "text", text: recipeCtx }
4362
+ ];
4363
+ }
4364
+ }
4137
4365
  const stream = this.provider.chat({
4138
4366
  messages: this.messages,
4139
- systemPrompt: this.systemPrompt,
4367
+ systemPrompt: effectivePrompt,
4140
4368
  tools: toolDefs,
4141
4369
  model: this.model,
4142
4370
  maxTokens: this.maxTokens,
@@ -4418,6 +4646,7 @@ ${summary.join("\n")}`);
4418
4646
  event.cacheReadTokens,
4419
4647
  event.cacheWriteTokens
4420
4648
  );
4649
+ this.contextBudget.update(event.inputTokens);
4421
4650
  yield {
4422
4651
  type: "usage",
4423
4652
  inputTokens: event.inputTokens,
@@ -4634,6 +4863,148 @@ var MemorySessionStore = class {
4634
4863
  }
4635
4864
  }
4636
4865
  };
4866
+ var StepRequirementSchema = z.object({
4867
+ step: z.string().optional(),
4868
+ field: z.string().optional(),
4869
+ confirmation: z.boolean().optional()
4870
+ });
4871
+ var OnErrorSchema = z.object({
4872
+ action: z.enum(["abort", "refuse", "report", "retry"]),
4873
+ message: z.string(),
4874
+ suggest: z.string().optional()
4875
+ });
4876
+ var StepSchema = z.object({
4877
+ name: z.string().min(1),
4878
+ tool: z.string().optional(),
4879
+ service: z.string().optional(),
4880
+ purpose: z.string().min(1),
4881
+ cost: z.string().optional(),
4882
+ output: z.object({ type: z.string(), key: z.string() }).optional(),
4883
+ gate: z.enum(["none", "preview", "review", "estimate"]).optional(),
4884
+ gate_prompt: z.string().optional(),
4885
+ requires: z.array(StepRequirementSchema).optional(),
4886
+ rules: z.array(z.string()).optional(),
4887
+ condition: z.string().optional(),
4888
+ notes: z.string().optional(),
4889
+ flags: z.record(z.unknown()).optional(),
4890
+ on_error: OnErrorSchema.optional(),
4891
+ input_template: z.record(z.string()).optional(),
4892
+ cost_per_unit: z.string().optional()
4893
+ });
4894
+ var RecipeSchema = z.object({
4895
+ name: z.string().min(1),
4896
+ description: z.string().min(1),
4897
+ triggers: z.array(z.string().min(1)).min(1),
4898
+ services: z.array(z.string()).optional(),
4899
+ prerequisites: z.array(z.object({ field: z.string(), prompt: z.string() })).optional(),
4900
+ steps: z.array(StepSchema).min(1)
4901
+ }).refine(
4902
+ (r) => {
4903
+ const names = r.steps.map((s) => s.name);
4904
+ return new Set(names).size === names.length;
4905
+ },
4906
+ { message: "Step names must be unique within a recipe" }
4907
+ );
4908
+ function loadRecipes(yamlDir) {
4909
+ const files = readdirSync(yamlDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
4910
+ const recipes = [];
4911
+ for (const file of files) {
4912
+ const content = readFileSync(join(yamlDir, file), "utf-8");
4913
+ const raw = yaml.load(content);
4914
+ const parsed = RecipeSchema.parse(raw);
4915
+ recipes.push(parsed);
4916
+ }
4917
+ return recipes;
4918
+ }
4919
+ function parseRecipe(yamlContent) {
4920
+ const raw = yaml.load(yamlContent);
4921
+ return RecipeSchema.parse(raw);
4922
+ }
4923
+
4924
+ // src/recipes/registry.ts
4925
+ var RecipeRegistry = class {
4926
+ recipes = [];
4927
+ /** Load all recipes from a directory of YAML files. */
4928
+ loadDir(yamlDir) {
4929
+ this.recipes.push(...loadRecipes(yamlDir));
4930
+ }
4931
+ /** Register a single recipe from a YAML string. */
4932
+ loadYaml(yamlContent) {
4933
+ this.recipes.push(parseRecipe(yamlContent));
4934
+ }
4935
+ /** Register a pre-parsed Recipe object. */
4936
+ register(recipe) {
4937
+ this.recipes.push(recipe);
4938
+ }
4939
+ /** All loaded recipes. */
4940
+ all() {
4941
+ return this.recipes;
4942
+ }
4943
+ /**
4944
+ * Match a user message to the most specific recipe.
4945
+ * Longest trigger phrase match wins. Returns null if no match.
4946
+ */
4947
+ match(userMessage) {
4948
+ const normalized = userMessage.toLowerCase().trim();
4949
+ let best = null;
4950
+ let bestLength = 0;
4951
+ for (const recipe of this.recipes) {
4952
+ for (const trigger of recipe.triggers) {
4953
+ const triggerLower = trigger.toLowerCase();
4954
+ if (normalized.includes(triggerLower) && triggerLower.length > bestLength) {
4955
+ best = recipe;
4956
+ bestLength = triggerLower.length;
4957
+ }
4958
+ }
4959
+ }
4960
+ return best;
4961
+ }
4962
+ /**
4963
+ * Format a matched recipe as a compact context block for the system prompt.
4964
+ * Injected dynamically — only when the recipe matches.
4965
+ */
4966
+ toPromptContext(recipe) {
4967
+ const lines = [
4968
+ `## Active Recipe: ${recipe.name}`,
4969
+ recipe.description,
4970
+ "Follow these steps:"
4971
+ ];
4972
+ for (let i = 0; i < recipe.steps.length; i++) {
4973
+ const step = recipe.steps[i];
4974
+ const num = i + 1;
4975
+ const toolNote = step.tool ? ` \u2192 ${step.tool}` : "";
4976
+ const serviceNote = step.service ? ` (${step.service})` : "";
4977
+ const costNote = step.cost ? ` \u2014 ${step.cost}` : "";
4978
+ const gateNote = step.gate && step.gate !== "none" ? ` [GATE: ${step.gate}]` : "";
4979
+ let line = `${num}. ${step.name}${toolNote}${serviceNote}${costNote}${gateNote}`;
4980
+ if (step.gate_prompt) {
4981
+ line += ` \u2014 "${step.gate_prompt}"`;
4982
+ }
4983
+ lines.push(line);
4984
+ if (step.rules?.length) {
4985
+ for (const rule of step.rules) {
4986
+ lines.push(` - ${rule}`);
4987
+ }
4988
+ }
4989
+ if (step.notes) {
4990
+ lines.push(` Note: ${step.notes}`);
4991
+ }
4992
+ if (step.on_error) {
4993
+ lines.push(` On error: ${step.on_error.action} \u2014 ${step.on_error.message}`);
4994
+ }
4995
+ if (step.condition) {
4996
+ lines.push(` Condition: ${step.condition}`);
4997
+ }
4998
+ }
4999
+ if (recipe.prerequisites?.length) {
5000
+ lines.push("Prerequisites (ask before starting):");
5001
+ for (const pre of recipe.prerequisites) {
5002
+ lines.push(`- ${pre.field}: "${pre.prompt}"`);
5003
+ }
5004
+ }
5005
+ return lines.join("\n");
5006
+ }
5007
+ };
4637
5008
 
4638
5009
  // src/classify-effort.ts
4639
5010
  function classifyEffort(model, userMessage, matchedRecipe, sessionWriteCount) {
@@ -4794,120 +5165,6 @@ function buildStateContext(state) {
4794
5165
  }
4795
5166
  }
4796
5167
 
4797
- // src/context.ts
4798
- var CHARS_PER_TOKEN = 4;
4799
- function estimateTokens(messages) {
4800
- let chars = 0;
4801
- for (const msg of messages) {
4802
- for (const block of msg.content) {
4803
- chars += blockCharCount(block);
4804
- }
4805
- }
4806
- return Math.ceil(chars / CHARS_PER_TOKEN);
4807
- }
4808
- function blockCharCount(block) {
4809
- switch (block.type) {
4810
- case "text":
4811
- return block.text.length;
4812
- case "thinking":
4813
- return block.thinking.length;
4814
- case "redacted_thinking":
4815
- return block.data.length;
4816
- case "tool_use":
4817
- return block.name.length + JSON.stringify(block.input).length;
4818
- case "tool_result":
4819
- return block.content.length;
4820
- }
4821
- }
4822
- function compactMessages(messages, opts = {}) {
4823
- const maxTokens = opts.maxTokens ?? 1e5;
4824
- const keepRecent = opts.keepRecentCount ?? 6;
4825
- const systemTokens = opts.systemPromptTokens ?? 500;
4826
- const budget = maxTokens - systemTokens;
4827
- if (messages.length === 0) return [];
4828
- const mutable = messages.map((m) => ({
4829
- role: m.role,
4830
- content: m.content.map((b) => ({ ...b }))
4831
- }));
4832
- if (estimateTokens(mutable) <= budget) return mutable;
4833
- const splitIdx = Math.max(0, mutable.length - keepRecent);
4834
- for (let i = 0; i < splitIdx; i++) {
4835
- mutable[i].content = mutable[i].content.map((block) => {
4836
- if (block.type === "tool_result" && block.content.length > 200) {
4837
- return {
4838
- ...block,
4839
- content: truncateToolResult(block.content)
4840
- };
4841
- }
4842
- return block;
4843
- });
4844
- }
4845
- if (estimateTokens(mutable) <= budget) return mutable;
4846
- const first = mutable[0];
4847
- const recent = mutable.slice(splitIdx);
4848
- const oldSection = mutable.slice(1, splitIdx);
4849
- while (oldSection.length > 0 && estimateTokens([first, ...oldSection, ...recent]) > budget) {
4850
- oldSection.shift();
4851
- }
4852
- const compacted = [first, ...oldSection, ...recent];
4853
- if (estimateTokens(compacted) > budget) {
4854
- for (const msg of compacted) {
4855
- msg.content = msg.content.map((block) => {
4856
- if (block.type === "tool_result" && block.content.length > 100) {
4857
- return { ...block, content: truncateToolResult(block.content) };
4858
- }
4859
- return block;
4860
- });
4861
- }
4862
- }
4863
- return sanitizeMessages(compacted);
4864
- }
4865
- function sanitizeMessages(messages) {
4866
- const toolUseIds = /* @__PURE__ */ new Set();
4867
- const toolResultIds = /* @__PURE__ */ new Set();
4868
- for (const msg of messages) {
4869
- for (const block of msg.content) {
4870
- if (block.type === "tool_use") toolUseIds.add(block.id);
4871
- if (block.type === "tool_result") toolResultIds.add(block.toolUseId);
4872
- }
4873
- }
4874
- return messages.map((msg) => {
4875
- const filtered = msg.content.filter((block) => {
4876
- if (block.type === "tool_result") return toolUseIds.has(block.toolUseId);
4877
- if (block.type === "tool_use") return toolResultIds.has(block.id);
4878
- return true;
4879
- });
4880
- if (filtered.length === 0) return null;
4881
- return { ...msg, content: filtered };
4882
- }).filter((m) => m !== null);
4883
- }
4884
- function truncateToolResult(content) {
4885
- try {
4886
- const parsed = JSON.parse(content);
4887
- if (parsed.error) {
4888
- return JSON.stringify({ error: parsed.error });
4889
- }
4890
- if (typeof parsed === "object" && parsed !== null) {
4891
- const summary = {};
4892
- for (const [key, value] of Object.entries(parsed)) {
4893
- if (typeof value === "number" || typeof value === "boolean") {
4894
- summary[key] = value;
4895
- } else if (typeof value === "string") {
4896
- summary[key] = value.length > 50 ? value.slice(0, 50) + "\u2026" : value;
4897
- } else if (Array.isArray(value)) {
4898
- summary[key] = `[${value.length} items]`;
4899
- } else {
4900
- summary[key] = "{\u2026}";
4901
- }
4902
- }
4903
- return JSON.stringify(summary);
4904
- }
4905
- return content.slice(0, 100);
4906
- } catch {
4907
- return content.slice(0, 100);
4908
- }
4909
- }
4910
-
4911
5168
  // src/mcp.ts
4912
5169
  function buildMcpTools(context, tools) {
4913
5170
  const engineTools = tools ?? getDefaultTools();
@@ -5475,6 +5732,6 @@ function sanitizeAnthropicMessages(messages) {
5475
5732
  return merged;
5476
5733
  }
5477
5734
 
5478
- export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_SYSTEM_PROMPT, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_SERVER_NAME, NaviTools, QueryEngine, READ_TOOLS, RetryTracker, TOOL_FLAGS, TxMutex, WRITE_TOOLS, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, balanceCheckTool, borrowTool, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort, clearPriceCache, compactMessages, createGuardRunnerState, defillamaChainTvlTool, defillamaPriceChangeTool, defillamaProtocolFeesTool, defillamaProtocolInfoTool, defillamaSuiProtocolsTool, defillamaTokenPricesTool, defillamaYieldPoolsTool, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getDefaultTools, getMcpManager, getToolFlags, getWalletAddress, guardArtifactPreview, guardStaleData, hasNaviMcp, healthCheckTool, mppServicesTool, parseMcpJson, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
5735
+ export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_SYSTEM_PROMPT, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_SERVER_NAME, NaviTools, QueryEngine, READ_TOOLS, RecipeRegistry, RetryTracker, TOOL_FLAGS, TxMutex, WRITE_TOOLS, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, balanceCheckTool, borrowTool, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, classifyEffort, clearPriceCache, compactMessages, createGuardRunnerState, defillamaChainTvlTool, defillamaPriceChangeTool, defillamaProtocolFeesTool, defillamaProtocolInfoTool, defillamaSuiProtocolsTool, defillamaTokenPricesTool, defillamaYieldPoolsTool, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getDefaultTools, getMcpManager, getToolFlags, getWalletAddress, guardArtifactPreview, guardStaleData, hasNaviMcp, healthCheckTool, loadRecipes, mppServicesTool, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
5479
5736
  //# sourceMappingURL=index.js.map
5480
5737
  //# sourceMappingURL=index.js.map