@oracle-agent/oracle 0.9.8 → 0.11.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.
Files changed (160) hide show
  1. package/README.md +15 -10
  2. package/bin/oracle-data-mcp.mjs +9 -0
  3. package/docs/architecture.md +3 -1
  4. package/docs/profiles.md +3 -6
  5. package/docs/public-surface.md +133 -0
  6. package/package.json +3 -2
  7. package/public/oracle-splash/assets/cli-chain-hyperliquid.jpg +0 -0
  8. package/public/oracle-splash/brand/aave.webp +0 -0
  9. package/public/oracle-splash/brand/aavev2.webp +0 -0
  10. package/public/oracle-splash/brand/aborean.webp +0 -0
  11. package/public/oracle-splash/brand/across.webp +0 -0
  12. package/public/oracle-splash/brand/aerodrome.webp +0 -0
  13. package/public/oracle-splash/brand/afi.webp +0 -0
  14. package/public/oracle-splash/brand/arcus.webp +0 -0
  15. package/public/oracle-splash/brand/aster.webp +0 -0
  16. package/public/oracle-splash/brand/avant.webp +0 -0
  17. package/public/oracle-splash/brand/balancer.webp +0 -0
  18. package/public/oracle-splash/brand/beefy.webp +0 -0
  19. package/public/oracle-splash/brand/benqi.webp +0 -0
  20. package/public/oracle-splash/brand/bestinslot.ico +0 -0
  21. package/public/oracle-splash/brand/bitfi.webp +0 -0
  22. package/public/oracle-splash/brand/bonkswap.webp +0 -0
  23. package/public/oracle-splash/brand/bouncebit.webp +0 -0
  24. package/public/oracle-splash/brand/camelot.webp +0 -0
  25. package/public/oracle-splash/brand/compound.webp +0 -0
  26. package/public/oracle-splash/brand/concrete.webp +0 -0
  27. package/public/oracle-splash/brand/cow.webp +0 -0
  28. package/public/oracle-splash/brand/curve.webp +0 -0
  29. package/public/oracle-splash/brand/derive.webp +0 -0
  30. package/public/oracle-splash/brand/dolomite.webp +0 -0
  31. package/public/oracle-splash/brand/eigenlayer.webp +0 -0
  32. package/public/oracle-splash/brand/ekubo.webp +0 -0
  33. package/public/oracle-splash/brand/ethena.webp +0 -0
  34. package/public/oracle-splash/brand/etherfi.webp +0 -0
  35. package/public/oracle-splash/brand/etherfiborrow.webp +0 -0
  36. package/public/oracle-splash/brand/felix.webp +0 -0
  37. package/public/oracle-splash/brand/fluid.webp +0 -0
  38. package/public/oracle-splash/brand/fluxbeam.webp +0 -0
  39. package/public/oracle-splash/brand/folks.webp +0 -0
  40. package/public/oracle-splash/brand/fortyacres.webp +0 -0
  41. package/public/oracle-splash/brand/gainsnetwork.webp +0 -0
  42. package/public/oracle-splash/brand/gassss.webp +0 -0
  43. package/public/oracle-splash/brand/gmx.webp +0 -0
  44. package/public/oracle-splash/brand/harmonix.webp +0 -0
  45. package/public/oracle-splash/brand/hlorderbook.webp +0 -0
  46. package/public/oracle-splash/brand/hlp.webp +0 -0
  47. package/public/oracle-splash/brand/hop.webp +0 -0
  48. package/public/oracle-splash/brand/hyperbeat.webp +0 -0
  49. package/public/oracle-splash/brand/hyperlend.webp +0 -0
  50. package/public/oracle-splash/brand/hyperliquid.webp +0 -0
  51. package/public/oracle-splash/brand/hyperliquidhpl.webp +0 -0
  52. package/public/oracle-splash/brand/hyperliquidspot.webp +0 -0
  53. package/public/oracle-splash/brand/hyperswap.webp +0 -0
  54. package/public/oracle-splash/brand/hypha.webp +0 -0
  55. package/public/oracle-splash/brand/joedex.webp +0 -0
  56. package/public/oracle-splash/brand/jupiter.webp +0 -0
  57. package/public/oracle-splash/brand/kelp.webp +0 -0
  58. package/public/oracle-splash/brand/kinetiq.webp +0 -0
  59. package/public/oracle-splash/brand/kmhype.webp +0 -0
  60. package/public/oracle-splash/brand/kona.webp +0 -0
  61. package/public/oracle-splash/brand/konalend.webp +0 -0
  62. package/public/oracle-splash/brand/lfj.webp +0 -0
  63. package/public/oracle-splash/brand/lido.webp +0 -0
  64. package/public/oracle-splash/brand/lifi.webp +0 -0
  65. package/public/oracle-splash/brand/lighter.webp +0 -0
  66. package/public/oracle-splash/brand/lista.webp +0 -0
  67. package/public/oracle-splash/brand/loopedhype.webp +0 -0
  68. package/public/oracle-splash/brand/lorenzo.webp +0 -0
  69. package/public/oracle-splash/brand/magic-eden.webp +0 -0
  70. package/public/oracle-splash/brand/magiceden.webp +0 -0
  71. package/public/oracle-splash/brand/mempool.png +0 -0
  72. package/public/oracle-splash/brand/meteora.webp +0 -0
  73. package/public/oracle-splash/brand/morpho.webp +0 -0
  74. package/public/oracle-splash/brand/myriad.webp +0 -0
  75. package/public/oracle-splash/brand/noxa.webp +0 -0
  76. package/public/oracle-splash/brand/odos.webp +0 -0
  77. package/public/oracle-splash/brand/oneinch.webp +0 -0
  78. package/public/oracle-splash/brand/openbook.webp +0 -0
  79. package/public/oracle-splash/brand/opensea.webp +0 -0
  80. package/public/oracle-splash/brand/orca.webp +0 -0
  81. package/public/oracle-splash/brand/ordinals.png +0 -0
  82. package/public/oracle-splash/brand/ostium.webp +0 -0
  83. package/public/oracle-splash/brand/pancakeswap.webp +0 -0
  84. package/public/oracle-splash/brand/pendle.webp +0 -0
  85. package/public/oracle-splash/brand/perena.webp +0 -0
  86. package/public/oracle-splash/brand/pharaoh.webp +0 -0
  87. package/public/oracle-splash/brand/phoenix.webp +0 -0
  88. package/public/oracle-splash/brand/polymarket.webp +0 -0
  89. package/public/oracle-splash/brand/polynomial.webp +0 -0
  90. package/public/oracle-splash/brand/projectx.webp +0 -0
  91. package/public/oracle-splash/brand/pumpfun.webp +0 -0
  92. package/public/oracle-splash/brand/quickswap.webp +0 -0
  93. package/public/oracle-splash/brand/rain.webp +0 -0
  94. package/public/oracle-splash/brand/raydium.webp +0 -0
  95. package/public/oracle-splash/brand/relay.webp +0 -0
  96. package/public/oracle-splash/brand/rocketpool.webp +0 -0
  97. package/public/oracle-splash/brand/saber.webp +0 -0
  98. package/public/oracle-splash/brand/sakuraswap.webp +0 -0
  99. package/public/oracle-splash/brand/sanctum.webp +0 -0
  100. package/public/oracle-splash/brand/satflow.png +0 -0
  101. package/public/oracle-splash/brand/sierra.webp +0 -0
  102. package/public/oracle-splash/brand/sky.webp +0 -0
  103. package/public/oracle-splash/brand/solv.webp +0 -0
  104. package/public/oracle-splash/brand/spark.webp +0 -0
  105. package/public/oracle-splash/brand/sparksavings.webp +0 -0
  106. package/public/oracle-splash/brand/sphere.webp +0 -0
  107. package/public/oracle-splash/brand/ssv.webp +0 -0
  108. package/public/oracle-splash/brand/stabble.webp +0 -0
  109. package/public/oracle-splash/brand/stableswap.webp +0 -0
  110. package/public/oracle-splash/brand/stakewise.webp +0 -0
  111. package/public/oracle-splash/brand/stargate.webp +0 -0
  112. package/public/oracle-splash/brand/sthype.webp +0 -0
  113. package/public/oracle-splash/brand/sushiswap.webp +0 -0
  114. package/public/oracle-splash/brand/unisat.png +0 -0
  115. package/public/oracle-splash/brand/uniswap.webp +0 -0
  116. package/public/oracle-splash/brand/uniswapv2.webp +0 -0
  117. package/public/oracle-splash/brand/uniswapv4.webp +0 -0
  118. package/public/oracle-splash/brand/unit.webp +0 -0
  119. package/public/oracle-splash/brand/velodrome.webp +0 -0
  120. package/public/oracle-splash/brand/velora.webp +0 -0
  121. package/public/oracle-splash/brand/venus.webp +0 -0
  122. package/public/oracle-splash/brand/wbeth.webp +0 -0
  123. package/public/oracle-splash/brand/xsy.webp +0 -0
  124. package/public/oracle-splash/brand/yieldyak.webp +0 -0
  125. package/public/oracle-splash/brand/zerobase.webp +0 -0
  126. package/public/oracle-splash/brand/zerox.webp +0 -0
  127. package/public/oracle-splash/index.html +337 -197
  128. package/scripts/check-doc-drift.mjs +11 -0
  129. package/scripts/e2e-solana-bitcoin.mjs +1 -1
  130. package/scripts/secret-scan.mjs +1 -1
  131. package/src/action-receipts.mjs +108 -11
  132. package/src/auth/oauth.mjs +672 -0
  133. package/src/cli/commands/auth.mjs +193 -0
  134. package/src/cli/commands/bootstrap.mjs +2 -2
  135. package/src/cli/commands/chat.mjs +97 -75
  136. package/src/cli/commands/data.mjs +3 -1
  137. package/src/cli/commands/doctor.mjs +11 -1
  138. package/src/cli/commands/model.mjs +79 -23
  139. package/src/cli/kernel.mjs +4 -3
  140. package/src/cli/model-config.mjs +70 -0
  141. package/src/data/catalog.mjs +38 -7
  142. package/src/data/desk-data.mjs +12 -0
  143. package/src/data/providers/evm-rpc.mjs +19 -16
  144. package/src/data/providers/jupiter-venues.mjs +119 -0
  145. package/src/data/providers/jupiter.mjs +132 -0
  146. package/src/data/providers/magiceden-sol.mjs +13 -0
  147. package/src/data/providers/morpho.mjs +6 -2
  148. package/src/data/providers/pendle.mjs +3 -2
  149. package/src/data/providers/satflow.mjs +4 -1
  150. package/src/data/providers/signed-material-guard.mjs +108 -0
  151. package/src/data/providers/uniswap-v3.mjs +34 -5
  152. package/src/data/providers/uniswap-v4.mjs +414 -0
  153. package/src/prepare-envelope.mjs +22 -4
  154. package/src/router/prepare-route.mjs +17 -0
  155. package/src/router/route-sources.mjs +35 -0
  156. package/src/tui/app.mjs +18 -7
  157. package/src/tui/backend.mjs +145 -0
  158. package/src/tui/standalone-client.mjs +666 -0
  159. package/src/cards.mjs +0 -369
  160. package/src/cli/__pycache__/oracle-harness.cpython-311.pyc +0 -0
