@t2000/engine 1.24.11 → 1.24.13

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
@@ -53,25 +53,6 @@ declare function extractAllProactiveMarkers(text: string): Array<{
53
53
  subjectKey: string;
54
54
  }>;
55
55
 
56
- type PostWritePollOutcome = 'detected_change' | 'ceiling' | 'aborted' | 'fallback_no_baseline' | 'fallback_no_address' | 'fallback_no_rpc';
57
- interface PostWritePollResult {
58
- outcome: PostWritePollOutcome;
59
- /** Number of poll attempts made (0 if fallback fired before polling). */
60
- attempts: number;
61
- /** Wall-clock ms from `pollForIndexerCatchup` entry to return. */
62
- resolvedAtMs: number;
63
- }
64
- interface PostWritePollOptions {
65
- suiRpcUrl: string | undefined;
66
- address: string | undefined;
67
- /** Hard ceiling on total wait time. Default 1500ms (matches old fixed sleep). */
68
- ceilingMs: number;
69
- /** Wait between poll attempts. Default 250ms. */
70
- pollIntervalMs: number;
71
- signal: AbortSignal;
72
- }
73
- declare function pollForIndexerCatchup(options: PostWritePollOptions): Promise<PostWritePollResult>;
74
-
75
56
  /** Rough token count for a message array. */
76
57
  declare function estimateTokens(messages: Message[]): number;
