@t2000/engine 1.14.0 → 1.15.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,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
@@ -6737,7 +6737,7 @@ var REGENERATABLE_READ_TOOLS = /* @__PURE__ */ new Set([
6737
6737
  ]);
6738
6738
 
6739
6739
  // src/compose-bundle.ts
6740
- var MAX_BUNDLE_OPS = 3;
6740
+ var MAX_BUNDLE_OPS = 4;
6741
6741
  var VALID_PAIRS = /* @__PURE__ */ new Set([
6742
6742
  "swap_execute->send_transfer",
6743
6743
  "swap_execute->save_deposit",
@@ -7885,7 +7885,7 @@ ${recipeCtx}`;
7885
7885
  }
7886
7886
  if (guardPassedWrites.length > MAX_BUNDLE_OPS) {
7887
7887
  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.`,
7888
+ 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
7889
  _gate: "max_bundle_ops"
7890
7890
  };
7891
7891
  for (const write of guardPassedWrites) {
@@ -7916,50 +7916,6 @@ ${recipeCtx}`;
7916
7916
  const turnIndex = this.messages.filter((m) => m.role === "assistant").length;
7917
7917
  this.turnPaused = true;
7918
7918
  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
7919
  const completedResults = toolResultBlocks.map((b) => ({
7964
7920
  toolUseId: b.toolUseId,
7965
7921
  content: b.content,