@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 +19 -55
- package/dist/index.js +159 -167
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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({
|
|
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
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
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({
|
|
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({
|
|
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({
|
|
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({
|
|
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({
|
|
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 {
|