@@ -21,6 +21,7 @@ import { zeroxQuote } from "../data/providers/zerox.mjs";
21
21
  import { oneinchQuote } from "../data/providers/oneinch.mjs";
22
22
  import { cowQuote } from "../data/providers/cowswap.mjs";
23
23
  import { uniV3QuoteExactIn, UNI_V3_CHAINS } from "../data/providers/uniswap-v3.mjs";
24
+ import { uniV4QuoteExactIn, UNI_V4_CHAINS } from "../data/providers/uniswap-v4.mjs";
24
25
  import { llamaPrices } from "../data/providers/defillama.mjs";
25
26
  import { NATIVE_PRICE_KEY } from "./best-execution.mjs";
26
27
 
@@ -67,6 +68,7 @@ export function swapCandidates({
67
68
  tokenOut,
68
69
  amountIn,
69
70
  taker,
71
+ slippageBps,
70
72
  decimalsIn,
71
73
  decimalsOut,
72
74
  nativePriceUsd = null,
@@ -225,6 +227,39 @@ export function swapCandidates({
225
227
  });
226
228
  }
227
229
 
230
+ // Uniswap V4 Quoter. Cross-chain floor alongside V3. On RH the live SQUEEZE book
231
+ // is V4; V3 alone can return a drained shell that looks like a real quote. Kept
232
+ // after V3 so best-execution still compares net output when both answer.
233
+ if (UNI_V4_CHAINS[Number(chainId)]) {
234
+ c.push({
235
+ source: "uniswap-v4",
236
+ run: async () => {
237
+ const q = await uniV4QuoteExactIn(
238
+ { chainId, tokenIn, tokenOut, amountIn, recipient: taker, slippageBps },
239
+ opts,
240
+ );
241
+ return {
242
+ amountOut: q?.amountOut,
243
+ minOut: q?.minOut ?? q?.amountOutMinimum ?? null,
244
+ gasUsd: null,
245
+ meta: {
246
+ onchain: true,
247
+ venue: "uniswap-v4",
248
+ fee: q?.fee,
249
+ tickSpacing: q?.tickSpacing,
250
+ poolId: q?.poolId,
251
+ hooks: q?.hooks,
252
+ gasUnits: q?.gasEstimate,
253
+ quoter: q?.quoter,
254
+ poolManager: q?.poolManager,
255
+ prepareReady: q?.prepareReady === true,
256
+ executionReady: false,
257
+ },
258
+ };
259
+ },
260
+ });
261
+ }
262
+
228
263
  return c;
229
264
  }
