@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.d.ts +148 -28
- package/dist/index.js +373 -116
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
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:
|
|
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
|