@blockrun/clawrouter 0.12.94 → 0.12.96

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.
Files changed (282) hide show
  1. package/dist/auth.d.ts +82 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +388 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/balance.d.ts +97 -0
  6. package/dist/balance.d.ts.map +1 -0
  7. package/dist/balance.js +157 -0
  8. package/dist/balance.js.map +1 -0
  9. package/dist/cli.d.ts +15 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +256 -54991
  12. package/dist/cli.js.map +1 -1
  13. package/dist/compression/codebook.d.ts +24 -0
  14. package/dist/compression/codebook.d.ts.map +1 -0
  15. package/dist/compression/codebook.js +119 -0
  16. package/dist/compression/codebook.js.map +1 -0
  17. package/dist/compression/index.d.ts +33 -0
  18. package/dist/compression/index.d.ts.map +1 -0
  19. package/dist/compression/index.js +246 -0
  20. package/dist/compression/index.js.map +1 -0
  21. package/dist/compression/layers/deduplication.d.ts +28 -0
  22. package/dist/compression/layers/deduplication.d.ts.map +1 -0
  23. package/dist/compression/layers/deduplication.js +101 -0
  24. package/dist/compression/layers/deduplication.js.map +1 -0
  25. package/dist/compression/layers/dictionary.d.ts +21 -0
  26. package/dist/compression/layers/dictionary.d.ts.map +1 -0
  27. package/dist/compression/layers/dictionary.js +73 -0
  28. package/dist/compression/layers/dictionary.js.map +1 -0
  29. package/dist/compression/layers/dynamic-codebook.d.ts +26 -0
  30. package/dist/compression/layers/dynamic-codebook.d.ts.map +1 -0
  31. package/dist/compression/layers/dynamic-codebook.js +149 -0
  32. package/dist/compression/layers/dynamic-codebook.js.map +1 -0
  33. package/dist/compression/layers/json-compact.d.ts +23 -0
  34. package/dist/compression/layers/json-compact.d.ts.map +1 -0
  35. package/dist/compression/layers/json-compact.js +79 -0
  36. package/dist/compression/layers/json-compact.js.map +1 -0
  37. package/dist/compression/layers/observation.d.ts +21 -0
  38. package/dist/compression/layers/observation.d.ts.map +1 -0
  39. package/dist/compression/layers/observation.js +130 -0
  40. package/dist/compression/layers/observation.js.map +1 -0
  41. package/dist/compression/layers/paths.d.ts +24 -0
  42. package/dist/compression/layers/paths.d.ts.map +1 -0
  43. package/dist/compression/layers/paths.js +110 -0
  44. package/dist/compression/layers/paths.js.map +1 -0
  45. package/dist/compression/layers/whitespace.d.ts +27 -0
  46. package/dist/compression/layers/whitespace.d.ts.map +1 -0
  47. package/dist/compression/layers/whitespace.js +60 -0
  48. package/dist/compression/layers/whitespace.js.map +1 -0
  49. package/dist/compression/types.d.ts +84 -0
  50. package/dist/compression/types.d.ts.map +1 -0
  51. package/dist/compression/types.js +27 -0
  52. package/dist/compression/types.js.map +1 -0
  53. package/dist/config.d.ts +12 -0
  54. package/dist/config.d.ts.map +1 -0
  55. package/dist/config.js +22 -0
  56. package/dist/config.js.map +1 -0
  57. package/dist/dedup.d.ts +34 -0
  58. package/dist/dedup.d.ts.map +1 -0
  59. package/dist/dedup.js +150 -0
  60. package/dist/dedup.js.map +1 -0
  61. package/dist/doctor.d.ts +8 -0
  62. package/dist/doctor.d.ts.map +1 -0
  63. package/dist/doctor.js +375 -0
  64. package/dist/doctor.js.map +1 -0
  65. package/dist/error-classification.test.d.ts +2 -0
  66. package/dist/error-classification.test.d.ts.map +1 -0
  67. package/dist/error-classification.test.js +59 -0
  68. package/dist/error-classification.test.js.map +1 -0
  69. package/dist/errors.d.ts +54 -0
  70. package/dist/errors.d.ts.map +1 -0
  71. package/dist/errors.js +85 -0
  72. package/dist/errors.js.map +1 -0
  73. package/dist/exclude-models.d.ts +28 -0
  74. package/dist/exclude-models.d.ts.map +1 -0
  75. package/dist/exclude-models.integration.test.d.ts +2 -0
  76. package/dist/exclude-models.integration.test.d.ts.map +1 -0
  77. package/dist/exclude-models.integration.test.js +46 -0
  78. package/dist/exclude-models.integration.test.js.map +1 -0
  79. package/dist/exclude-models.js +70 -0
  80. package/dist/exclude-models.js.map +1 -0
  81. package/dist/exclude-models.test.d.ts +5 -0
  82. package/dist/exclude-models.test.d.ts.map +1 -0
  83. package/dist/exclude-models.test.js +95 -0
  84. package/dist/exclude-models.test.js.map +1 -0
  85. package/dist/fs-read.d.ts +11 -0
  86. package/dist/fs-read.d.ts.map +1 -0
  87. package/dist/fs-read.js +47 -0
  88. package/dist/fs-read.js.map +1 -0
  89. package/dist/index.d.ts +34 -1368
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +1071 -56191
  92. package/dist/index.js.map +1 -1
  93. package/dist/journal.d.ts +67 -0
  94. package/dist/journal.d.ts.map +1 -0
  95. package/dist/journal.js +184 -0
  96. package/dist/journal.js.map +1 -0
  97. package/dist/journal.test.d.ts +2 -0
  98. package/dist/journal.test.d.ts.map +1 -0
  99. package/dist/journal.test.js +201 -0
  100. package/dist/journal.test.js.map +1 -0
  101. package/dist/logger.d.ts +33 -0
  102. package/dist/logger.d.ts.map +1 -0
  103. package/dist/logger.js +35 -0
  104. package/dist/logger.js.map +1 -0
  105. package/dist/models.d.ts +107 -0
  106. package/dist/models.d.ts.map +1 -0
  107. package/dist/models.js +941 -0
  108. package/dist/models.js.map +1 -0
  109. package/dist/models.test.d.ts +2 -0
  110. package/dist/models.test.d.ts.map +1 -0
  111. package/dist/models.test.js +38 -0
  112. package/dist/models.test.js.map +1 -0
  113. package/dist/partners/index.d.ts +8 -0
  114. package/dist/partners/index.d.ts.map +1 -0
  115. package/dist/partners/index.js +6 -0
  116. package/dist/partners/index.js.map +1 -0
  117. package/dist/partners/registry.d.ts +51 -0
  118. package/dist/partners/registry.d.ts.map +1 -0
  119. package/dist/partners/registry.js +256 -0
  120. package/dist/partners/registry.js.map +1 -0
  121. package/dist/partners/tools.d.ts +24 -0
  122. package/dist/partners/tools.d.ts.map +1 -0
  123. package/dist/partners/tools.js +97 -0
  124. package/dist/partners/tools.js.map +1 -0
  125. package/dist/payment-preauth.d.ts +17 -0
  126. package/dist/payment-preauth.d.ts.map +1 -0
  127. package/dist/payment-preauth.js +102 -0
  128. package/dist/payment-preauth.js.map +1 -0
  129. package/dist/provider.d.ts +19 -0
  130. package/dist/provider.d.ts.map +1 -0
  131. package/dist/provider.js +45 -0
  132. package/dist/provider.js.map +1 -0
  133. package/dist/proxy.d.ts +210 -0
  134. package/dist/proxy.d.ts.map +1 -0
  135. package/dist/proxy.debrand.test.d.ts +2 -0
  136. package/dist/proxy.debrand.test.d.ts.map +1 -0
  137. package/dist/proxy.debrand.test.js +101 -0
  138. package/dist/proxy.debrand.test.js.map +1 -0
  139. package/dist/proxy.degraded-response.test.d.ts +2 -0
  140. package/dist/proxy.degraded-response.test.d.ts.map +1 -0
  141. package/dist/proxy.degraded-response.test.js +105 -0
  142. package/dist/proxy.degraded-response.test.js.map +1 -0
  143. package/dist/proxy.image-download.test.d.ts +2 -0
  144. package/dist/proxy.image-download.test.d.ts.map +1 -0
  145. package/dist/proxy.image-download.test.js +92 -0
  146. package/dist/proxy.image-download.test.js.map +1 -0
  147. package/dist/proxy.js +3810 -0
  148. package/dist/proxy.js.map +1 -0
  149. package/dist/proxy.models-endpoint.test.d.ts +2 -0
  150. package/dist/proxy.models-endpoint.test.d.ts.map +1 -0
  151. package/dist/proxy.models-endpoint.test.js +19 -0
  152. package/dist/proxy.models-endpoint.test.js.map +1 -0
  153. package/dist/proxy.multiturn-thinking.test.d.ts +2 -0
  154. package/dist/proxy.multiturn-thinking.test.d.ts.map +1 -0
  155. package/dist/proxy.multiturn-thinking.test.js +82 -0
  156. package/dist/proxy.multiturn-thinking.test.js.map +1 -0
  157. package/dist/proxy.payment-chain-reuse.test.d.ts +2 -0
  158. package/dist/proxy.payment-chain-reuse.test.d.ts.map +1 -0
  159. package/dist/proxy.payment-chain-reuse.test.js +30 -0
  160. package/dist/proxy.payment-chain-reuse.test.js.map +1 -0
  161. package/dist/proxy.solana-resilience.test.d.ts +12 -0
  162. package/dist/proxy.solana-resilience.test.d.ts.map +1 -0
  163. package/dist/proxy.solana-resilience.test.js +39 -0
  164. package/dist/proxy.solana-resilience.test.js.map +1 -0
  165. package/dist/report.d.ts +6 -0
  166. package/dist/report.d.ts.map +1 -0
  167. package/dist/report.js +44 -0
  168. package/dist/report.js.map +1 -0
  169. package/dist/response-cache.advanced.test.d.ts +2 -0
  170. package/dist/response-cache.advanced.test.d.ts.map +1 -0
  171. package/dist/response-cache.advanced.test.js +566 -0
  172. package/dist/response-cache.advanced.test.js.map +1 -0
  173. package/dist/response-cache.d.ts +85 -0
  174. package/dist/response-cache.d.ts.map +1 -0
  175. package/dist/response-cache.edge-cases.test.d.ts +2 -0
  176. package/dist/response-cache.edge-cases.test.d.ts.map +1 -0
  177. package/dist/response-cache.edge-cases.test.js +488 -0
  178. package/dist/response-cache.edge-cases.test.js.map +1 -0
  179. package/dist/response-cache.extreme.test.d.ts +2 -0
  180. package/dist/response-cache.extreme.test.d.ts.map +1 -0
  181. package/dist/response-cache.extreme.test.js +561 -0
  182. package/dist/response-cache.extreme.test.js.map +1 -0
  183. package/dist/response-cache.js +240 -0
  184. package/dist/response-cache.js.map +1 -0
  185. package/dist/response-cache.test.d.ts +2 -0
  186. package/dist/response-cache.test.d.ts.map +1 -0
  187. package/dist/response-cache.test.js +242 -0
  188. package/dist/response-cache.test.js.map +1 -0
  189. package/dist/retry.d.ts +42 -0
  190. package/dist/retry.d.ts.map +1 -0
  191. package/dist/retry.js +99 -0
  192. package/dist/retry.js.map +1 -0
  193. package/dist/router/config.d.ts +11 -0
  194. package/dist/router/config.d.ts.map +1 -0
  195. package/dist/router/config.js +1215 -0
  196. package/dist/router/config.js.map +1 -0
  197. package/dist/router/index.d.ts +18 -0
  198. package/dist/router/index.d.ts.map +1 -0
  199. package/dist/router/index.js +19 -0
  200. package/dist/router/index.js.map +1 -0
  201. package/dist/router/llm-classifier.d.ts +29 -0
  202. package/dist/router/llm-classifier.d.ts.map +1 -0
  203. package/dist/router/llm-classifier.js +99 -0
  204. package/dist/router/llm-classifier.js.map +1 -0
  205. package/dist/router/rules.d.ts +12 -0
  206. package/dist/router/rules.d.ts.map +1 -0
  207. package/dist/router/rules.js +202 -0
  208. package/dist/router/rules.js.map +1 -0
  209. package/dist/router/selector.d.ts +58 -0
  210. package/dist/router/selector.d.ts.map +1 -0
  211. package/dist/router/selector.js +165 -0
  212. package/dist/router/selector.js.map +1 -0
  213. package/dist/router/selector.test.d.ts +2 -0
  214. package/dist/router/selector.test.d.ts.map +1 -0
  215. package/dist/router/selector.test.js +67 -0
  216. package/dist/router/selector.test.js.map +1 -0
  217. package/dist/router/strategy.d.ts +19 -0
  218. package/dist/router/strategy.d.ts.map +1 -0
  219. package/dist/router/strategy.js +138 -0
  220. package/dist/router/strategy.js.map +1 -0
  221. package/dist/router/strategy.test.d.ts +2 -0
  222. package/dist/router/strategy.test.d.ts.map +1 -0
  223. package/dist/router/strategy.test.js +131 -0
  224. package/dist/router/strategy.test.js.map +1 -0
  225. package/dist/router/types.d.ts +127 -0
  226. package/dist/router/types.d.ts.map +1 -0
  227. package/dist/router/types.js +11 -0
  228. package/dist/router/types.js.map +1 -0
  229. package/dist/session.d.ts +124 -0
  230. package/dist/session.d.ts.map +1 -0
  231. package/dist/session.js +256 -0
  232. package/dist/session.js.map +1 -0
  233. package/dist/session.test.d.ts +2 -0
  234. package/dist/session.test.d.ts.map +1 -0
  235. package/dist/session.test.js +163 -0
  236. package/dist/session.test.js.map +1 -0
  237. package/dist/solana-balance.d.ts +48 -0
  238. package/dist/solana-balance.d.ts.map +1 -0
  239. package/dist/solana-balance.js +142 -0
  240. package/dist/solana-balance.js.map +1 -0
  241. package/dist/spend-control.d.ts +112 -0
  242. package/dist/spend-control.d.ts.map +1 -0
  243. package/dist/spend-control.js +278 -0
  244. package/dist/spend-control.js.map +1 -0
  245. package/dist/spend-control.test.d.ts +5 -0
  246. package/dist/spend-control.test.d.ts.map +1 -0
  247. package/dist/spend-control.test.js +210 -0
  248. package/dist/spend-control.test.js.map +1 -0
  249. package/dist/stats.d.ts +64 -0
  250. package/dist/stats.d.ts.map +1 -0
  251. package/dist/stats.js +295 -0
  252. package/dist/stats.js.map +1 -0
  253. package/dist/types.d.ts +150 -0
  254. package/dist/types.d.ts.map +1 -0
  255. package/dist/types.js +9 -0
  256. package/dist/types.js.map +1 -0
  257. package/dist/update-hint.test.d.ts +9 -0
  258. package/dist/update-hint.test.d.ts.map +1 -0
  259. package/dist/update-hint.test.js +156 -0
  260. package/dist/update-hint.test.js.map +1 -0
  261. package/dist/updater.d.ts +10 -0
  262. package/dist/updater.d.ts.map +1 -0
  263. package/dist/updater.js +55 -0
  264. package/dist/updater.js.map +1 -0
  265. package/dist/version.d.ts +3 -0
  266. package/dist/version.d.ts.map +1 -0
  267. package/dist/version.js +16 -0
  268. package/dist/version.js.map +1 -0
  269. package/dist/wallet.d.ts +52 -0
  270. package/dist/wallet.d.ts.map +1 -0
  271. package/dist/wallet.js +93 -0
  272. package/dist/wallet.js.map +1 -0
  273. package/dist/wallet.test.d.ts +6 -0
  274. package/dist/wallet.test.d.ts.map +1 -0
  275. package/dist/wallet.test.js +117 -0
  276. package/dist/wallet.test.js.map +1 -0
  277. package/dist/x402-sdk.test.d.ts +6 -0
  278. package/dist/x402-sdk.test.d.ts.map +1 -0
  279. package/dist/x402-sdk.test.js +161 -0
  280. package/dist/x402-sdk.test.js.map +1 -0
  281. package/package.json +2 -2
  282. package/scripts/e2e-multiturn.mjs +4 -1
