@t2000/engine 1.6.0 → 1.8.0

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
@@ -215,19 +215,19 @@ ${note}`;
215
215
 
216
216
  // src/tool-flags.ts
217
217
  var TOOL_FLAGS = {
218
- // Write tools — financial
219
- save_deposit: { mutating: true, requiresBalance: true },
220
- withdraw: { mutating: true, affectsHealth: true },
221
- send_transfer: { mutating: true, requiresBalance: true, irreversible: true },
222
- swap_execute: { mutating: true, requiresBalance: true },
223
- borrow: { mutating: true, affectsHealth: true },
224
- repay_debt: { mutating: true, requiresBalance: true },
225
- claim_rewards: { mutating: true },
226
- volo_stake: { mutating: true, requiresBalance: true },
227
- volo_unstake: { mutating: true },
228
- // Write tools — pay / services
218
+ // Write tools — financial (bundleable — SPEC 7 Layer 2)
219
+ save_deposit: { mutating: true, requiresBalance: true, bundleable: true },
220
+ withdraw: { mutating: true, affectsHealth: true, bundleable: true },
221
+ send_transfer: { mutating: true, requiresBalance: true, irreversible: true, bundleable: true },
222
+ swap_execute: { mutating: true, requiresBalance: true, bundleable: true },
223
+ borrow: { mutating: true, affectsHealth: true, bundleable: true },
224
+ repay_debt: { mutating: true, requiresBalance: true, bundleable: true },
225
+ claim_rewards: { mutating: true, bundleable: true },
226
+ volo_stake: { mutating: true, requiresBalance: true, bundleable: true },
227
+ volo_unstake: { mutating: true, bundleable: true },
228
+ // Write tools — pay / services (NOT bundleable — see ToolFlags.bundleable JSDoc)
229
229
  pay_api: { mutating: true, requiresBalance: true, costAware: true, producesArtifact: true, maxRetries: 1 },
230
- // Write tools — lightweight (no financial guards)
230
+ // Write tools — lightweight (no financial guards, NOT bundleable — Postgres only)
231
231
  save_contact: {},
232
232
  // [SIMPLIFICATION DAY 7] Removed flag entries for deleted tools:
233
233
  // create_schedule, cancel_schedule (DCA schedules retired)
@@ -248,6 +248,9 @@ function applyToolFlags(tools) {
248
248
  function getToolFlags(name) {
249
249
  return TOOL_FLAGS[name] ?? {};
250
250
  }
251
+ function isBundleableTool(name) {
252
+ return TOOL_FLAGS[name]?.bundleable === true;
253
+ }
251
254
 
252
255
  // src/navi-config.ts
253
256
  var NAVI_SERVER_NAME = "navi";
@@ -5476,6 +5479,51 @@ var CostTracker = class {
5476
5479
  }
5477
5480
  };
5478
5481
 
5482
+ // src/describe-action.ts
5483
+ function resolveTokenSymbol(nameOrType) {
5484
+ if (!nameOrType.includes("::")) return nameOrType;
5485
+ const parts = nameOrType.split("::");
5486
+ return parts[parts.length - 1];
5487
+ }
5488
+ function describeAction(tool, call) {
5489
+ const input = call.input;
5490
+ switch (tool.name) {
5491
+ case "save_deposit": {
5492
+ return `Save ${input.amount} USDC into lending`;
5493
+ }
5494
+ case "withdraw": {
5495
+ const wAsset = input.asset ?? "";
5496
+ return `Withdraw ${input.amount}${wAsset ? " " + wAsset : ""} from lending`;
5497
+ }
5498
+ case "send_transfer":
5499
+ return `Send $${input.amount} to ${input.to}`;
5500
+ case "borrow":
5501
+ return `Borrow $${input.amount} against collateral`;
5502
+ case "repay_debt":
5503
+ return `Repay $${input.amount} of outstanding debt`;
5504
+ case "claim_rewards":
5505
+ return "Claim all pending protocol rewards";
5506
+ case "pay_api": {
5507
+ const url = String(input.url ?? "");
5508
+ const cost = estimatePayApiCost(url);
5509
+ return `Pay for API call to ${url} (~$${cost})`;
5510
+ }
5511
+ case "swap_execute": {
5512
+ const from = resolveTokenSymbol(String(input.from ?? "?"));
5513
+ const to = resolveTokenSymbol(String(input.to ?? "?"));
5514
+ const amt = input.amount ?? "?";
5515
+ const slippagePct = (input.slippage ?? 0.01) * 100;
5516
+ return `Swap ${amt} ${from} for ${to} (${slippagePct}% max slippage)`;
5517
+ }
5518
+ case "volo_stake":
5519
+ return `Stake ${input.amount} SUI for vSUI`;
5520
+ case "volo_unstake":
5521
+ return `Unstake ${input.amount === "all" ? "all" : input.amount} vSUI`;
5522
+ default:
5523
+ return `Execute ${tool.name}`;
5524
+ }
5525
+ }
5526
+
5479
5527
  // src/thinking-budget.ts
5480
5528
  var EFFORT_THINKING_BUDGET_CAPS = {
5481
5529
  // null = thinking force-disabled (LEAN tier — single-fact reads need
@@ -6610,6 +6658,98 @@ async function executeTool(tool, call, context) {
6610
6658
  return { data: result.data, isError: false };
6611
6659
  }
6612
6660
 
6661
+ // src/tool-ttls.ts
6662
+ var TOOL_TTL_MS = {
6663
+ swap_quote: 3e4,
6664
+ rates_info: 9e4,
6665
+ balance_check: 12e4,
6666
+ portfolio_analysis: 12e4,
6667
+ savings_info: 12e4,
6668
+ health_check: 9e4
6669
+ };
6670
+ var DEFAULT_TOOL_TTL_MS = 6e4;
6671
+ function bundleShortestTtl(toolUseIds, toolNamesById) {
6672
+ if (toolUseIds.length === 0) return DEFAULT_TOOL_TTL_MS;
6673
+ let shortest = Number.POSITIVE_INFINITY;
6674
+ for (const id of toolUseIds) {
6675
+ const name = toolNamesById[id];
6676
+ const ttl = (name !== void 0 ? TOOL_TTL_MS[name] : void 0) ?? DEFAULT_TOOL_TTL_MS;
6677
+ if (ttl < shortest) shortest = ttl;
6678
+ }
6679
+ return Number.isFinite(shortest) ? shortest : DEFAULT_TOOL_TTL_MS;
6680
+ }
6681
+ var REGENERATABLE_READ_TOOLS = /* @__PURE__ */ new Set([
6682
+ "swap_quote",
6683
+ "rates_info",
6684
+ "balance_check",
6685
+ "portfolio_analysis",
6686
+ "savings_info",
6687
+ "health_check"
6688
+ ]);
6689
+
6690
+ // src/compose-bundle.ts
6691
+ function composeBundleFromToolResults(input) {
6692
+ if (input.pendingWrites.length < 2) {
6693
+ throw new Error(
6694
+ "composeBundleFromToolResults requires \u22652 pending writes; use the legacy single-write path for N=1."
6695
+ );
6696
+ }
6697
+ const steps = input.pendingWrites.map((call) => {
6698
+ const tool = findTool(input.tools, call.name);
6699
+ if (!tool) {
6700
+ throw new Error(`Unknown tool '${call.name}' in bundle composition`);
6701
+ }
6702
+ if (tool.flags?.bundleable !== true) {
6703
+ throw new Error(
6704
+ `Tool '${call.name}' is not bundleable. Set ToolFlags.bundleable=true in tool-flags.ts before including it in a bundle. See SPEC 7 \xA7 "Layer 2 \u2014 Bundleable tools (v1)".`
6705
+ );
6706
+ }
6707
+ const description = describeAction(tool, call);
6708
+ const modifiableFields = getModifiableFields(call.name);
6709
+ return {
6710
+ toolName: call.name,
6711
+ toolUseId: call.id,
6712
+ attemptId: randomUUID(),
6713
+ input: call.input,
6714
+ description,
6715
+ ...modifiableFields?.length ? { modifiableFields } : {}
6716
+ };
6717
+ });
6718
+ const regenerateToolUseIds = input.readResults.filter((r) => REGENERATABLE_READ_TOOLS.has(r.toolName)).map((r) => r.toolUseId);
6719
+ const canRegenerate = regenerateToolUseIds.length > 0;
6720
+ let quoteAge;
6721
+ if (regenerateToolUseIds.length > 0) {
6722
+ const stalest = Math.min(
6723
+ ...input.readResults.filter((r) => REGENERATABLE_READ_TOOLS.has(r.toolName)).map((r) => r.timestamp)
6724
+ );
6725
+ quoteAge = Math.max(0, Date.now() - stalest);
6726
+ }
6727
+ const allGuardInjections = [];
6728
+ if (input.guardInjectionsByCallId) {
6729
+ for (const call of input.pendingWrites) {
6730
+ const injections = input.guardInjectionsByCallId[call.id];
6731
+ if (injections?.length) allGuardInjections.push(...injections);
6732
+ }
6733
+ }
6734
+ const firstStep = steps[0];
6735
+ const action = {
6736
+ toolName: firstStep.toolName,
6737
+ toolUseId: firstStep.toolUseId,
6738
+ input: firstStep.input,
6739
+ description: firstStep.description,
6740
+ assistantContent: input.assistantContent,
6741
+ completedResults: input.completedResults,
6742
+ ...allGuardInjections.length ? { guardInjections: allGuardInjections } : {},
6743
+ turnIndex: input.turnIndex,
6744
+ attemptId: firstStep.attemptId,
6745
+ steps,
6746
+ canRegenerate,
6747
+ ...quoteAge !== void 0 ? { quoteAge } : {},
6748
+ ...regenerateToolUseIds.length > 0 ? { regenerateInput: { toolUseIds: regenerateToolUseIds } } : {}
6749
+ };
6750
+ return action;
6751
+ }
6752
+
6613
6753
  // src/engine.ts
6614
6754
  var DEFAULT_MAX_TURNS = 10;
6615
6755
  var DEFAULT_MAX_TOKENS = 4096;
@@ -6760,17 +6900,57 @@ var QueryEngine = class {
6760
6900
  async *resumeWithToolResult(action, response) {
6761
6901
  this.abortController = new AbortController();
6762
6902
  const signal = this.abortController.signal;
6763
- const writeResult = response.approved ? {
6764
- type: "tool_result",
6765
- toolUseId: action.toolUseId,
6766
- content: JSON.stringify(response.executionResult ?? { success: true }),
6767
- isError: false
6768
- } : {
6769
- type: "tool_result",
6770
- toolUseId: action.toolUseId,
6771
- content: JSON.stringify({ error: "User declined this action" }),
6772
- isError: true
6773
- };
6903
+ const isBundle = Array.isArray(action.steps) && action.steps.length > 0;
6904
+ const writeResultBlocks = [];
6905
+ if (isBundle) {
6906
+ const steps = action.steps;
6907
+ const stepResults = response.stepResults ?? [];
6908
+ const resultByToolUseId = new Map(stepResults.map((r) => [r.toolUseId, r]));
6909
+ for (const step of steps) {
6910
+ if (response.approved) {
6911
+ const stepResult = resultByToolUseId.get(step.toolUseId);
6912
+ if (stepResult) {
6913
+ writeResultBlocks.push({
6914
+ type: "tool_result",
6915
+ toolUseId: step.toolUseId,
6916
+ content: JSON.stringify(stepResult.result),
6917
+ isError: stepResult.isError
6918
+ });
6919
+ } else {
6920
+ writeResultBlocks.push({
6921
+ type: "tool_result",
6922
+ toolUseId: step.toolUseId,
6923
+ content: JSON.stringify({
6924
+ error: "Host omitted this step's execution result. Treating as failure \u2014 actual on-chain state is unknown. Re-check wallet via balance_check before re-attempting.",
6925
+ _hostBugMissingStepResult: true
6926
+ }),
6927
+ isError: true
6928
+ });
6929
+ }
6930
+ } else {
6931
+ writeResultBlocks.push({
6932
+ type: "tool_result",
6933
+ toolUseId: step.toolUseId,
6934
+ content: JSON.stringify({ error: "User declined this action" }),
6935
+ isError: true
6936
+ });
6937
+ }
6938
+ }
6939
+ } else {
6940
+ writeResultBlocks.push(
6941
+ response.approved ? {
6942
+ type: "tool_result",
6943
+ toolUseId: action.toolUseId,
6944
+ content: JSON.stringify(response.executionResult ?? { success: true }),
6945
+ isError: false
6946
+ } : {
6947
+ type: "tool_result",
6948
+ toolUseId: action.toolUseId,
6949
+ content: JSON.stringify({ error: "User declined this action" }),
6950
+ isError: true
6951
+ }
6952
+ );
6953
+ }
6774
6954
  if (action.assistantContent?.length) {
6775
6955
  this.messages.push({ role: "assistant", content: action.assistantContent });
6776
6956
  }
@@ -6781,16 +6961,47 @@ var QueryEngine = class {
6781
6961
  content: r.content,
6782
6962
  isError: r.isError
6783
6963
  })),
6784
- writeResult
6964
+ ...writeResultBlocks
6785
6965
  ];
6786
6966
  this.messages.push({ role: "user", content: allResults });
6787
- yield {
6788
- type: "tool_result",
6789
- toolName: action.toolName,
6790
- toolUseId: action.toolUseId,
6791
- result: response.approved ? response.executionResult ?? { success: true } : { error: "User declined this action" },
6792
- isError: !response.approved
6793
- };
6967
+ if (isBundle) {
6968
+ const steps = action.steps;
6969
+ const stepResults = response.stepResults ?? [];
6970
+ const resultByToolUseId = new Map(stepResults.map((r) => [r.toolUseId, r]));
6971
+ for (const step of steps) {
6972
+ const stepResult = resultByToolUseId.get(step.toolUseId);
6973
+ let eventResult;
6974
+ let eventIsError;
6975
+ if (!response.approved) {
6976
+ eventResult = { error: "User declined this action" };
6977
+ eventIsError = true;
6978
+ } else if (stepResult) {
6979
+ eventResult = stepResult.result;
6980
+ eventIsError = stepResult.isError;
6981
+ } else {
6982
+ eventResult = {
6983
+ error: "Host omitted this step's execution result.",
6984
+ _hostBugMissingStepResult: true
6985
+ };
6986
+ eventIsError = true;
6987
+ }
6988
+ yield {
6989
+ type: "tool_result",
6990
+ toolName: step.toolName,
6991
+ toolUseId: step.toolUseId,
6992
+ result: eventResult,
6993
+ isError: eventIsError
6994
+ };
6995
+ }
6996
+ } else {
6997
+ yield {
6998
+ type: "tool_result",
6999
+ toolName: action.toolName,
7000
+ toolUseId: action.toolUseId,
7001
+ result: response.approved ? response.executionResult ?? { success: true } : { error: "User declined this action" },
7002
+ isError: !response.approved
7003
+ };
7004
+ }
6794
7005
  if (!response.approved) {
6795
7006
  yield { type: "turn_complete", stopReason: "end_turn" };
6796
7007
  this.turnReadCache.clear();
@@ -6817,10 +7028,27 @@ var QueryEngine = class {
6817
7028
  * the fresh tool results and narrates from them.
6818
7029
  */
6819
7030
  async *runPostWriteRefresh(action, response, signal) {
6820
- const refreshList = this.postWriteRefresh?.[action.toolName];
6821
- if (!refreshList || refreshList.length === 0) return;
6822
- const exec = response.executionResult;
6823
- const writeFailed = exec != null && typeof exec === "object" && "success" in exec && exec.success === false;
7031
+ const isBundle = Array.isArray(action.steps) && action.steps.length > 0;
7032
+ const refreshSet = /* @__PURE__ */ new Set();
7033
+ if (isBundle) {
7034
+ for (const step of action.steps) {
7035
+ const stepRefresh = this.postWriteRefresh?.[step.toolName];
7036
+ if (stepRefresh) for (const t of stepRefresh) refreshSet.add(t);
7037
+ }
7038
+ } else {
7039
+ const singleRefresh = this.postWriteRefresh?.[action.toolName];
7040
+ if (singleRefresh) for (const t of singleRefresh) refreshSet.add(t);
7041
+ }
7042
+ if (refreshSet.size === 0) return;
7043
+ const refreshList = Array.from(refreshSet);
7044
+ const writeFailed = (() => {
7045
+ if (isBundle) {
7046
+ const stepResults = response.stepResults ?? [];
7047
+ return stepResults.some((r) => r.isError);
7048
+ }
7049
+ const exec = response.executionResult;
7050
+ return exec != null && typeof exec === "object" && "success" in exec && exec.success === false;
7051
+ })();
6824
7052
  if (writeFailed) return;
6825
7053
  const refreshTools = refreshList.map((name) => findTool(this.tools, name)).filter(
6826
7054
  (t) => t !== void 0 && t.isReadOnly && t.isConcurrencySafe
@@ -7067,6 +7295,7 @@ var QueryEngine = class {
7067
7295
  pendingToolCalls: []
7068
7296
  };
7069
7297
  const dispatcher = new EarlyToolDispatcher(this.tools, context, this.turnReadCache);
7298
+ const turnReadToolResults = [];
7070
7299
  try {
7071
7300
  const microcompacted = microcompact(this.messages, this.tools);
7072
7301
  this.messages = microcompacted;
@@ -7217,6 +7446,13 @@ ${recipeCtx}`;
7217
7446
  };