77
58
  interface CompactOptions {
@@ -1736,41 +1717,6 @@ interface EngineConfig {
1736
1717
  * Omit (undefined / empty map) to disable post-write refresh entirely.
1737
1718
  */
1738
1719
  postWriteRefresh?: Record<string, string[]>;
1739
- /**
1740
- * [SPEC 19 Phase B / 2026-05-09] Pre-warmed indexer-catchup poll Promise.
1741
- *
1742
- * Hosts that know the wallet address + Sui RPC URL the moment a resume
1743
- * request lands (e.g. `app/api/engine/resume/route.ts`, where both are
1744
- * available immediately after JWT validation) can fire
1745
- * `pollForIndexerCatchup` in parallel with `createEngine` and pass the
1746
- * returned Promise here. The engine awaits this Promise inside
1747
- * `runPostWriteRefresh` instead of starting a fresh poll, eliminating
1748
- * up to ~1.5s of serial wait when engine boot wall-clock ≥ poll
1749
- * wall-clock.
1750
- *
1751
- * Behavior:
1752
- * - Promise resolves before refresh starts → instant pass-through
1753
- * (poll already done during boot). This is the typical case.
1754
- * - Promise still pending when refresh starts → engine awaits it.
1755
- * Net effect: same as pre-Phase-B fresh-poll behavior.
1756
- * - Promise undefined → engine starts a fresh poll (pre-Phase-B path).
1757
- * - Promise rejects → engine logs warn + falls back to fresh poll
1758
- * (correctness preserved; same as `outcome: 'fallback_*'`).
1759
- *
1760
- * Telemetry: `engine.pwr.sleep_ms` carries `source` tag = `'pre-warmed' | 'engine'`
1761
- * so the host can verify the parallelization is shaving wall-clock.
1762
- * On the pre-warmed path, the `outcome` + `attempts` tags are inherited
1763
- * from the host's poll result — same shape, just resolved earlier.
1764
- *
1765
- * Safe because:
1766
- * - Poll baseline is captured via Sui RPC `getAllBalances` and is
1767
- * independent of any BlockVision cache state. Pre-firing the poll
1768
- * before engine boot does NOT race the engine's cache invalidation.
1769
- * - The poll's correctness contract (never proceed until indexer
1770
- * reflects the write) is preserved — it just runs on a different
1771
- * timeline.
1772
- */
1773
- indexerCatchupPromise?: Promise<PostWritePollResult>;
1774
1720
  }
1775
1721
  interface LLMProvider {
1776
1722
  chat(params: ChatParams): AsyncGenerator<ProviderEvent>;
@@ -1921,7 +1867,6 @@ declare class QueryEngine {
1921
1867
  private readonly blockvisionApiKey;
1922
1868
  private readonly portfolioCache;
1923
1869
  private readonly postWriteRefresh;
1924
- private readonly indexerCatchupPromise;
1925
1870
  private matchedRecipe;
1926
1871
  private messages;
1927
1872
  private abortController;
@@ -2071,6 +2016,25 @@ declare class QueryEngine {
2071
2016
  */
2072
2017
  declare function validateHistory(messages: Message[]): Message[];
2073
2018
 
2019
+ type PostWritePollOutcome = 'detected_change' | 'ceiling' | 'aborted' | 'fallback_no_baseline' | 'fallback_no_address' | 'fallback_no_rpc';
2020
+ interface PostWritePollResult {
2021
+ outcome: PostWritePollOutcome;
2022
+ /** Number of poll attempts made (0 if fallback fired before polling). */
2023
+ attempts: number;
2024
+ /** Wall-clock ms from `pollForIndexerCatchup` entry to return. */
2025
+ resolvedAtMs: number;
2026
+ }
2027
+ interface PostWritePollOptions {
2028
+ suiRpcUrl: string | undefined;
2029
+ address: string | undefined;
2030
+ /** Hard ceiling on total wait time. Default 1500ms (matches old fixed sleep). */
2031
+ ceilingMs: number;
2032
+ /** Wait between poll attempts. Default 250ms. */
2033
+ pollIntervalMs: number;
2034
+ signal: AbortSignal;
2035
+ }
2036
+ declare function pollForIndexerCatchup(options: PostWritePollOptions): Promise<PostWritePollResult>;
2037
+
2074
2038
  /**
2075
2039
  * SPEC 7 v0.3 Quote-Refresh ReviewCard — per-tool result freshness budgets.
2076
2040
  *
package/dist/index.js CHANGED
@@ -5786,120 +5786,6 @@ Only offer to execute actions you have tools for. If you retrieved a quote, data
5786
5786
  - Cap: at most ONE proactive block per turn.
5787
5787
  - Skip proactive blocks when nothing notable changed since the last turn, when the user is mid-flow on something else, or when you'd just be restating the financial-context block. Quality over quantity \u2014 a block ignored is worse than no block.`;
5788
5788
 
5789
- // src/post-write-poll.ts
5790
- async function pollForIndexerCatchup(options) {
5791
- const { suiRpcUrl, address, ceilingMs, pollIntervalMs, signal } = options;
5792
- const start = Date.now();
5793
- if (!suiRpcUrl) {
5794
- await sleepWithFallback(ceilingMs, signal);
5795
- return {
5796
- outcome: "fallback_no_rpc",
5797
- attempts: 0,
5798
- resolvedAtMs: Date.now() - start
5799
- };
5800
- }
5801
- if (!address) {
5802
- await sleepWithFallback(ceilingMs, signal);
5803
- return {
5804
- outcome: "fallback_no_address",
5805
- attempts: 0,
5806
- resolvedAtMs: Date.now() - start
5807
- };
5808
- }
5809
- let baseline;
5810
- try {
5811
- const coins = await fetchWalletCoins(address, suiRpcUrl);
5812
- baseline = new Map(
5813
- coins.map((c) => [c.coinType, BigInt(c.totalBalance)])
5814
- );
5815
- } catch (err) {
5816
- console.warn(
5817
- "[post-write-poll] baseline fetch failed; falling back to fixed sleep:",
5818
- err
5819
- );
5820
- await sleepWithFallback(ceilingMs, signal);
5821
- return {
5822
- outcome: "fallback_no_baseline",
5823
- attempts: 0,
5824
- resolvedAtMs: Date.now() - start
5825
- };
5826
- }
5827
- const maxAttempts = Math.max(1, Math.floor(ceilingMs / pollIntervalMs));
5828
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
5829
- if (signal.aborted) {
5830
- return {
5831
- outcome: "aborted",
5832
- attempts: attempt - 1,
5833
- resolvedAtMs: Date.now() - start
5834
- };
5835
- }
5836
- await new Promise((resolve) => {
5837
- const t = setTimeout(resolve, pollIntervalMs);
5838
- signal.addEventListener(
5839
- "abort",
5840
- () => {
5841
- clearTimeout(t);
5842
- resolve();
5843
- },
5844
- { once: true }
5845
- );
5846
- });
5847
- if (signal.aborted) {
5848
- return {
5849
- outcome: "aborted",
5850
- attempts: attempt,
5851
- resolvedAtMs: Date.now() - start
5852
- };
5853
- }
5854
- let current;
5855
- try {
5856
- const coins = await fetchWalletCoins(address, suiRpcUrl);
5857
- current = new Map(
5858
- coins.map((c) => [c.coinType, BigInt(c.totalBalance)])
5859
- );
5860
- } catch (err) {
5861
- console.warn(
5862
- `[post-write-poll] poll attempt ${attempt} failed; continuing:`,
5863
- err
5864
- );
5865
- continue;
5866
- }
5867
- if (balancesDiffer(baseline, current)) {
5868
- return {
5869
- outcome: "detected_change",
5870
- attempts: attempt,
5871
- resolvedAtMs: Date.now() - start
5872
- };
5873
- }
5874
- }
5875
- return {
5876
- outcome: "ceiling",
5877
- attempts: maxAttempts,
5878
- resolvedAtMs: Date.now() - start
5879
- };
5880
- }
5881
- function balancesDiffer(a, b) {
5882
- if (a.size !== b.size) return true;
5883
- for (const [coinType, balance] of a) {
5884
- if (b.get(coinType) !== balance) return true;
5885
- }
5886
- return false;
5887
- }
5888
- async function sleepWithFallback(ms, signal) {
5889
- if (signal.aborted) return;
5890
- await new Promise((resolve) => {
5891
- const t = setTimeout(resolve, ms);
5892
- signal.addEventListener(
5893
- "abort",
5894
- () => {
5895
- clearTimeout(t);
5896
- resolve();
5897
- },
5898
- { once: true }
5899
- );
5900
- });
5901
- }
5902
-
5903
5789
  // src/proactive-marker.ts
5904
5790
  var VALID_TYPES = /* @__PURE__ */ new Set([
5905
5791
  "idle_balance",
@@ -7506,12 +7392,6 @@ var QueryEngine = class {
7506
7392
  // [v1.5] See `EngineConfig.postWriteRefresh` — drives the post-write
7507
7393
  // synthetic read injection in `resumeWithToolResult`.
7508
7394
  postWriteRefresh;
7509
- // [SPEC 19 Phase B / 2026-05-09] Pre-warmed indexer-catchup poll Promise.
7510
- // When provided by the host (resume route fires `pollForIndexerCatchup`
7511
- // in parallel with `createEngine`), `runPostWriteRefresh` awaits this
7512
- // instead of starting a fresh poll. See `EngineConfig.indexerCatchupPromise`
7513
- // for the full design rationale.
7514
- indexerCatchupPromise;
7515
7395
  matchedRecipe = null;
7516
7396
  messages = [];
7517
7397
  abortController = null;
@@ -7587,7 +7467,6 @@ var QueryEngine = class {
7587
7467
  this.onAutoExecuted = config.onAutoExecuted;
7588
7468
  this.onGuardFired = config.onGuardFired;
7589
7469
  this.postWriteRefresh = config.postWriteRefresh;
7590
- this.indexerCatchupPromise = config.indexerCatchupPromise;
7591
7470
  this.blockvisionApiKey = config.blockvisionApiKey;
7592
7471
  this.portfolioCache = config.portfolioCache;
7593
7472
  this.tools = config.tools ?? (config.agent ? getDefaultTools() : []);
@@ -7702,7 +7581,10 @@ var QueryEngine = class {
7702
7581
  );
7703
7582
  }
7704
7583
  if (action.assistantContent?.length) {
7705
- this.messages.push({ role: "assistant", content: action.assistantContent });
7584
+ this.messages.push({
7585
+ role: "assistant",
7586
+ content: stripPseudoThinking(action.assistantContent)
7587
+ });
7706
7588
  }
7707
7589
  const allResults = [
7708
7590
  ...(action.completedResults ?? []).map((r) => ({
@@ -7808,7 +7690,7 @@ var QueryEngine = class {
7808
7690
  if (pendingInput.assistantContent.length > 0) {
7809
7691
  this.messages.push({
7810
7692
  role: "assistant",
7811
- content: pendingInput.assistantContent
7693
+ content: stripPseudoThinking(pendingInput.assistantContent)
7812
7694
  });
7813
7695
  }
7814
7696
  const tool = findTool(this.tools, pendingInput.toolName);
@@ -7987,45 +7869,9 @@ var QueryEngine = class {
7987
7869
  Date.now() - cacheInvalidationStart,
7988
7870
  { has_wallet: this.walletAddress ? "1" : "0" }
7989
7871
  );
7990
- const sleepStart = Date.now();
7991
- let pollResult;
7992
- let sleepSource = "engine";
7993
- if (this.indexerCatchupPromise) {
7994
- try {
7995
- pollResult = await this.indexerCatchupPromise;
7996
- sleepSource = "pre-warmed";
7997
- } catch (err) {
7998
- console.warn(
7999
- "[engine.pwr] pre-warmed indexer catchup promise rejected; falling back to fresh poll:",
8000
- err
8001
- );
8002
- pollResult = await pollForIndexerCatchup({
8003
- suiRpcUrl: this.suiRpcUrl,
8004
- address: this.walletAddress,
8005
- ceilingMs: 1500,
8006
- pollIntervalMs: 250,
8007
- signal
8008
- });
8009
- }
8010
- } else {
8011
- pollResult = await pollForIndexerCatchup({
8012
- suiRpcUrl: this.suiRpcUrl,
8013
- address: this.walletAddress,
8014
- ceilingMs: 1500,
8015
- pollIntervalMs: 250,
8016
- signal
8017
- });
8018
- }
8019
- getTelemetrySink().histogram(
8020
- "engine.pwr.sleep_ms",
8021
- Date.now() - sleepStart,
8022
- {
8023
- aborted: signal.aborted ? "1" : "0",
8024
- outcome: pollResult.outcome,
8025
- attempts: String(pollResult.attempts),
8026
- source: sleepSource
8027
- }
8028
- );
7872
+ getTelemetrySink().counter("engine.pwr.skipped_sleep_count", {
7873
+ has_wallet: this.walletAddress ? "1" : "0"
7874
+ });
8029
7875
  if (signal.aborted) return;
8030
7876
  const refreshStart = Date.now();
8031
7877
  const idStem = `pwr_${action.toolUseId.slice(-6)}`;
@@ -8493,14 +8339,20 @@ ${recipeCtx}`;
8493
8339
  const hasEarlyResults = earlyResultBlocks.length > 0;
8494
8340
  const hasRemainingCalls = acc.pendingToolCalls.length > 0;
8495
8341
  if (!hasEarlyResults && !hasRemainingCalls) {
8496
- this.messages.push({ role: "assistant", content: acc.assistantBlocks });
8342
+ this.messages.push({
8343
+ role: "assistant",
8344
+ content: stripPseudoThinking(acc.assistantBlocks)
8345
+ });
8497
8346
  getTelemetrySink().histogram("anthropic.latency_ms", Date.now() - turnStartMs);
8498
8347
  yield { type: "turn_complete", stopReason: acc.stopReason };
8499
8348
  recordTurnOutcome("turn_complete", { stopReason: acc.stopReason });
8500
8349
  return;
8501
8350
  }
8502
8351
  if (signal.aborted) {
8503
- this.messages.push({ role: "assistant", content: acc.assistantBlocks });
8352
+ this.messages.push({
8353
+ role: "assistant",
8354
+ content: stripPseudoThinking(acc.assistantBlocks)
8355
+ });
8504
8356
  if (hasEarlyResults) {
8505
8357
  this.messages.push({ role: "user", content: earlyResultBlocks });
8506
8358
  }
@@ -8806,7 +8658,10 @@ ${recipeCtx}`;
8806
8658
  guardPassedWrites.push(write);
8807
8659
  }
8808
8660
  if (anyGuardBlocked) {
8809
- this.messages.push({ role: "assistant", content: acc.assistantBlocks });
8661
+ this.messages.push({
8662
+ role: "assistant",
8663
+ content: stripPseudoThinking(acc.assistantBlocks)
8664
+ });
8810
8665
  this.messages.push({ role: "user", content: toolResultBlocks });
8811
8666
  getTelemetrySink().counter("engine.turn_outcome", {
8812
8667
  entry: freshPrompt !== null ? "submit" : "resume",
@@ -8837,7 +8692,10 @@ ${recipeCtx}`;
8837
8692
  isError: true
8838
8693
  });
8839
8694
  }
8840
- this.messages.push({ role: "assistant", content: acc.assistantBlocks });
8695
+ this.messages.push({
8696
+ role: "assistant",
8697
+ content: stripPseudoThinking(acc.assistantBlocks)
8698
+ });
8841
8699
  this.messages.push({ role: "user", content: toolResultBlocks });
8842
8700
  getTelemetrySink().counter("engine.turn_outcome", {
8843
8701
  entry: freshPrompt !== null ? "submit" : "resume",
@@ -8922,7 +8780,10 @@ ${recipeCtx}`;
8922
8780
  recordTurnOutcome("pending_action_single");
8923
8781
  return;
8924
8782
  }
8925
- this.messages.push({ role: "assistant", content: acc.assistantBlocks });
8783
+ this.messages.push({
8784
+ role: "assistant",
8785
+ content: stripPseudoThinking(acc.assistantBlocks)
8786
+ });
8926
8787
  this.messages.push({ role: "user", content: toolResultBlocks });
8927
8788
  const toolUseBlocks = acc.assistantBlocks.filter((b) => b.type === "tool_use");
8928
8789
  const allUpdateTodo = toolUseBlocks.length > 0 && toolUseBlocks.every((b) => b.name === "update_todo");
@@ -9064,6 +8925,23 @@ function isCorruptHistoryError(err) {
9064
8925
  const msg = err instanceof Error ? err.message : String(err);
9065
8926
  return msg.includes("tool_use") && msg.includes("tool_result") || msg.includes("roles must alternate") || msg.includes("400") && msg.includes("invalid_request_error");
9066
8927
  }
8928
+ function stripPseudoThinking(blocks) {
8929
+ const out = [];
8930
+ for (const b of blocks) {
8931
+ if (b.type !== "text") {
8932
+ out.push(b);
8933
+ continue;
8934
+ }
8935
+ const stripped = b.text.replace(/<thinking\b[^>]*>[\s\S]*?(?:<\/thinking>|$)/gi, "").trim();
8936
+ if (stripped.length > 0) {
8937
+ out.push({ ...b, text: stripped });
8938
+ }
8939
+ }
8940
+ if (out.length === 0 && blocks.length > 0) {
8941
+ out.push({ type: "text", text: "[narration omitted]" });
8942
+ }
8943
+ return out;
8944
+ }
9067
8945
  function validateHistory(messages) {
9068
8946
  const result = [];
9069
8947
  let i = 0;
@@ -9169,6 +9047,120 @@ function harnessShapeForEffort(effort) {
9169
9047
  return "max";
9170
9048
  }
9171
9049
  }
9050
+
9051
+ // src/post-write-poll.ts
9052
+ async function pollForIndexerCatchup(options) {
9053
+ const { suiRpcUrl, address, ceilingMs, pollIntervalMs, signal } = options;
9054
+ const start = Date.now();
9055
+ if (!suiRpcUrl) {
9056
+ await sleepWithFallback(ceilingMs, signal);
9057
+ return {
9058
+ outcome: "fallback_no_rpc",
9059
+ attempts: 0,
9060
+ resolvedAtMs: Date.now() - start
9061
+ };
9062
+ }
9063
+ if (!address) {
9064
+ await sleepWithFallback(ceilingMs, signal);
9065
+ return {
9066
+ outcome: "fallback_no_address",
9067
+ attempts: 0,
9068
+ resolvedAtMs: Date.now() - start
9069
+ };
9070
+ }
9071
+ let baseline;
9072
+ try {
9073
+ const coins = await fetchWalletCoins(address, suiRpcUrl);
9074
+ baseline = new Map(
9075
+ coins.map((c) => [c.coinType, BigInt(c.totalBalance)])
9076
+ );
9077
+ } catch (err) {
9078
+ console.warn(
9079
+ "[post-write-poll] baseline fetch failed; falling back to fixed sleep:",
9080
+ err
9081
+ );
9082
+ await sleepWithFallback(ceilingMs, signal);
9083
+ return {
9084
+ outcome: "fallback_no_baseline",
9085
+ attempts: 0,
9086
+ resolvedAtMs: Date.now() - start
9087
+ };
9088
+ }
9089
+ const maxAttempts = Math.max(1, Math.floor(ceilingMs / pollIntervalMs));
9090
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
9091
+ if (signal.aborted) {
9092
+ return {
9093
+ outcome: "aborted",
9094
+ attempts: attempt - 1,
9095
+ resolvedAtMs: Date.now() - start
9096
+ };
9097
+ }
9098
+ await new Promise((resolve) => {
9099
+ const t = setTimeout(resolve, pollIntervalMs);
9100
+ signal.addEventListener(
9101
+ "abort",
9102
+ () => {
9103
+ clearTimeout(t);
9104
+ resolve();
9105
+ },
9106
+ { once: true }
9107
+ );
9108
+ });
9109
+ if (signal.aborted) {
9110
+ return {
9111
+ outcome: "aborted",
9112
+ attempts: attempt,
9113
+ resolvedAtMs: Date.now() - start
9114
+ };
9115
+ }
9116
+ let current;
9117
+ try {
9118
+ const coins = await fetchWalletCoins(address, suiRpcUrl);
9119
+ current = new Map(
9120
+ coins.map((c) => [c.coinType, BigInt(c.totalBalance)])
9121
+ );
9122
+ } catch (err) {
9123
+ console.warn(
9124
+ `[post-write-poll] poll attempt ${attempt} failed; continuing:`,
9125
+ err
9126
+ );
9127
+ continue;
9128
+ }
9129
+ if (balancesDiffer(baseline, current)) {
9130
+ return {
9131
+ outcome: "detected_change",
9132
+ attempts: attempt,
9133
+ resolvedAtMs: Date.now() - start
9134
+ };
9135
+ }
9136
+ }
9137
+ return {
9138
+ outcome: "ceiling",
9139
+ attempts: maxAttempts,
9140
+ resolvedAtMs: Date.now() - start
9141
+ };
9142
+ }
9143
+ function balancesDiffer(a, b) {
9144
+ if (a.size !== b.size) return true;
9145
+ for (const [coinType, balance] of a) {
9146
+ if (b.get(coinType) !== balance) return true;
9147
+ }
9148
+ return false;
9149
+ }
9150
+ async function sleepWithFallback(ms, signal) {
9151
+ if (signal.aborted) return;
9152
+ await new Promise((resolve) => {
9153
+ const t = setTimeout(resolve, ms);
9154
+ signal.addEventListener(
9155
+ "abort",
9156
+ () => {
9157
+ clearTimeout(t);
9158
+ resolve();
9159
+ },
9160
+ { once: true }
9161
+ );
9162
+ });
9163
+ }
9172
9164
  async function regenerateBundle(engine, action) {
9173
9165
  if (action.canRegenerate !== true) {
9174
9166
  return {