@@ -0,0 +1,163 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { deriveSessionId, DEFAULT_SESSION_CONFIG, SessionStore, hashRequestContent, } from "./session.js";
3
+ describe("deriveSessionId", () => {
4
+ it("returns same ID for same first user message", () => {
5
+ const messages = [{ role: "user", content: "hello world" }];
6
+ const id1 = deriveSessionId(messages);
7
+ const id2 = deriveSessionId(messages);
8
+ expect(id1).toBe(id2);
9
+ });
10
+ it("returns different IDs for different first user messages", () => {
11
+ const a = deriveSessionId([{ role: "user", content: "first conversation" }]);
12
+ const b = deriveSessionId([{ role: "user", content: "second conversation" }]);
13
+ expect(a).not.toBe(b);
14
+ });
15
+ it("is stable regardless of subsequent messages", () => {
16
+ const firstMsg = { role: "user", content: "what is the capital of France?" };
17
+ const id1 = deriveSessionId([firstMsg]);
18
+ const id2 = deriveSessionId([
19
+ firstMsg,
20
+ { role: "assistant", content: "Paris" },
21
+ { role: "user", content: "and Germany?" },
22
+ ]);
23
+ expect(id1).toBe(id2);
24
+ });
25
+ it("skips system messages and uses first user message", () => {
26
+ const withSystem = [
27
+ { role: "system", content: "you are a helpful assistant" },
28
+ { role: "user", content: "my question" },
29
+ ];
30
+ const withoutSystem = [{ role: "user", content: "my question" }];
31
+ expect(deriveSessionId(withSystem)).toBe(deriveSessionId(withoutSystem));
32
+ });
33
+ it("returns undefined when no user messages exist", () => {
34
+ expect(deriveSessionId([])).toBeUndefined();
35
+ expect(deriveSessionId([{ role: "system", content: "only system" }])).toBeUndefined();
36
+ });
37
+ it("returns a short hex string (8 chars)", () => {
38
+ const id = deriveSessionId([{ role: "user", content: "test" }]);
39
+ expect(id).toMatch(/^[0-9a-f]{8}$/);
40
+ });
41
+ });
42
+ describe("DEFAULT_SESSION_CONFIG", () => {
43
+ it("has session persistence enabled by default", () => {
44
+ expect(DEFAULT_SESSION_CONFIG.enabled).toBe(true);
45
+ });
46
+ });
47
+ describe("SessionStore.setSession", () => {
48
+ it("updates pinned model when fallback model differs from routing decision", () => {
49
+ const store = new SessionStore();
50
+ const sessionId = "abc12345";
51
+ // First call: routing decision pins kimi-k2.5
52
+ store.setSession(sessionId, "moonshot/kimi-k2.5", "MEDIUM");
53
+ expect(store.getSession(sessionId)?.model).toBe("moonshot/kimi-k2.5");
54
+ // Second call: actual model used was fallback (gemini-flash)
55
+ store.setSession(sessionId, "google/gemini-2.5-flash-lite", "MEDIUM");
56
+ expect(store.getSession(sessionId)?.model).toBe("google/gemini-2.5-flash-lite");
57
+ });
58
+ it("subsequent requests use the fallback model when pinned", () => {
59
+ const store = new SessionStore();
60
+ const sessionId = "abc12345";
61
+ store.setSession(sessionId, "moonshot/kimi-k2.5", "MEDIUM");
62
+ store.setSession(sessionId, "google/gemini-2.5-flash-lite", "MEDIUM");
63
+ // Next request reads pinned model — should get the fallback, not the primary
64
+ const pinned = store.getSession(sessionId);
65
+ expect(pinned?.model).toBe("google/gemini-2.5-flash-lite");
66
+ expect(pinned?.requestCount).toBe(2);
67
+ });
68
+ it("initializes three-strike fields on new session", () => {
69
+ const store = new SessionStore();
70
+ store.setSession("s1", "model-a", "SIMPLE");
71
+ const entry = store.getSession("s1");
72
+ expect(entry?.recentHashes).toEqual([]);
73
+ expect(entry?.strikes).toBe(0);
74
+ expect(entry?.escalated).toBe(false);
75
+ });
76
+ });
77
+ describe("SessionStore.recordRequestHash", () => {
78
+ it("triggers escalation after 3 consecutive identical hashes", () => {
79
+ const store = new SessionStore();
80
+ store.setSession("s1", "moonshot/kimi-k2.5", "MEDIUM");
81
+ expect(store.recordRequestHash("s1", "aaa")).toBe(false); // 1st — no prior
82
+ expect(store.recordRequestHash("s1", "aaa")).toBe(false); // 2nd — strikes=1
83
+ expect(store.recordRequestHash("s1", "aaa")).toBe(true); // 3rd — strikes=2, trigger!
84
+ });
85
+ it("resets strikes when a different hash is inserted", () => {
86
+ const store = new SessionStore();
87
+ store.setSession("s1", "moonshot/kimi-k2.5", "MEDIUM");
88
+ store.recordRequestHash("s1", "aaa");
89
+ store.recordRequestHash("s1", "aaa"); // strikes=1
90
+ store.recordRequestHash("s1", "bbb"); // different → strikes=0
91
+ expect(store.recordRequestHash("s1", "bbb")).toBe(false); // strikes=1, not 2 yet
92
+ });
93
+ it("returns false for unknown session", () => {
94
+ const store = new SessionStore();
95
+ expect(store.recordRequestHash("nonexistent", "aaa")).toBe(false);
96
+ });
97
+ });
98
+ describe("SessionStore.escalateSession", () => {
99
+ const tierConfigs = {
100
+ SIMPLE: { primary: "cheap-model", fallback: [] },
101
+ MEDIUM: { primary: "moonshot/kimi-k2.5", fallback: ["google/gemini-2.5-flash-lite"] },
102
+ COMPLEX: { primary: "anthropic/claude-sonnet-4", fallback: ["openai/gpt-4o"] },
103
+ REASONING: { primary: "anthropic/claude-opus-4", fallback: [] },
104
+ };
105
+ it("escalates from MEDIUM to COMPLEX", () => {
106
+ const store = new SessionStore();
107
+ store.setSession("s1", "moonshot/kimi-k2.5", "MEDIUM");
108
+ const result = store.escalateSession("s1", tierConfigs);
109
+ expect(result).toEqual({ model: "anthropic/claude-sonnet-4", tier: "COMPLEX" });
110
+ const entry = store.getSession("s1");
111
+ expect(entry?.model).toBe("anthropic/claude-sonnet-4");
112
+ expect(entry?.tier).toBe("COMPLEX");
113
+ expect(entry?.escalated).toBe(true);
114
+ expect(entry?.strikes).toBe(0);
115
+ });
116
+ it("returns null when already at REASONING (max tier)", () => {
117
+ const store = new SessionStore();
118
+ store.setSession("s1", "anthropic/claude-opus-4", "REASONING");
119
+ const result = store.escalateSession("s1", tierConfigs);
120
+ expect(result).toBeNull();
121
+ });
122
+ it("does not re-trigger after escalation (escalated flag)", () => {
123
+ const store = new SessionStore();
124
+ store.setSession("s1", "moonshot/kimi-k2.5", "MEDIUM");
125
+ // Trigger escalation
126
+ store.recordRequestHash("s1", "aaa");
127
+ store.recordRequestHash("s1", "aaa");
128
+ const shouldEscalate = store.recordRequestHash("s1", "aaa");
129
+ expect(shouldEscalate).toBe(true);
130
+ store.escalateSession("s1", tierConfigs);
131
+ // Now even with more repeated hashes, should NOT trigger again
132
+ store.recordRequestHash("s1", "bbb");
133
+ store.recordRequestHash("s1", "bbb");
134
+ expect(store.recordRequestHash("s1", "bbb")).toBe(false); // escalated=true blocks it
135
+ });
136
+ });
137
+ describe("hashRequestContent", () => {
138
+ it("produces same hash for identical content", () => {
139
+ const h1 = hashRequestContent("hello world");
140
+ const h2 = hashRequestContent("hello world");
141
+ expect(h1).toBe(h2);
142
+ });
143
+ it("produces different hash for different content", () => {
144
+ const h1 = hashRequestContent("hello world");
145
+ const h2 = hashRequestContent("goodbye world");
146
+ expect(h1).not.toBe(h2);
147
+ });
148
+ it("normalizes whitespace differences", () => {
149
+ const h1 = hashRequestContent("hello world\n\tfoo");
150
+ const h2 = hashRequestContent("hello world foo");
151
+ expect(h1).toBe(h2);
152
+ });
153
+ it("includes tool call names in hash", () => {
154
+ const h1 = hashRequestContent("hello", ["read_file", "write_file"]);
155
+ const h2 = hashRequestContent("hello");
156
+ expect(h1).not.toBe(h2);
157
+ });
158
+ it("returns a 12-char hex string", () => {
159
+ const h = hashRequestContent("test content");
160
+ expect(h).toMatch(/^[0-9a-f]{12}$/);
161
+ });
162
+ });
163
+ //# sourceMappingURL=session.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.test.js","sourceRoot":"","sources":["../src/session.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Solana USDC Balance Monitor
3
+ *
4
+ * Checks USDC balance on Solana mainnet with caching.
5
+ * Absorbed from @blockrun/clawwallet's solana-adapter.ts (balance portion only).
6
+ */
7
+ export type SolanaBalanceInfo = {
8
+ balance: bigint;
9
+ balanceUSD: string;
10
+ isLow: boolean;
11
+ isEmpty: boolean;
12
+ walletAddress: string;
13
+ };
14
+ /** Result from checkSufficient() */
15
+ export type SolanaSufficiencyResult = {
16
+ sufficient: boolean;
17
+ info: SolanaBalanceInfo;
18
+ shortfall?: string;
19
+ };
20
+ export declare class SolanaBalanceMonitor {
21
+ private readonly rpc;
22
+ private readonly walletAddress;
23
+ private cachedBalance;
24
+ private cachedAt;
25
+ constructor(walletAddress: string, rpcUrl?: string);
26
+ checkBalance(): Promise<SolanaBalanceInfo>;
27
+ deductEstimated(amountMicros: bigint): void;
28
+ invalidate(): void;
29
+ refresh(): Promise<SolanaBalanceInfo>;
30
+ /**
31
+ * Check if balance is sufficient for an estimated cost.
32
+ */
33
+ checkSufficient(estimatedCostMicros: bigint): Promise<SolanaSufficiencyResult>;
34
+ /**
35
+ * Format USDC amount (in micros) as "$X.XX".
36
+ */
37
+ formatUSDC(amountMicros: bigint): string;
38
+ getWalletAddress(): string;
39
+ /**
40
+ * Check native SOL balance (in lamports). Useful for detecting users who
41
+ * funded with SOL instead of USDC.
42
+ */
43
+ checkSolBalance(): Promise<bigint>;
44
+ private fetchBalance;
45
+ private fetchBalanceOnce;
46
+ private buildInfo;
47
+ }
48
+ //# sourceMappingURL=solana-balance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solana-balance.d.ts","sourceRoot":"","sources":["../src/solana-balance.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,oCAAoC;AACpC,MAAM,MAAM,uBAAuB,GAAG;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqC;IACzD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAK;gBAET,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAM5C,YAAY,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAmBhD,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAM3C,UAAU,IAAI,IAAI;IAKZ,OAAO,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAK3C;;OAEG;IACG,eAAe,CAAC,mBAAmB,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAapF;;OAEG;IACH,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAKxC,gBAAgB,IAAI,MAAM;IAI1B;;;OAGG;IACG,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;YAc1B,YAAY;YAcZ,gBAAgB;IAgC9B,OAAO,CAAC,SAAS;CAUlB"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Solana USDC Balance Monitor
3
+ *
4
+ * Checks USDC balance on Solana mainnet with caching.
5
+ * Absorbed from @blockrun/clawwallet's solana-adapter.ts (balance portion only).
6
+ */
7
+ import { address as solAddress, createSolanaRpc } from "@solana/kit";
8
+ const SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
9
+ const SOLANA_DEFAULT_RPC = "https://api.mainnet-beta.solana.com";
10
+ const BALANCE_TIMEOUT_MS = 10_000;
11
+ const CACHE_TTL_MS = 30_000;
12
+ export class SolanaBalanceMonitor {
13
+ rpc;
14
+ walletAddress;
15
+ cachedBalance = null;
16
+ cachedAt = 0;
17
+ constructor(walletAddress, rpcUrl) {
18
+ this.walletAddress = walletAddress;
19
+ const url = rpcUrl || process["env"].CLAWROUTER_SOLANA_RPC_URL || SOLANA_DEFAULT_RPC;
20
+ this.rpc = createSolanaRpc(url);
21
+ }
22
+ async checkBalance() {
23
+ const now = Date.now();
24
+ if (this.cachedBalance !== null &&
25
+ this.cachedBalance > 0n &&
26
+ now - this.cachedAt < CACHE_TTL_MS) {
27
+ return this.buildInfo(this.cachedBalance);
28
+ }
29
+ // Zero balance is never cached — always re-fetch so a funded wallet is
30
+ // detected on the next request without waiting for cache expiry.
31
+ const balance = await this.fetchBalance();
32
+ if (balance > 0n) {
33
+ this.cachedBalance = balance;
34
+ this.cachedAt = now;
35
+ }
36
+ return this.buildInfo(balance);
37
+ }
38
+ deductEstimated(amountMicros) {
39
+ if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) {
40
+ this.cachedBalance -= amountMicros;
41
+ }
42
+ }
43
+ invalidate() {
44
+ this.cachedBalance = null;
45
+ this.cachedAt = 0;
46
+ }
47
+ async refresh() {
48
+ this.invalidate();
49
+ return this.checkBalance();
50
+ }
51
+ /**
52
+ * Check if balance is sufficient for an estimated cost.
53
+ */
54
+ async checkSufficient(estimatedCostMicros) {
55
+ const info = await this.checkBalance();
56
+ if (info.balance >= estimatedCostMicros) {
57
+ return { sufficient: true, info };
58
+ }
59
+ const shortfall = estimatedCostMicros - info.balance;
60
+ return {
61
+ sufficient: false,
62
+ info,
63
+ shortfall: this.formatUSDC(shortfall),
64
+ };
65
+ }
66
+ /**
67
+ * Format USDC amount (in micros) as "$X.XX".
68
+ */
69
+ formatUSDC(amountMicros) {
70
+ const dollars = Number(amountMicros) / 1_000_000;
71
+ return `$${dollars.toFixed(2)}`;
72
+ }
73
+ getWalletAddress() {
74
+ return this.walletAddress;
75
+ }
76
+ /**
77
+ * Check native SOL balance (in lamports). Useful for detecting users who
78
+ * funded with SOL instead of USDC.
79
+ */
80
+ async checkSolBalance() {
81
+ const controller = new AbortController();
82
+ const timer = setTimeout(() => controller.abort(), BALANCE_TIMEOUT_MS);
83
+ try {
84
+ const owner = solAddress(this.walletAddress);
85
+ const response = await this.rpc.getBalance(owner).send({ abortSignal: controller.signal });
86
+ return BigInt(response.value);
87
+ }
88
+ catch {
89
+ return 0n;
90
+ }
91
+ finally {
92
+ clearTimeout(timer);
93
+ }
94
+ }
95
+ async fetchBalance() {
96
+ const owner = solAddress(this.walletAddress);
97
+ const mint = solAddress(SOLANA_USDC_MINT);
98
+ // The public Solana RPC frequently returns empty token account lists even
99
+ // for funded wallets. Retry once on empty before accepting $0 as truth.
100
+ for (let attempt = 0; attempt < 2; attempt++) {
101
+ const result = await this.fetchBalanceOnce(owner, mint);
102
+ if (result > 0n || attempt === 1)
103
+ return result;
104
+ await new Promise((r) => setTimeout(r, 1_000));
105
+ }
106
+ return 0n;
107
+ }
108
+ async fetchBalanceOnce(owner, mint) {
109
+ const controller = new AbortController();
110
+ const timer = setTimeout(() => controller.abort(), BALANCE_TIMEOUT_MS);
111
+ try {
112
+ const response = await this.rpc
113
+ .getTokenAccountsByOwner(owner, { mint }, { encoding: "jsonParsed" })
114
+ .send({ abortSignal: controller.signal });
115
+ if (response.value.length === 0)
116
+ return 0n;
117
+ let total = 0n;
118
+ for (const account of response.value) {
119
+ const parsed = account.account.data;
120
+ total += BigInt(parsed.parsed.info.tokenAmount.amount);
121
+ }
122
+ return total;
123
+ }
124
+ catch (err) {
125
+ throw new Error(`Failed to fetch Solana USDC balance: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
126
+ }
127
+ finally {
128
+ clearTimeout(timer);
129
+ }
130
+ }
131
+ buildInfo(balance) {
132
+ const dollars = Number(balance) / 1_000_000;
133
+ return {
134
+ balance,
135
+ balanceUSD: `$${dollars.toFixed(2)}`,
136
+ isLow: balance < 1000000n,
137
+ isEmpty: balance < 100n,
138
+ walletAddress: this.walletAddress,
139
+ };
140
+ }
141
+ }
142
+ //# sourceMappingURL=solana-balance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solana-balance.js","sourceRoot":"","sources":["../src/solana-balance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Spend Control - Time-windowed spending limits
3
+ *
4
+ * Absorbed from @blockrun/clawwallet. Chain-agnostic (works for both EVM and Solana).
5
+ *
6
+ * Features:
7
+ * - Per-request limits (e.g., max $0.10 per call)
8
+ * - Hourly limits (e.g., max $3.00 per hour)
9
+ * - Daily limits (e.g., max $20.00 per day)
10
+ * - Session limits (e.g., max $5.00 per session)
11
+ * - Rolling windows (last 1h, last 24h)
12
+ * - Persistent storage (~/.openclaw/blockrun/spending.json)
13
+ */
14
+ export type SpendWindow = "perRequest" | "hourly" | "daily" | "session";
15
+ export interface SpendLimits {
16
+ perRequest?: number;
17
+ hourly?: number;
18
+ daily?: number;
19
+ session?: number;
20
+ }
21
+ export interface SpendRecord {
22
+ timestamp: number;
23
+ amount: number;
24
+ model?: string;
25
+ action?: string;
26
+ }
27
+ export interface SpendingStatus {
28
+ limits: SpendLimits;
29
+ spending: {
30
+ hourly: number;
31
+ daily: number;
32
+ session: number;
33
+ };
34
+ remaining: {
35
+ hourly: number | null;
36
+ daily: number | null;
37
+ session: number | null;
38
+ };
39
+ calls: number;
40
+ }
41
+ export interface CheckResult {
42
+ allowed: boolean;
43
+ blockedBy?: SpendWindow;
44
+ remaining?: number;
45
+ reason?: string;
46
+ resetIn?: number;
47
+ }
48
+ export interface SpendControlStorage {
49
+ load(): {
50
+ limits: SpendLimits;
51
+ history: SpendRecord[];
52
+ } | null;
53
+ save(data: {
54
+ limits: SpendLimits;
55
+ history: SpendRecord[];
56
+ }): void;
57
+ }
58
+ export declare class FileSpendControlStorage implements SpendControlStorage {
59
+ private readonly spendingFile;
60
+ constructor();
61
+ load(): {
62
+ limits: SpendLimits;
63
+ history: SpendRecord[];
64
+ } | null;
65
+ save(data: {
66
+ limits: SpendLimits;
67
+ history: SpendRecord[];
68
+ }): void;
69
+ }
70
+ export declare class InMemorySpendControlStorage implements SpendControlStorage {
71
+ private data;
72
+ load(): {
73
+ limits: SpendLimits;
74
+ history: SpendRecord[];
75
+ } | null;
76
+ save(data: {
77
+ limits: SpendLimits;
78
+ history: SpendRecord[];
79
+ }): void;
80
+ }
81
+ export interface SpendControlOptions {
82
+ storage?: SpendControlStorage;
83
+ now?: () => number;
84
+ }
85
+ export declare class SpendControl {
86
+ private limits;
87
+ private history;
88
+ private sessionSpent;
89
+ private sessionCalls;
90
+ private readonly storage;
91
+ private readonly now;
92
+ constructor(options?: SpendControlOptions);
93
+ setLimit(window: SpendWindow, amount: number): void;
94
+ clearLimit(window: SpendWindow): void;
95
+ getLimits(): SpendLimits;
96
+ check(estimatedCost: number): CheckResult;
97
+ record(amount: number, metadata?: {
98
+ model?: string;
99
+ action?: string;
100
+ }): void;
101
+ private getSpendingInWindow;
102
+ getSpending(window: "hourly" | "daily" | "session"): number;
103
+ getRemaining(window: "hourly" | "daily" | "session"): number | null;
104
+ getStatus(): SpendingStatus;
105
+ getHistory(limit?: number): SpendRecord[];
106
+ resetSession(): void;
107
+ private cleanup;
108
+ private save;
109
+ private load;
110
+ }
111
+ export declare function formatDuration(seconds: number): string;
112
+ //# sourceMappingURL=spend-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spend-control.d.ts","sourceRoot":"","sources":["../src/spend-control.ts"],"names":[],"mappings":"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"}