@t2000/engine 1.14.0 → 1.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.
package/dist/index.d.ts CHANGED
@@ -1715,7 +1715,7 @@ declare function bundleShortestTtl(toolUseIds: string[], toolNamesById: Record<s
1715
1715
  declare const REGENERATABLE_READ_TOOLS: ReadonlySet<string>;
1716
1716
 
1717
1717
  /**
1718
- * [Phase 0 → Phase 2 / SPEC 13] Maximum number of writes per atomic bundle.
1718
+ * [Phase 0 → Phase 3a / SPEC 13] Maximum number of writes per atomic bundle.
1719
1719
  *
1720
1720
  * **History.**
1721
1721
  * - Pre-Phase-0: 5 (F14-fix-2, 2026-05-03 morning).
@@ -1730,31 +1730,35 @@ declare const REGENERATABLE_READ_TOOLS: ReadonlySet<string>;
1730
1730
  * `composeTx` orchestration loop) but didn't widen the cap. The
1731
1731
  * primitive is what makes Phase 2's raise to 3 possible — without it,
1732
1732
  * every additional step is another wallet-fetch race.
1733
- * - Phase 2 (1.14.0, this version): cap raised to 3. Composition rule
1734
- * is strict-adjacency: every (step[i], step[i+1]) pair must be in
1735
- * `VALID_PAIRS`. No new pairs added Phase 2 is purely the cap raise
1736
- * + adjacency-loop validation. The chain-mode population loop already
1737
- * runs over every `(i, i+1)` since 1.13.0, so 3-op atomic bundles
1738
- * like `withdraw swap send` thread two coin handles end-to-end
1739
- * in one PTB.
1740
- *
1741
- * **Why strict adjacency?** Every consecutive pair must be whitelisted
1742
- * even if the consumer doesn't chain (no `inputCoinFromStep`). This
1743
- * keeps the validator simple and matches the spec's Phase 2 model. The
1744
- * looser DAG-aware variant (where non-chained adjacent steps can be
1745
- * any tool combo) is a Phase 3 follow-up — defer until we see real
1746
- * production flows that need it.
1747
- *
1748
- * **Phase 3+:** `swap_execute swap_execute` (Demo 1 unlock) + DAG-aware
1749
- * validator + cap raise to 4. SPEC 13 §"Phase 3" / §"Phase 5" tracks
1750
- * these. Don't pre-emptively raise this constant past 3 without those
1751
- * landing.
1733
+ * - Phase 2 (1.14.0): cap raised to 3. Composition rule was strict-
1734
+ * adjacency: every (step[i], step[i+1]) pair must be in `VALID_PAIRS`.
1735
+ * Strict adjacency rejected DAG shapes (`swap save send`
1736
+ * last pair `save→send` not in whitelist even though step 3 doesn't
1737
+ * chain from step 2; it pulls a fresh wallet coin).
1738
+ * - **Phase 3a (1.15.0, this version): cap raised to 4. Composition rule
1739
+ * relaxed to DAG-aware: only pairs that actually chain via
1740
+ * `inputCoinFromStep` need whitelist checking. Standalone steps
1741
+ * interleaved between chained steps run wallet-mode independently
1742
+ * inside the same atomic PTB.** This unlocks Demo 1 ("swap 10% to
1743
+ * SUI, save 50% as USDsui, send $100 to Mom" 4-op DAG with one
1744
+ * chain at step 1→2, three standalone wallet-mode steps).
1745
+ *
1746
+ * **DAG-aware semantics.** Pre-3a: every adjacent pair gates the entire
1747
+ * bundle. Phase 3a+: each (i, i+1) pair contributes IF a chain is wired
1748
+ * (via `shouldChainCoin`). Non-chained pairs are independent they
1749
+ * each pre-fetch their own coin from the wallet inside the PTB. Atomic
1750
+ * settlement at the PTB level holds either way.
1751
+ *
1752
+ * **Phase 3b (deferred):** `swap_execute → swap_execute` whitelist add
1753
+ * for explicit multi-hop swap chains (rare; flag-gated when shipped).
1754
+ * **Phase 5+:** cap > 4 once production telemetry confirms zero edge-
1755
+ * case revert rate at cap=4.
1752
1756
  *
1753
1757
  * Hosts importing this constant for system-prompt construction get the
1754
1758
  * current cap automatically. Bumping the cap is a one-line change here
1755
1759
  * that propagates to prompts via the import.
1756
1760
  */
1757
- declare const MAX_BUNDLE_OPS = 3;
1761
+ declare const MAX_BUNDLE_OPS = 4;
1758
1762
  /**
1759
1763
  * [Phase 0 / SPEC 13] Whitelisted (producer, consumer) pairs for atomic
1760
1764
  * bundling. Every key has the shape `${producer}->${consumer}`. Bundles
package/dist/index.js CHANGED
@@ -3647,26 +3647,35 @@ var swapExecuteTool = buildTool({
3647
3647
  },
3648
3648
  async call(input, context) {
3649
3649
  const agent = requireAgent(context);
3650
- const result = await agent.swap({
3651
- from: input.from,
3652
- to: input.to,
3653
- amount: input.amount,
3654
- byAmountIn: input.byAmountIn,
3655
- slippage: input.slippage
3656
- });
3657
- return {
3658
- data: {
3659
- tx: result.tx,
3660
- fromToken: result.fromToken,
3661
- toToken: result.toToken,
3662
- fromAmount: result.fromAmount,
3663
- toAmount: result.toAmount,
3664
- priceImpact: result.priceImpact,
3665
- route: result.route,
3666
- gasCost: result.gasCost
3667
- },
3668
- displayText: `Swapped ${result.fromAmount} ${result.fromToken} for ${result.toAmount.toFixed(4)} ${result.toToken} (tx: ${result.tx.slice(0, 8)}...)`
3669
- };
3650
+ const sink = getTelemetrySink();
3651
+ const start = Date.now();
3652
+ try {
3653
+ const result = await agent.swap({
3654
+ from: input.from,
3655
+ to: input.to,
3656
+ amount: input.amount,
3657
+ byAmountIn: input.byAmountIn,
3658
+ slippage: input.slippage
3659
+ });
3660
+ sink.histogram("cetus.swap_execute_total_ms", Date.now() - start);
3661
+ sink.counter("cetus.swap_execute_count", { outcome: "success" });
3662
+ return {
3663
+ data: {
3664
+ tx: result.tx,
3665
+ fromToken: result.fromToken,
3666
+ toToken: result.toToken,
3667
+ fromAmount: result.fromAmount,
3668
+ toAmount: result.toAmount,
3669
+ priceImpact: result.priceImpact,
3670
+ route: result.route,
3671
+ gasCost: result.gasCost
3672
+ },
3673
+ displayText: `Swapped ${result.fromAmount} ${result.fromToken} for ${result.toAmount.toFixed(4)} ${result.toToken} (tx: ${result.tx.slice(0, 8)}...)`
3674
+ };
3675
+ } catch (err) {
3676
+ sink.counter("cetus.swap_execute_count", { outcome: "error" });
3677
+ throw err;
3678
+ }
3670
3679
  }
3671
3680
  });
3672
3681
  var swapQuoteTool = buildTool({
@@ -3691,17 +3700,26 @@ var swapQuoteTool = buildTool({
3691
3700
  isReadOnly: true,
3692
3701
  async call(input, context) {
3693
3702
  const walletAddress = context.agent ? context.agent.address() : getWalletAddress(context);
3694
- const result = await getSwapQuote({
3695
- walletAddress,
3696
- from: input.from,
3697
- to: input.to,
3698
- amount: input.amount,
3699
- byAmountIn: input.byAmountIn
3700
- });
3701
- return {
3702
- data: result,
3703
- displayText: `${result.fromAmount} ${result.fromToken} \u2192 ${result.toAmount.toFixed(4)} ${result.toToken} (impact: ${(result.priceImpact * 100).toFixed(2)}%, via ${result.route})`
3704
- };
3703
+ const sink = getTelemetrySink();
3704
+ const start = Date.now();
3705
+ try {
3706
+ const result = await getSwapQuote({
3707
+ walletAddress,
3708
+ from: input.from,
3709
+ to: input.to,
3710
+ amount: input.amount,
3711
+ byAmountIn: input.byAmountIn
3712
+ });
3713
+ sink.histogram("cetus.find_route_ms", Date.now() - start);
3714
+ sink.counter("cetus.find_route_count", { outcome: "success" });
3715
+ return {
3716
+ data: result,
3717
+ displayText: `${result.fromAmount} ${result.fromToken} \u2192 ${result.toAmount.toFixed(4)} ${result.toToken} (impact: ${(result.priceImpact * 100).toFixed(2)}%, via ${result.route})`
3718
+ };
3719
+ } catch (err) {
3720
+ sink.counter("cetus.find_route_count", { outcome: "error" });
3721
+ throw err;
3722
+ }
3705
3723
  }
3706
3724
  });
3707
3725
  var voloStakeTool = buildTool({
@@ -6737,7 +6755,7 @@ var REGENERATABLE_READ_TOOLS = /* @__PURE__ */ new Set([
6737
6755
  ]);
6738
6756
 
6739
6757
  // src/compose-bundle.ts
6740
- var MAX_BUNDLE_OPS = 3;
6758
+ var MAX_BUNDLE_OPS = 4;
6741
6759
  var VALID_PAIRS = /* @__PURE__ */ new Set([
6742
6760
  "swap_execute->send_transfer",
6743
6761
  "swap_execute->save_deposit",
@@ -7885,7 +7903,7 @@ ${recipeCtx}`;
7885
7903
  }
7886
7904
  if (guardPassedWrites.length > MAX_BUNDLE_OPS) {
7887
7905
  const cappedError = {
7888
- error: `Atomic bundles are capped at ${MAX_BUNDLE_OPS} ops in Phase 0. You attempted ${guardPassedWrites.length}. Execute these as ${guardPassedWrites.length} sequential single-write transactions: tell the user "I'll do this in ${guardPassedWrites.length} steps", then emit only the FIRST write. After it lands and the user confirms each step, emit the next.`,
7906
+ error: `Atomic bundles are capped at ${MAX_BUNDLE_OPS} ops. You attempted ${guardPassedWrites.length}. Execute these as ${guardPassedWrites.length} sequential single-write transactions: tell the user "I'll do this in ${guardPassedWrites.length} steps", then emit only the FIRST write. After it lands and the user confirms each step, emit the next.`,
7889
7907
  _gate: "max_bundle_ops"
7890
7908
  };
7891
7909
  for (const write of guardPassedWrites) {
@@ -7916,50 +7934,6 @@ ${recipeCtx}`;
7916
7934
  const turnIndex = this.messages.filter((m) => m.role === "assistant").length;
7917
7935
  this.turnPaused = true;
7918
7936
  if (allBundleable) {
7919
- if (guardPassedWrites.length >= 2) {
7920
- let badPair = null;
7921
- for (let i = 0; i < guardPassedWrites.length - 1; i++) {
7922
- const producer = guardPassedWrites[i].call.name;
7923
- const consumer = guardPassedWrites[i + 1].call.name;
7924
- const check = checkValidPair(producer, consumer);
7925
- if (!check.ok) {
7926
- badPair = check;
7927
- break;
7928
- }
7929
- }
7930
- if (badPair !== null) {
7931
- const N = guardPassedWrites.length;
7932
- const stepsPhrase = N === 2 ? "two steps" : `${N} steps`;
7933
- const pairError = {
7934
- error: `Bundle pair '${badPair.pair}' is not in the chaining whitelist. Whitelisted pairs: ${[...VALID_PAIRS].join(", ")}. Run these ${N} writes sequentially: tell the user "I'll do this in ${stepsPhrase}", emit only the first write, then the next after it lands and confirms.`,
7935
- _gate: "pair_not_whitelisted"
7936
- };
7937
- for (const write of guardPassedWrites) {
7938
- yield {
7939
- type: "tool_result",
7940
- toolName: write.call.name,
7941
- toolUseId: write.call.id,
7942
- result: pairError,
7943
- isError: true
7944
- };
7945
- toolResultBlocks.push({
7946
- type: "tool_result",
7947
- toolUseId: write.call.id,
7948
- content: JSON.stringify(pairError),
7949
- isError: true
7950
- });
7951
- }
7952
- this.turnPaused = false;
7953
- this.messages.push({ role: "assistant", content: acc.assistantBlocks });
7954
- this.messages.push({ role: "user", content: toolResultBlocks });
7955
- getTelemetrySink().counter("engine.turn_outcome", {
7956
- entry: freshPrompt !== null ? "submit" : "resume",
7957
- outcome: "pair_not_whitelisted_continue",
7958
- pair: badPair.pair
7959
- });
7960
- continue;
7961
- }
7962
- }
7963
7937
  const completedResults = toolResultBlocks.map((b) => ({
7964
7938
  toolUseId: b.toolUseId,
7965
7939
  content: b.content,