7218
7447
  }
7219
7448
  }
7449
+ if (!finalEvent.isError && tool && tool.isReadOnly) {
7450
+ turnReadToolResults.push({
7451
+ toolUseId: finalEvent.toolUseId,
7452
+ toolName: finalEvent.toolName,
7453
+ timestamp: Date.now()
7454
+ });
7455
+ }
7220
7456
  earlyResultBlocks.push({
7221
7457
  type: "tool_result",
7222
7458
  toolUseId: finalEvent.toolUseId,
@@ -7245,7 +7481,7 @@ ${recipeCtx}`;
7245
7481
  }
7246
7482
  const approved = [];
7247
7483
  const toolResultBlocks = [...earlyResultBlocks];
7248
- let pendingWrite = null;
7484
+ const pendingWrites = [];
7249
7485
  for (const call of acc.pendingToolCalls) {
7250
7486
  const tool = findTool(this.tools, call.name);
7251
7487
  if (tool && tool.isReadOnly) {
@@ -7298,8 +7534,7 @@ ${recipeCtx}`;
7298
7534
  yield { type: "tool_start", toolName: call.name, toolUseId: call.id, input: call.input };
7299
7535
  continue;
7300
7536
  }
7301
- pendingWrite = { call, tool };
7302
- break;
7537
+ pendingWrites.push({ call, tool });
7303
7538
  }