230
265
 
package/src/tui/app.mjs CHANGED
@@ -371,23 +371,34 @@ export function createOracleTui(options = {}) {
371
371
  });
372
372
  }
373
373
 
374
- export async function runOracleTui({ hermesPython, args = [], env = process.env, cwd = process.cwd(), stdout = process.stdout, stdin = process.stdin } = {}) {
374
+ export async function runOracleTui({
375
+ hermesPython,
376
+ client,
377
+ tuiFactory = createOracleTui,
378
+ args = [],
379
+ env = process.env,
380
+ cwd = process.cwd(),
381
+ stdout = process.stdout,
382
+ stdin = process.stdin,
383
+ } = {}) {
375
384
  const parsed = parseChatArgs(args);
376
385
  if (parsed.query) return { native: false, pass: parsed.pass };
377
386
  if (!stdin.isTTY || !stdout.isTTY) return { native: false, pass: parsed.pass };
378
- // Native TUI is intentionally opt-in while the stable oracle shim remains pinned.
379
- if (env.ORACLE_NATIVE_TUI !== "1") return { native: false, pass: parsed.pass };
387
+ if (env.ORACLE_NATIVE_TUI === "0") return { native: false, pass: parsed.pass };
380
388
  const invocation = hermesPython;
381
- if (!invocation) return { native: false, pass: parsed.pass };
389
+ if (!client && !invocation) return { native: false, pass: parsed.pass };
382
390
  const cleanEnv = activeChainEnv({
383
391
  ...env,
384
392
  ORACLE_CHAT_SURFACE: "1",
385
393
  ORACLE_PROFILE: "oracle",
386
394
  ORACLE_NODE_BIN: process.execPath,
387
395
  });
388
- const tui = createOracleTui({
389
- python: invocation.command,
390
- pythonArgs: invocation.prefix || [],
396
+ const tui = tuiFactory({
397
+ ...(client ? { client } : {}),
398
+ ...(invocation ? {
399
+ python: invocation.command,
400
+ pythonArgs: invocation.prefix || [],
401
+ } : {}),
391
402
  cwd,
392
403
  env: cleanEnv,
393
404
  stdin,
@@ -0,0 +1,145 @@
1
+ const PROVIDERS = Object.freeze({
2
+ openrouter: {
3
+ key: "OPENROUTER_API_KEY",
4
+ baseUrl: "https://openrouter.ai/api/v1",
5
+ model: "openrouter/auto",
6
+ },
7
+ openai: {
8
+ key: "OPENAI_API_KEY",
9
+ baseUrl: "https://api.openai.com/v1",
10
+ model: "gpt-4.1-mini",
11
+ },
12
+ xai: {
13
+ key: "XAI_API_KEY",
14
+ baseUrl: "https://api.x.ai/v1",
15
+ model: "grok-4-latest",
16
+ },
17
+ deepseek: {
18
+ key: "DEEPSEEK_API_KEY",
19
+ baseUrl: "https://api.deepseek.com/v1",
20
+ model: "deepseek-chat",
21
+ },
22
+ gemini: {
23
+ key: "GEMINI_API_KEY",
24
+ alternateKey: "GOOGLE_API_KEY",
25
+ baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai",
26
+ model: "gemini-2.5-flash",
27
+ },
28
+ });
29
+
30
+ const OAUTH_PROVIDERS = Object.freeze({
31
+ "anthropic-oauth": {
32
+ baseUrl: "https://api.anthropic.com/v1",
33
+ model: "claude-sonnet-4-6",
34
+ protocol: "anthropic-messages",
35
+ },
36
+ "openai-codex": {
37
+ baseUrl: "https://chatgpt.com/backend-api/codex",
38
+ model: "gpt-5.6-sol",
39
+ protocol: "responses",
40
+ },
41
+ "xai-oauth": {
42
+ baseUrl: "https://api.x.ai/v1",
43
+ model: "grok-4.5",
44
+ protocol: "responses",
45
+ },
46
+ });
47
+
48
+ function positiveInteger(value, fallback) {
49
+ const parsed = Number.parseInt(String(value || ""), 10);
50
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
51
+ }
52
+
53
+ function providerFromEnv(env, stored = {}) {
54
+ const explicit = String(env.ORACLE_PROVIDER || stored.provider || "").trim().toLowerCase();
55
+ if (explicit) return explicit;
56
+ if (env.ORACLE_BASE_URL) return "custom";
57
+ for (const [name, preset] of Object.entries(PROVIDERS)) {
58
+ if (env[preset.key] || (preset.alternateKey && env[preset.alternateKey])) return name;
59
+ }
60
+ return null;
61
+ }
62
+
63
+ export function standaloneConfigFromEnv(
64
+ env = process.env,
65
+ stored = {},
66
+ oauthAvailable = () => false,
67
+ apiKeyResolver = () => "",
68
+ ) {
69
+ const provider = providerFromEnv(env, stored);
70
+ if (!provider) return null;
71
+
72
+ const oauthPreset = OAUTH_PROVIDERS[provider];
73
+ const preset = PROVIDERS[provider] || oauthPreset;
74
+ const apiKeyEnv = String(env.ORACLE_API_KEY_ENV || stored.apiKeyEnv || "");
75
+ const apiKey = String(
76
+ env.ORACLE_API_KEY ||
77
+ (apiKeyEnv && env[apiKeyEnv]) ||
78
+ (preset && (env[preset.key] || (preset.alternateKey && env[preset.alternateKey]))) ||
79
+ apiKeyResolver(provider) ||
80
+ "",
81
+ );
82
+ const baseUrl = String(
83
+ oauthPreset
84
+ ? oauthPreset.baseUrl
85
+ : provider === "custom"
86
+ ? (env.ORACLE_BASE_URL || stored.baseUrl || "")
87
+ : (preset?.baseUrl || ""),
88
+ ).replace(/\/+$/, "");
89
+ const model = String(env.ORACLE_MODEL || stored.model || preset?.model || "");
90
+ if (!baseUrl || !model) return null;
91
+ const hasOAuth = Boolean(oauthPreset && oauthAvailable(provider));
92
+ if (oauthPreset && !hasOAuth) return null;
93
+ if (!oauthPreset && provider !== "custom" && !apiKey) return null;
94
+
95
+ return {
96
+ kind: "standalone",
97
+ provider,
98
+ model,
99
+ baseUrl,
100
+ ...(hasOAuth ? { authType: "oauth", protocol: oauthPreset.protocol } : { apiKey }),
101
+ contextLength: positiveInteger(env.ORACLE_CONTEXT_LENGTH || stored.contextLength, 128000),
102
+ reasoningEffort: String(env.ORACLE_REASONING_EFFORT || stored.reasoningEffort || "high"),
103
+ };
104
+ }
105
+
106
+ function setupReason(extra = "") {
107
+ return [
108
+ "Oracle standalone chat does not require Hermes.",
109
+ "Set OPENROUTER_API_KEY, run `oracle auth login claude|codex|grok`, or configure a custom OpenAI-compatible endpoint.",
110
+ "If Hermes is installed, Oracle will reuse it automatically.",
111
+ extra,
112
+ ].filter(Boolean).join("\n");
113
+ }
114
+
115
+ export function resolveChatBackend({
116
+ env = process.env,
117
+ hermes = { ok: false },
118
+ storedConfig = null,
119
+ oauthAvailable = () => false,
120
+ apiKeyResolver = () => "",
121
+ } = {}) {
122
+ const requested = String(env.ORACLE_CHAT_BACKEND || storedConfig?.backend || "auto").trim().toLowerCase();
123
+ const standalone = standaloneConfigFromEnv(env, storedConfig || {}, oauthAvailable, apiKeyResolver);
124
+
125
+ if (requested === "standalone") {
126
+ return standalone
127
+ ? { kind: "standalone", config: standalone }
128
+ : { kind: "unconfigured", reason: setupReason("ORACLE_CHAT_BACKEND=standalone was requested, but no standalone model is configured.") };
129
+ }
130
+
131
+ if (requested === "hermes") {
132
+ return hermes?.ok
133
+ ? { kind: "hermes", bin: hermes.bin }
134
+ : { kind: "unconfigured", reason: setupReason("ORACLE_CHAT_BACKEND=hermes was requested, but Hermes was not found.") };
135
+ }
136
+
137
+ if (requested !== "auto") {
138
+ return { kind: "unconfigured", reason: setupReason(`Unknown ORACLE_CHAT_BACKEND value: ${requested}`) };
139
+ }
140
+ if (hermes?.ok) return { kind: "hermes", bin: hermes.bin };
141
+ if (standalone) return { kind: "standalone", config: standalone };
142
+ return { kind: "unconfigured", reason: setupReason() };
143
+ }
144
+
145
+ export default { resolveChatBackend, standaloneConfigFromEnv };