@t2000/engine 1.13.0 → 1.14.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.d.ts CHANGED
@@ -1715,41 +1715,46 @@ declare function bundleShortestTtl(toolUseIds: string[], toolNamesById: Record<s
1715
1715
  declare const REGENERATABLE_READ_TOOLS: ReadonlySet<string>;
1716
1716
 
1717
1717
  /**
1718
- * [Phase 0 / SPEC 13 / 2026-05-03] Maximum number of writes per atomic bundle.
1719
- *
1720
- * **History.** This was 5 between F14-fix-2 (2026-05-03 morning) and
1721
- * Phase 0 (2026-05-03 evening). The May 3 production review found that
1722
- * the underlying problem was never the count bundles fail because
1723
- * SDK appenders pre-fetch coins from the wallet and the chained asset
1724
- * doesn't exist there yet (e.g. `swap_execute(USDC→USDsui) +
1725
- * save_deposit(USDsui)` reverts at PREPARE because USDsui isn't in the
1726
- * wallet at compose time). SPEC 13 builds the chained-coin handoff
1727
- * primitive. Until that ships in `1.13.0`, Phase 0 strict-tightens
1728
- * bundles to:
1729
- *
1730
- * - Cap = 2 ops (was 5).
1731
- * - Every adjacent pair MUST be in `VALID_PAIRS` (was: any 2
1732
- * bundleable tools).
1733
- *
1734
- * **Why 2.** Every multi-write production failure today reduces to
1735
- * the same gap: a chained-asset bundle whose intermediate output
1736
- * doesn't exist in the wallet yet. The `VALID_PAIRS` whitelist below
1737
- * enumerates every (producer, consumer) couple where chaining either
1738
- * works today (because the consumer takes a wallet coin that the
1739
- * producer happens to leave there via `tx.transferObjects`) OR will
1740
- * work after Phase 1. Anything outside the whitelist falls through
1741
- * to sequential same outcome the LLM was already producing as a
1742
- * fallback, just without the wasted PREPARE round-trip.
1743
- *
1744
- * **Why not 3+.** 3-op chains require a graph validator (every adjacent
1745
- * pair valid + DAG topology checks) that's Phase 2. Cap stays at 2
1746
- * until Phase 1 lands the validator.
1718
+ * [Phase 0 → Phase 2 / SPEC 13] Maximum number of writes per atomic bundle.
1719
+ *
1720
+ * **History.**
1721
+ * - Pre-Phase-0: 5 (F14-fix-2, 2026-05-03 morning).
1722
+ * - Phase 0 (1.12.0, 2026-05-03 evening): tightened to 2 after the May 3
1723
+ * production review found bundle failures all reduced to chained-asset
1724
+ * gaps the SDK pre-fetched coins from the wallet and the chained
1725
+ * asset didn't exist yet (e.g. `swap_execute(USDC→USDsui) +
1726
+ * save_deposit(USDsui)` reverted at PREPARE because USDsui wasn't in
1727
+ * the wallet at compose time).
1728
+ * - Phase 1 (1.13.0): cap stayed 2. SPEC 13 Phase 1 added the chained-
1729
+ * coin handoff primitive (`PendingActionStep.inputCoinFromStep` +
1730
+ * `composeTx` orchestration loop) but didn't widen the cap. The
1731
+ * primitive is what makes Phase 2's raise to 3 possible — without it,
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.
1747
1752
  *
1748
1753
  * Hosts importing this constant for system-prompt construction get the
1749
- * current cap automatically. Bumping the cap in Phase 1 + Phase 2 + …
1750
- * is a one-line change here that propagates to prompts via the import.
1754
+ * current cap automatically. Bumping the cap is a one-line change here
1755
+ * that propagates to prompts via the import.
1751
1756
  */
1752
- declare const MAX_BUNDLE_OPS = 2;
1757
+ declare const MAX_BUNDLE_OPS = 3;
1753
1758
  /**
1754
1759
  * [Phase 0 / SPEC 13] Whitelisted (producer, consumer) pairs for atomic
1755
1760
  * bundling. Every key has the shape `${producer}->${consumer}`. Bundles
package/dist/index.js CHANGED
@@ -5506,7 +5506,8 @@ function describeAction(tool, call) {
5506
5506
  const input = call.input;
5507
5507
  switch (tool.name) {
5508
5508
  case "save_deposit": {
5509
- return `Save ${input.amount} USDC into lending`;
5509
+ const sAsset = input.asset ?? "USDC";
5510
+ return `Save ${input.amount} ${sAsset} into lending`;
5510
5511
  }
5511
5512
  case "withdraw": {
5512
5513
  const wAsset = input.asset ?? "";
@@ -5514,10 +5515,14 @@ function describeAction(tool, call) {
5514
5515
  }
5515
5516
  case "send_transfer":
5516
5517
  return `Send $${input.amount} to ${input.to}`;
5517
- case "borrow":
5518
- return `Borrow $${input.amount} against collateral`;
5519
- case "repay_debt":
5520
- return `Repay $${input.amount} of outstanding debt`;
5518
+ case "borrow": {
5519
+ const bAsset = input.asset ?? "USDC";
5520
+ return `Borrow $${input.amount} ${bAsset} against collateral`;
5521
+ }
5522
+ case "repay_debt": {
5523
+ const rAsset = input.asset ?? "USDC";
5524
+ return `Repay $${input.amount} ${rAsset} of outstanding debt`;
5525
+ }
5521
5526
  case "claim_rewards":
5522
5527
  return "Claim all pending protocol rewards";
5523
5528
  case "pay_api": {
@@ -6732,7 +6737,7 @@ var REGENERATABLE_READ_TOOLS = /* @__PURE__ */ new Set([
6732
6737
  ]);
6733
6738
 
6734
6739
  // src/compose-bundle.ts
6735
- var MAX_BUNDLE_OPS = 2;
6740
+ var MAX_BUNDLE_OPS = 3;
6736
6741
  var VALID_PAIRS = /* @__PURE__ */ new Set([
6737
6742
  "swap_execute->send_transfer",
6738
6743
  "swap_execute->save_deposit",
@@ -6808,6 +6813,10 @@ function composeBundleFromToolResults(input) {
6808
6813
  const consumer = input.pendingWrites[i];
6809
6814
  if (shouldChainCoin(producer, consumer)) {
6810
6815
  steps[i].inputCoinFromStep = i - 1;
6816
+ getTelemetrySink().counter("engine.bundle_chain_mode_set", {
6817
+ producer: producer.name,
6818
+ consumer: consumer.name
6819
+ });
6811
6820
  }
6812
6821
  }
6813
6822
  const regenerateToolUseIds = input.readResults.filter((r) => REGENERATABLE_READ_TOOLS.has(r.toolName)).map((r) => r.toolUseId);
@@ -7907,13 +7916,22 @@ ${recipeCtx}`;
7907
7916
  const turnIndex = this.messages.filter((m) => m.role === "assistant").length;
7908
7917
  this.turnPaused = true;
7909
7918
  if (allBundleable) {
7910
- if (guardPassedWrites.length === 2) {
7911
- const producer = guardPassedWrites[0].call.name;
7912
- const consumer = guardPassedWrites[1].call.name;
7913
- const check = checkValidPair(producer, consumer);
7914
- if (!check.ok) {
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`;
7915
7933
  const pairError = {
7916
- error: `Bundle pair '${check.pair}' is not in the Phase 0 chaining whitelist. Whitelisted pairs: ${[...VALID_PAIRS].join(", ")}. Run these two writes sequentially: tell the user "I'll do this in two steps", emit only the first write, then the second after it lands and confirms.`,
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.`,
7917
7935
  _gate: "pair_not_whitelisted"
7918
7936
  };
7919
7937
  for (const write of guardPassedWrites) {
@@ -7937,7 +7955,7 @@ ${recipeCtx}`;
7937
7955
  getTelemetrySink().counter("engine.turn_outcome", {
7938
7956
  entry: freshPrompt !== null ? "submit" : "resume",
7939
7957
  outcome: "pair_not_whitelisted_continue",
7940
- pair: check.pair
7958
+ pair: badPair.pair
7941
7959
  });
7942
7960
  continue;
7943
7961
  }