7304
7539
  const guardedApproved = [];
7305
7540
  if (this.guardConfig) {
@@ -7396,6 +7631,11 @@ ${recipeCtx}`;
7396
7631
  result: finalEvent.result,
7397
7632
  sourceToolUseId: finalEvent.toolUseId
7398
7633
  });
7634
+ turnReadToolResults.push({
7635
+ toolUseId: finalEvent.toolUseId,
7636
+ toolName: finalEvent.toolName,
7637
+ timestamp: Date.now()
7638
+ });
7399
7639
  } else {
7400
7640
  this.turnReadCache.clear();
7401
7641
  }
@@ -7447,46 +7687,101 @@ ${recipeCtx}`;
7447
7687
  }
7448
7688
  yield toolEvent;
7449
7689
  }
7450
- if (pendingWrite && this.guardConfig) {
7690
+ const guardPassedWrites = [];
7691
+ const guardInjectionsByCallId = {};
7692
+ let anyGuardBlocked = false;
7693
+ if (this.guardConfig && pendingWrites.length > 0) {
7451
7694
  const convCtx = extractConversationText(this.messages);
7452
- const check = runGuards(
7453
- pendingWrite.tool,
7454
- pendingWrite.call,
7455
- this.guardState,
7456
- this.guardConfig,
7457
- convCtx,
7458
- this.onGuardFired,
7459
- { contacts: this.contacts, walletAddress: this.walletAddress }
7460
- );
7461
- this.guardEvents.push(...check.events);
7462
- if (check.blocked) {
7463
- yield {
7464
- type: "tool_result",
7465
- toolName: pendingWrite.call.name,
7466
- toolUseId: pendingWrite.call.id,
7467
- result: { error: check.blockReason, _gate: check.blockGate },
7468
- isError: true
7469
- };
7470
- toolResultBlocks.push({
7471
- type: "tool_result",
7472
- toolUseId: pendingWrite.call.id,
7473
- content: JSON.stringify({ error: check.blockReason, _gate: check.blockGate }),
7474
- isError: true
7475
- });
7695
+ for (const write of pendingWrites) {
7696
+ const check = runGuards(
7697
+ write.tool,
7698
+ write.call,
7699
+ this.guardState,
7700
+ this.guardConfig,
7701
+ convCtx,
7702
+ this.onGuardFired,
7703
+ { contacts: this.contacts, walletAddress: this.walletAddress }
7704
+ );
7705
+ this.guardEvents.push(...check.events);
7706
+ if (check.blocked) {
7707
+ anyGuardBlocked = true;
7708
+ yield {
7709
+ type: "tool_result",
7710
+ toolName: write.call.name,
7711
+ toolUseId: write.call.id,
7712
+ result: { error: check.blockReason, _gate: check.blockGate },
7713
+ isError: true
7714
+ };
7715
+ toolResultBlocks.push({
7716
+ type: "tool_result",
7717
+ toolUseId: write.call.id,
7718
+ content: JSON.stringify({ error: check.blockReason, _gate: check.blockGate }),
7719
+ isError: true
7720
+ });
7721
+ continue;
7722
+ }
7723
+ if (check.injections.length > 0) {
7724
+ guardInjectionsByCallId[write.call.id] = check.injections;
7725
+ write.call._guardInjections = check.injections;
7726
+ }
7727
+ guardPassedWrites.push(write);
7728
+ }
7729
+ if (anyGuardBlocked) {
7476
7730
  this.messages.push({ role: "assistant", content: acc.assistantBlocks });
7477
7731
  this.messages.push({ role: "user", content: toolResultBlocks });
7478
7732
  continue;
7479
7733
  }
7480
- if (check.injections.length > 0) {
7481
- pendingWrite.call._guardInjections = check.injections;
7482
- }
7734
+ } else {
7735
+ guardPassedWrites.push(...pendingWrites);
7483
7736
  }
7484
- if (pendingWrite) {
7485
- const writeGuardInjections = pendingWrite.call._guardInjections;
7486
- const modifiableFields = getModifiableFields(pendingWrite.call.name);
7737
+ if (guardPassedWrites.length > 0) {
7738
+ const allBundleable = guardPassedWrites.length >= 2 && guardPassedWrites.every((w) => w.tool.flags?.bundleable === true);
7487
7739
  const turnIndex = this.messages.filter((m) => m.role === "assistant").length;
7488
- const attemptId = randomUUID();
7489
7740
  this.turnPaused = true;
7741
+ if (allBundleable) {
7742
+ const completedResults = toolResultBlocks.map((b) => ({
7743
+ toolUseId: b.toolUseId,
7744
+ content: b.content,
7745
+ isError: b.isError ?? false
7746
+ }));
7747
+ const bundleAction = composeBundleFromToolResults({
7748
+ pendingWrites: guardPassedWrites.map((w) => w.call),
7749
+ tools: this.tools,
7750
+ readResults: turnReadToolResults,
7751
+ assistantContent: acc.assistantBlocks,
7752
+ completedResults,
7753
+ guardInjectionsByCallId,
7754
+ turnIndex
7755
+ });
7756
+ yield { type: "pending_action", action: bundleAction };
7757
+ return;
7758
+ }
7759
+ const pendingWrite = guardPassedWrites[0];
7760
+ if (guardPassedWrites.length > 1) {
7761
+ for (let i = 1; i < guardPassedWrites.length; i++) {
7762
+ const dropped = guardPassedWrites[i];
7763
+ const errBody = JSON.stringify({
7764
+ error: "This write was emitted alongside another write that requires a separate confirmation. Re-emit it after the first write resolves.",
7765
+ _droppedDueToMixedBundleability: true
7766
+ });
7767
+ yield {
7768
+ type: "tool_result",
7769
+ toolName: dropped.call.name,
7770
+ toolUseId: dropped.call.id,
7771
+ result: { error: errBody },
7772
+ isError: true
7773
+ };
7774
+ toolResultBlocks.push({
7775
+ type: "tool_result",
7776
+ toolUseId: dropped.call.id,
7777
+ content: errBody,
7778
+ isError: true
7779
+ });
7780
+ }
7781
+ }
7782
+ const writeGuardInjections = guardInjectionsByCallId[pendingWrite.call.id];
7783
+ const modifiableFields = getModifiableFields(pendingWrite.call.name);
7784
+ const attemptId = randomUUID();
7490
7785
  yield {
7491
7786
  type: "pending_action",
7492
7787
  action: {
@@ -7697,49 +7992,6 @@ function validateHistory(messages) {
7697
7992
  }
7698
7993
  return merged;
7699
7994
  }
7700
- function resolveTokenSymbol(nameOrType) {
7701
- if (!nameOrType.includes("::")) return nameOrType;
7702
- const parts = nameOrType.split("::");
7703
- return parts[parts.length - 1];
7704
- }
7705
- function describeAction(tool, call) {
7706
- const input = call.input;
7707
- switch (tool.name) {
7708
- case "save_deposit": {
7709
- return `Save ${input.amount} USDC into lending`;
7710
- }
7711
- case "withdraw": {
7712
- const wAsset = input.asset ?? "";
7713
- return `Withdraw ${input.amount}${wAsset ? " " + wAsset : ""} from lending`;
7714
- }
7715
- case "send_transfer":
7716
- return `Send $${input.amount} to ${input.to}`;
7717
- case "borrow":
7718
- return `Borrow $${input.amount} against collateral`;
7719
- case "repay_debt":
7720
- return `Repay $${input.amount} of outstanding debt`;
7721
- case "claim_rewards":
7722
- return "Claim all pending protocol rewards";
7723
- case "pay_api": {
7724
- const url = String(input.url ?? "");
7725
- const cost = estimatePayApiCost(url);
7726
- return `Pay for API call to ${url} (~$${cost})`;
7727
- }
7728
- case "swap_execute": {
7729
- const from = resolveTokenSymbol(String(input.from ?? "?"));
7730
- const to = resolveTokenSymbol(String(input.to ?? "?"));
7731
- const amt = input.amount ?? "?";
7732
- const slippagePct = (input.slippage ?? 0.01) * 100;
7733
- return `Swap ${amt} ${from} for ${to} (${slippagePct}% max slippage)`;
7734
- }
7735
- case "volo_stake":
7736
- return `Stake ${input.amount} SUI for vSUI`;
7737
- case "volo_unstake":
7738
- return `Unstake ${input.amount === "all" ? "all" : input.amount} vSUI`;
7739
- default:
7740
- return `Execute ${tool.name}`;
7741
- }
7742
- }
7743
7995
  function flagSuspiciousResult(toolName, result) {
7744
7996
  if (!result || typeof result !== "object") return null;
7745
7997
  const r = result;
@@ -7870,7 +8122,8 @@ var StepSchema = z.object({
7870
8122
  flags: z.record(z.unknown()).optional(),
7871
8123
  on_error: OnErrorSchema.optional(),
7872
8124
  input_template: z.record(z.string()).optional(),
7873
- cost_per_unit: z.string().optional()
8125
+ cost_per_unit: z.string().optional(),
8126
+ bundle: z.boolean().optional()
7874
8127
  });
7875
8128
  var RecipeSchema = z.object({
7876
8129
  name: z.string().min(1),
@@ -7885,6 +8138,19 @@ var RecipeSchema = z.object({
7885
8138
  return new Set(names).size === names.length;
7886
8139
  },
7887
8140
  { message: "Step names must be unique within a recipe" }
8141
+ ).refine(
8142
+ (r) => {
8143
+ for (const step of r.steps) {
8144
+ if (step.bundle === true) {
8145
+ if (!step.tool) return false;
8146
+ if (!isBundleableTool(step.tool)) return false;
8147
+ }
8148
+ }
8149
+ return true;
8150
+ },
8151
+ {
8152
+ message: "Steps with bundle: true must reference a bundleable confirm-tier write tool. Allowed: save_deposit, withdraw, borrow, repay_debt, send_transfer, swap_execute, claim_rewards, volo_stake, volo_unstake. Forbidden: pay_api, save_contact, any read tool, any auto-tier write."
8153
+ }
7888
8154
  );
7889
8155
  function loadRecipes(yamlDir) {
7890
8156
  const files = readdirSync(yamlDir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
@@ -8857,6 +9123,6 @@ function sanitizeAnthropicMessages(messages) {
8857
9123
  return merged;
8858
9124
  }
8859
9125
 
8860
- export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_LEASE_SEC, DEFAULT_PERMISSION_CONFIG, DEFAULT_POLL_BUDGET_MS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SYSTEM_PROMPT, EFFORT_THINKING_BUDGET_CAPS, EarlyToolDispatcher, InMemoryDefiCacheStore, InMemoryFetchLock, InMemoryNaviCacheStore, InMemoryWalletCacheStore, InvalidAddressError, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_ADDR_TTL_SEC, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_RATES_TTL_SEC, NAVI_SERVER_NAME, NaviTools, PERMISSION_PRESETS, QueryEngine, READ_TOOLS, RecipeRegistry, RetryTracker, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SuinsNotRegisteredError, SuinsRpcError, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TxMutex, WRITE_TOOLS, _resetNaviCircuitBreaker, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, awaitOrFetch, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, claimRewardsTool, clampThinkingForEffort, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAddressDefiPortfolio, fetchAddressPortfolio, fetchAudricHistory, fetchAudricPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getAudricApiBase, getDefaultTools, getDefiCacheStore, getFetchLock, getMcpManager, getModifiableFields, getNaviCacheStore, getTelemetrySink, getToolFlags, getWalletAddress, getWalletCacheStore, guardArtifactPreview, guardStaleData, harnessShapeForEffort, hasNaviMcp, healthCheckTool, loadRecipes, looksLikeSuiNs, microcompact, mppServicesTool, naviKey, normalizeAddressInput, parseEvalSummary, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resetDefiCacheStore, resetFetchLock, resetNaviCacheStore, resetTelemetrySink, resetWalletCacheStore, resolveAddressToSuinsViaRpc, resolvePermissionTier, resolveSuinsTool, resolveSuinsViaRpc, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, setDefiCacheStore, setFetchLock, setNaviCacheStore, setTelemetrySink, setWalletCacheStore, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, updateTodoTool, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
9126
+ export { AnthropicProvider, BalanceTracker, CANVAS_TEMPLATES, ContextBudget, CostTracker, DEFAULT_GUARD_CONFIG, DEFAULT_LEASE_SEC, DEFAULT_PERMISSION_CONFIG, DEFAULT_POLL_BUDGET_MS, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SYSTEM_PROMPT, DEFAULT_TOOL_TTL_MS, EFFORT_THINKING_BUDGET_CAPS, EarlyToolDispatcher, InMemoryDefiCacheStore, InMemoryFetchLock, InMemoryNaviCacheStore, InMemoryWalletCacheStore, InvalidAddressError, McpClientManager, McpResponseCache, MemorySessionStore, NAVI_ADDR_TTL_SEC, NAVI_MCP_CONFIG, NAVI_MCP_URL, NAVI_RATES_TTL_SEC, NAVI_SERVER_NAME, NaviTools, PERMISSION_PRESETS, QueryEngine, READ_TOOLS, REGENERATABLE_READ_TOOLS, RecipeRegistry, RetryTracker, SUINS_NAME_REGEX, SUI_ADDRESS_REGEX, SUI_ADDRESS_STRICT_REGEX, SuinsNotRegisteredError, SuinsRpcError, TOOL_FLAGS, TOOL_MODIFIABLE_FIELDS, TOOL_TTL_MS, TxMutex, WRITE_TOOLS, _resetNaviCircuitBreaker, activitySummaryTool, adaptAllMcpTools, adaptAllServerTools, adaptMcpTool, applyToolFlags, awaitOrFetch, balanceCheckTool, borrowTool, budgetToolResult, buildCachedSystemPrompt, buildMcpTools, buildProactivenessInstructions, buildProfileContext, buildSelfEvaluationInstruction, buildStateContext, buildTool, bundleShortestTtl, claimRewardsTool, clampThinkingForEffort, classifyEffort, clearPortfolioCache, clearPortfolioCacheFor, clearPriceMapCache, compactMessages, createGuardRunnerState, engineToSSE, estimateTokens, explainTxTool, extractConversationText, extractMcpText, fetchAddressDefiPortfolio, fetchAddressPortfolio, fetchAudricHistory, fetchAudricPortfolio, fetchAvailableRewards, fetchBalance, fetchHealthFactor, fetchPositions, fetchProtocolStats, fetchRates, fetchSavings, fetchTokenPrices, fetchWalletCoins, findTool, getAudricApiBase, getDefaultTools, getDefiCacheStore, getFetchLock, getMcpManager, getModifiableFields, getNaviCacheStore, getTelemetrySink, getToolFlags, getWalletAddress, getWalletCacheStore, guardArtifactPreview, guardStaleData, harnessShapeForEffort, hasNaviMcp, healthCheckTool, isBundleableTool, loadRecipes, looksLikeSuiNs, microcompact, mppServicesTool, naviKey, normalizeAddressInput, parseEvalSummary, parseMcpJson, parseRecipe, parseSSE, payApiTool, portfolioAnalysisTool, protocolDeepDiveTool, ratesInfoTool, registerEngineTools, renderCanvasTool, repayDebtTool, requireAgent, resetDefiCacheStore, resetFetchLock, resetNaviCacheStore, resetTelemetrySink, resetWalletCacheStore, resolveAddressToSuinsViaRpc, resolvePermissionTier, resolveSuinsTool, resolveSuinsViaRpc, resolveUsdValue, runGuards, runTools, saveContactTool, saveDepositTool, savingsInfoTool, sendTransferTool, serializeSSE, setDefiCacheStore, setFetchLock, setNaviCacheStore, setTelemetrySink, setWalletCacheStore, spendingAnalyticsTool, swapExecuteTool, swapQuoteTool, tokenPricesTool, toolNameToOperation, toolsToDefinitions, transactionHistoryTool, transformBalance, transformHealthFactor, transformPositions, transformRates, transformRewards, transformSavings, updateGuardStateAfterToolResult, updateTodoTool, validateHistory, voloStakeTool, voloStatsTool, voloUnstakeTool, webSearchTool, withdrawTool, yieldSummaryTool };
8861
9127
  //# sourceMappingURL=index.js.map
8862
9128
  //# sourceMappingURL=index.js.map