@oracle-agent/oracle 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. package/README.md +2 -2
  2. package/SETUP.md +4 -4
  3. package/dist/action-receipts.mjs +1 -0
  4. package/dist/action-semantics.mjs +1 -0
  5. package/dist/address-book.mjs +2 -0
  6. package/dist/bin/desk-server.mjs +59 -0
  7. package/dist/bin/oracle-data-mcp.mjs +8 -0
  8. package/dist/bin/oracle-equities.mjs +20 -0
  9. package/dist/bin/oracle-init.mjs +24 -0
  10. package/dist/bin/oracle-public-server.mjs +5 -0
  11. package/dist/bin/oracle-route.mjs +22 -0
  12. package/dist/bin/oracle-scan.mjs +18 -0
  13. package/dist/bin/oracle-upgrade.mjs +21 -0
  14. package/dist/bin/oracle.mjs +61 -0
  15. package/dist/chains.mjs +1 -0
  16. package/dist/cli/commands/auth.mjs +16 -0
  17. package/dist/cli/commands/bootstrap.mjs +18 -0
  18. package/dist/cli/commands/chain.mjs +21 -0
  19. package/dist/cli/commands/chat.mjs +45 -0
  20. package/dist/cli/commands/credential.mjs +1 -0
  21. package/dist/cli/commands/data-mcp.mjs +2 -0
  22. package/dist/cli/commands/data.mjs +12 -0
  23. package/dist/cli/commands/doctor.mjs +5 -0
  24. package/dist/cli/commands/equities.mjs +15 -0
  25. package/dist/cli/commands/farm.mjs +59 -0
  26. package/dist/cli/commands/fees.mjs +10 -0
  27. package/dist/cli/commands/follow.mjs +28 -0
  28. package/dist/cli/commands/harness.mjs +15 -0
  29. package/dist/cli/commands/help.mjs +1 -0
  30. package/dist/cli/commands/init.mjs +3 -0
  31. package/dist/cli/commands/mcp.mjs +36 -0
  32. package/dist/cli/commands/model.mjs +54 -0
  33. package/dist/cli/commands/plugins.mjs +43 -0
  34. package/dist/cli/commands/prepare.mjs +2 -0
  35. package/dist/cli/commands/public.mjs +3 -0
  36. package/dist/cli/commands/resolve.mjs +23 -0
  37. package/dist/cli/commands/route.mjs +2 -0
  38. package/dist/cli/commands/runner.mjs +1 -0
  39. package/dist/cli/commands/scan.mjs +2 -0
  40. package/dist/cli/commands/setup.mjs +34 -0
  41. package/dist/cli/commands/sign.mjs +4 -0
  42. package/dist/cli/commands/signer.mjs +1 -0
  43. package/dist/cli/commands/swap.mjs +36 -0
  44. package/dist/cli/commands/upgrade.mjs +2 -0
  45. package/dist/cli/commands/vault.mjs +1 -0
  46. package/dist/cli/commands/version.mjs +2 -0
  47. package/dist/data/desk-data.mjs +55 -0
  48. package/dist/data/names.mjs +2 -0
  49. package/dist/data/providers/farming.mjs +2 -0
  50. package/dist/equities/index.mjs +1 -0
  51. package/dist/index.mjs +60 -0
  52. package/dist/nft-gas-war-guard.mjs +1 -0
  53. package/dist/onboarding/harness-configs.mjs +4 -0
  54. package/dist/portfolio-risk.mjs +1 -0
  55. package/dist/prepare-envelope.mjs +1 -0
  56. package/dist/public-api/agent-grants.mjs +3 -0
  57. package/dist/public-control/policy-schema.mjs +1 -0
  58. package/dist/router/index.mjs +2 -0
  59. package/dist/scanner/index.mjs +4 -0
  60. package/dist/signals/index.mjs +1 -0
  61. package/dist/watch-preferences.mjs +1 -0
  62. package/package.json +32 -78
  63. package/public/oracle-splash/_variants/ice.html +5 -18
  64. package/public/oracle-splash/_variants/ivory.html +5 -18
  65. package/public/oracle-splash/index.html +114 -34
  66. package/CONTRIBUTING.md +0 -99
  67. package/bin/desk-server.mjs +0 -473
  68. package/bin/oracle-data-mcp.mjs +0 -1252
  69. package/bin/oracle-equities.mjs +0 -168
  70. package/bin/oracle-init.mjs +0 -525
  71. package/bin/oracle-public-server.mjs +0 -36
  72. package/bin/oracle-route.mjs +0 -254
  73. package/bin/oracle-scan.mjs +0 -192
  74. package/bin/oracle-upgrade.mjs +0 -42
  75. package/bin/oracle.mjs +0 -34
  76. package/docs/adding-a-chain.md +0 -229
  77. package/docs/architecture.md +0 -139
  78. package/docs/buzz-integration.md +0 -115
  79. package/docs/cli.md +0 -143
  80. package/docs/connectors.md +0 -13
  81. package/docs/hyperliquid-builder-code.md +0 -50
  82. package/docs/locals-only-fee-waiver.md +0 -34
  83. package/docs/oracle-pack-standard.md +0 -29
  84. package/docs/profiles.md +0 -151
  85. package/docs/public-surface.md +0 -206
  86. package/examples/add-a-chain.mjs +0 -65
  87. package/examples/oracle-pack-template.mjs +0 -38
  88. package/examples/research-a-token.mjs +0 -70
  89. package/plugins/oracle-owner-gate/__init__.py +0 -288
  90. package/plugins/oracle-owner-gate/plugin.yaml +0 -9
  91. package/protocols/templates/safe-erc20/SECURITY.md +0 -16
  92. package/protocols/templates/safe-erc20/foundry.toml +0 -12
  93. package/protocols/templates/safe-erc20/remappings.txt +0 -2
  94. package/protocols/templates/safe-erc20/src/SafeERC20.sol +0 -53
  95. package/protocols/templates/safe-erc20/test/SafeERC20.t.sol +0 -72
  96. package/scripts/adversarial-bench.mjs +0 -114
  97. package/scripts/build-dist.mjs +0 -119
  98. package/scripts/build-inscription.py +0 -230
  99. package/scripts/check-doc-drift.mjs +0 -123
  100. package/scripts/check-test-count.mjs +0 -105
  101. package/scripts/e2e-hl-markets.mjs +0 -21
  102. package/scripts/e2e-hl-perps.mjs +0 -48
  103. package/scripts/e2e-hypercore-staking.mjs +0 -128
  104. package/scripts/e2e-solana-bitcoin.mjs +0 -183
  105. package/scripts/guard-publish.mjs +0 -22
  106. package/scripts/protocol-template-gate.mjs +0 -15
  107. package/scripts/public-api-scan.mjs +0 -23
  108. package/scripts/publish-dist.mjs +0 -67
  109. package/scripts/record-hero-cli.sh +0 -80
  110. package/scripts/secret-scan.mjs +0 -168
  111. package/scripts/smoke-dist-bins.mjs +0 -89
  112. package/scripts/verify-dist.mjs +0 -29
  113. package/scripts/verify-v3-venues.mjs +0 -233
  114. package/skins/oracle.yaml +0 -56
  115. package/src/action-receipts.mjs +0 -265
  116. package/src/action-semantics.mjs +0 -62
  117. package/src/address-book.mjs +0 -208
  118. package/src/agent-auth.mjs +0 -206
  119. package/src/approval-guard.mjs +0 -282
  120. package/src/attestation-secret.mjs +0 -94
  121. package/src/audit-log.mjs +0 -196
  122. package/src/auth/oauth.mjs +0 -672
  123. package/src/auto-slippage.mjs +0 -378
  124. package/src/capability-posture.mjs +0 -125
  125. package/src/cards.mjs +0 -422
  126. package/src/chains.mjs +0 -62
  127. package/src/cli/chain-catalog.mjs +0 -215
  128. package/src/cli/chain-state.mjs +0 -74
  129. package/src/cli/commands/auth.mjs +0 -193
  130. package/src/cli/commands/bootstrap.mjs +0 -87
  131. package/src/cli/commands/chain.mjs +0 -143
  132. package/src/cli/commands/chat.mjs +0 -465
  133. package/src/cli/commands/credential.mjs +0 -9
  134. package/src/cli/commands/data-mcp.mjs +0 -18
  135. package/src/cli/commands/data.mjs +0 -75
  136. package/src/cli/commands/doctor.mjs +0 -136
  137. package/src/cli/commands/equities.mjs +0 -41
  138. package/src/cli/commands/farm.mjs +0 -129
  139. package/src/cli/commands/fees.mjs +0 -109
  140. package/src/cli/commands/follow.mjs +0 -130
  141. package/src/cli/commands/harness.mjs +0 -63
  142. package/src/cli/commands/help.mjs +0 -7
  143. package/src/cli/commands/init.mjs +0 -27
  144. package/src/cli/commands/mcp.mjs +0 -154
  145. package/src/cli/commands/model.mjs +0 -106
  146. package/src/cli/commands/plugins.mjs +0 -241
  147. package/src/cli/commands/prepare.mjs +0 -12
  148. package/src/cli/commands/public.mjs +0 -20
  149. package/src/cli/commands/resolve.mjs +0 -64
  150. package/src/cli/commands/route.mjs +0 -12
  151. package/src/cli/commands/runner.mjs +0 -9
  152. package/src/cli/commands/scan.mjs +0 -12
  153. package/src/cli/commands/setup.mjs +0 -294
  154. package/src/cli/commands/sign.mjs +0 -21
  155. package/src/cli/commands/signer.mjs +0 -9
  156. package/src/cli/commands/swap.mjs +0 -102
  157. package/src/cli/commands/upgrade.mjs +0 -12
  158. package/src/cli/commands/vault.mjs +0 -9
  159. package/src/cli/commands/version.mjs +0 -22
  160. package/src/cli/first-run.mjs +0 -23
  161. package/src/cli/kernel.mjs +0 -262
  162. package/src/cli/mcp-targets/chatgpt.mjs +0 -51
  163. package/src/cli/mcp-targets/claude-code.mjs +0 -39
  164. package/src/cli/mcp-targets/claude-desktop.mjs +0 -24
  165. package/src/cli/mcp-targets/codex.mjs +0 -41
  166. package/src/cli/mcp-targets/cursor.mjs +0 -32
  167. package/src/cli/mcp-targets/shared.mjs +0 -71
  168. package/src/cli/mcp-targets/vscode.mjs +0 -39
  169. package/src/cli/messaging-platforms.mjs +0 -217
  170. package/src/cli/model-config.mjs +0 -78
  171. package/src/cli/operator-dispatch.mjs +0 -257
  172. package/src/cli/oracle-harness.py +0 -415
  173. package/src/cli/paths.mjs +0 -107
  174. package/src/cli/runtime.mjs +0 -351
  175. package/src/cli/setup-state.mjs +0 -168
  176. package/src/cli/spawn-child.mjs +0 -43
  177. package/src/data/catalog.mjs +0 -858
  178. package/src/data/desk-data.mjs +0 -877
  179. package/src/data/http.mjs +0 -253
  180. package/src/data/names.mjs +0 -188
  181. package/src/data/provider-endpoint.mjs +0 -94
  182. package/src/data/providers/aave-v3.mjs +0 -139
  183. package/src/data/providers/across-bridge.mjs +0 -41
  184. package/src/data/providers/aerodrome.mjs +0 -245
  185. package/src/data/providers/aevo.mjs +0 -33
  186. package/src/data/providers/approval-indexer.mjs +0 -133
  187. package/src/data/providers/approvals.mjs +0 -594
  188. package/src/data/providers/babylon-staking.mjs +0 -149
  189. package/src/data/providers/balancer.mjs +0 -209
  190. package/src/data/providers/beefy-yields.mjs +0 -45
  191. package/src/data/providers/birdeye-tokens.mjs +0 -33
  192. package/src/data/providers/bisq-markets.mjs +0 -145
  193. package/src/data/providers/bitcoin-esplora.mjs +0 -216
  194. package/src/data/providers/bitcoin-meta.mjs +0 -378
  195. package/src/data/providers/blockscout.mjs +0 -14
  196. package/src/data/providers/bob-rootstock.mjs +0 -198
  197. package/src/data/providers/braiins-insights.mjs +0 -103
  198. package/src/data/providers/bridge-aggregator.mjs +0 -259
  199. package/src/data/providers/bridges.mjs +0 -242
  200. package/src/data/providers/bungee.mjs +0 -29
  201. package/src/data/providers/celestia-da.mjs +0 -38
  202. package/src/data/providers/changenow.mjs +0 -135
  203. package/src/data/providers/compound.mjs +0 -32
  204. package/src/data/providers/connext.mjs +0 -28
  205. package/src/data/providers/convex.mjs +0 -30
  206. package/src/data/providers/cow-protocol.mjs +0 -110
  207. package/src/data/providers/cowswap.mjs +0 -415
  208. package/src/data/providers/curve.mjs +0 -201
  209. package/src/data/providers/das-assets.mjs +0 -59
  210. package/src/data/providers/debridge.mjs +0 -109
  211. package/src/data/providers/defillama.mjs +0 -88
  212. package/src/data/providers/dexscreener.mjs +0 -43
  213. package/src/data/providers/drift.mjs +0 -32
  214. package/src/data/providers/dydx.mjs +0 -41
  215. package/src/data/providers/evm-rpc.mjs +0 -225
  216. package/src/data/providers/farming.mjs +0 -291
  217. package/src/data/providers/geckoterminal.mjs +0 -34
  218. package/src/data/providers/gmx.mjs +0 -496
  219. package/src/data/providers/hiro-stacks.mjs +0 -150
  220. package/src/data/providers/hl-assets.mjs +0 -165
  221. package/src/data/providers/hl-info.mjs +0 -108
  222. package/src/data/providers/hl-markets.mjs +0 -210
  223. package/src/data/providers/hl-outcome.mjs +0 -61
  224. package/src/data/providers/hl-perps.mjs +0 -419
  225. package/src/data/providers/hl-staking.mjs +0 -353
  226. package/src/data/providers/hl-ws.mjs +0 -119
  227. package/src/data/providers/hyperevm-dex.mjs +0 -49
  228. package/src/data/providers/jito-mev.mjs +0 -117
  229. package/src/data/providers/jupiter-venues.mjs +0 -119
  230. package/src/data/providers/jupiter.mjs +0 -315
  231. package/src/data/providers/kamino-strategies.mjs +0 -143
  232. package/src/data/providers/l2beat-tvl.mjs +0 -42
  233. package/src/data/providers/lifi.mjs +0 -166
  234. package/src/data/providers/liquid-esplora.mjs +0 -152
  235. package/src/data/providers/magiceden-ordinals.mjs +0 -141
  236. package/src/data/providers/magiceden-sol.mjs +0 -406
  237. package/src/data/providers/mempool-lightning.mjs +0 -144
  238. package/src/data/providers/mempool-mining.mjs +0 -142
  239. package/src/data/providers/meteora-dlmm.mjs +0 -168
  240. package/src/data/providers/morpho.mjs +0 -177
  241. package/src/data/providers/nft-gallery.mjs +0 -163
  242. package/src/data/providers/nft-portfolio.mjs +0 -494
  243. package/src/data/providers/odos.mjs +0 -156
  244. package/src/data/providers/ondo.mjs +0 -31
  245. package/src/data/providers/oneinch.mjs +0 -174
  246. package/src/data/providers/opensea-multichain.mjs +0 -136
  247. package/src/data/providers/opensea-nft.mjs +0 -371
  248. package/src/data/providers/orca.mjs +0 -29
  249. package/src/data/providers/ordinals-runes.mjs +0 -180
  250. package/src/data/providers/pancakeswap.mjs +0 -28
  251. package/src/data/providers/paraswap.mjs +0 -125
  252. package/src/data/providers/pendle.mjs +0 -188
  253. package/src/data/providers/poly-clob.mjs +0 -283
  254. package/src/data/providers/poly-public.mjs +0 -96
  255. package/src/data/providers/poly-ws.mjs +0 -103
  256. package/src/data/providers/polygon-staking.mjs +0 -227
  257. package/src/data/providers/portfolio-history.mjs +0 -394
  258. package/src/data/providers/portfolio.mjs +0 -600
  259. package/src/data/providers/pyth-price-feeds.mjs +0 -172
  260. package/src/data/providers/raydium.mjs +0 -35
  261. package/src/data/providers/relayscan-mev.mjs +0 -72
  262. package/src/data/providers/rfq.mjs +0 -15
  263. package/src/data/providers/rh-agent.mjs +0 -59
  264. package/src/data/providers/sanctum-lst.mjs +0 -180
  265. package/src/data/providers/satflow.mjs +0 -341
  266. package/src/data/providers/signed-material-guard.mjs +0 -108
  267. package/src/data/providers/solana-rpc.mjs +0 -197
  268. package/src/data/providers/solend-lending.mjs +0 -166
  269. package/src/data/providers/spark-lend.mjs +0 -42
  270. package/src/data/providers/squid-router.mjs +0 -29
  271. package/src/data/providers/stargate.mjs +0 -29
  272. package/src/data/providers/sushiswap.mjs +0 -30
  273. package/src/data/providers/symbiotic-restaking.mjs +0 -41
  274. package/src/data/providers/thorchain.mjs +0 -36
  275. package/src/data/providers/traderjoe.mjs +0 -28
  276. package/src/data/providers/twap.mjs +0 -152
  277. package/src/data/providers/uniswap-v3.mjs +0 -457
  278. package/src/data/providers/uniswap-v4.mjs +0 -414
  279. package/src/data/providers/vertex.mjs +0 -41
  280. package/src/data/providers/wallet-watch.mjs +0 -264
  281. package/src/data/providers/wormhole.mjs +0 -28
  282. package/src/data/providers/yearn.mjs +0 -31
  283. package/src/data/providers/zerox.mjs +0 -167
  284. package/src/data/public-api-scan.mjs +0 -61
  285. package/src/data/quote-placeholder.mjs +0 -31
  286. package/src/desk/swap-prepare.mjs +0 -194
  287. package/src/equities/bridge.mjs +0 -388
  288. package/src/equities/fixtures/bridge_routes.json +0 -159
  289. package/src/equities/fixtures/dst_transitions.json +0 -29
  290. package/src/equities/fixtures/equity_dex_pools.json +0 -154
  291. package/src/equities/fixtures/hip3_l2books.json +0 -2711
  292. package/src/equities/fixtures/nyse_holidays.json +0 -1
  293. package/src/equities/fixtures/oracles.json +0 -100
  294. package/src/equities/fixtures/rh_pool_state.json +0 -244
  295. package/src/equities/fixtures/solana_xstocks.json +0 -704
  296. package/src/equities/fixtures/ton_xstocks.json +0 -35
  297. package/src/equities/fixtures/universe.json +0 -411
  298. package/src/equities/fixtures/venues.json +0 -3542
  299. package/src/equities/fixtures.mjs +0 -405
  300. package/src/equities/funding.mjs +0 -142
  301. package/src/equities/index.mjs +0 -166
  302. package/src/equities/liveness.mjs +0 -148
  303. package/src/equities/marketHours.mjs +0 -249
  304. package/src/equities/normalize.mjs +0 -295
  305. package/src/equities/num.mjs +0 -146
  306. package/src/equities/prepare.mjs +0 -95
  307. package/src/equities/rank.mjs +0 -260
  308. package/src/equities/sourceNum.mjs +0 -69
  309. package/src/equities/types.mjs +0 -220
  310. package/src/equities/venues/arcus-perp.mjs +0 -84
  311. package/src/equities/venues/arcus-spot.mjs +0 -105
  312. package/src/equities/venues/hyperliquid-hip3.mjs +0 -224
  313. package/src/equities/venues/rh-uniswap.mjs +0 -216
  314. package/src/equities/venues/solana-xstocks.mjs +0 -139
  315. package/src/equities/venues/ton-stonfi.mjs +0 -139
  316. package/src/exact-integer.mjs +0 -72
  317. package/src/exec-policy.mjs +0 -419
  318. package/src/flags.mjs +0 -15
  319. package/src/fresh-window.mjs +0 -76
  320. package/src/gmx-attestation.mjs +0 -176
  321. package/src/index.mjs +0 -107
  322. package/src/licensing/locals-only.mjs +0 -25
  323. package/src/nft-gas-war-guard.mjs +0 -139
  324. package/src/onboarding/agent-keys.mjs +0 -161
  325. package/src/onboarding/harness-configs.mjs +0 -102
  326. package/src/onboarding/index.mjs +0 -18
  327. package/src/onboarding/tiers.mjs +0 -139
  328. package/src/oracle-env.mjs +0 -55
  329. package/src/package-manifest.mjs +0 -42
  330. package/src/plugins/agent-plugin-loader.mjs +0 -225
  331. package/src/portfolio-risk.mjs +0 -170
  332. package/src/prepare-envelope.mjs +0 -204
  333. package/src/profile-upgrade.mjs +0 -277
  334. package/src/protocol-execution.mjs +0 -84
  335. package/src/protocol-templates/gate.mjs +0 -179
  336. package/src/protocol-templates/prepare-deploy.mjs +0 -81
  337. package/src/public-api/agent-grants.mjs +0 -57
  338. package/src/public-api/buzz-integration.mjs +0 -267
  339. package/src/public-api/connect-agent.mjs +0 -424
  340. package/src/public-api/grants.mjs +0 -142
  341. package/src/public-api/http.mjs +0 -707
  342. package/src/public-api/metering.mjs +0 -111
  343. package/src/public-control/aa-adapter.mjs +0 -402
  344. package/src/public-control/build-registry.mjs +0 -227
  345. package/src/public-control/bundler-client.mjs +0 -357
  346. package/src/public-control/grant-indexer.mjs +0 -296
  347. package/src/public-control/policy-render.mjs +0 -69
  348. package/src/public-control/policy-schema.mjs +0 -322
  349. package/src/public-control/runtime-config.mjs +0 -275
  350. package/src/public-control/session-key-model.mjs +0 -379
  351. package/src/public-control/session-orchestrator.mjs +0 -418
  352. package/src/rfq/intent.mjs +0 -180
  353. package/src/rfq/sources.mjs +0 -181
  354. package/src/route-attestation.mjs +0 -132
  355. package/src/router/best-execution.mjs +0 -326
  356. package/src/router/index.mjs +0 -250
  357. package/src/router/integrator-fee.mjs +0 -135
  358. package/src/router/prepare-bridge.mjs +0 -293
  359. package/src/router/prepare-route.mjs +0 -398
  360. package/src/router/proposal.mjs +0 -311
  361. package/src/router/risk-classifier.mjs +0 -119
  362. package/src/router/route-sources.mjs +0 -364
  363. package/src/scanner/chains.config.mjs +0 -543
  364. package/src/scanner/contract.mjs +0 -270
  365. package/src/scanner/evm-scanner.mjs +0 -394
  366. package/src/scanner/index.mjs +0 -9
  367. package/src/scanner/v2-venue.mjs +0 -351
  368. package/src/scanner/v3-venue.mjs +0 -308
  369. package/src/scopes.mjs +0 -44
  370. package/src/sell-simulation.mjs +0 -167
  371. package/src/signals/engine.mjs +0 -146
  372. package/src/signals/index.mjs +0 -1
  373. package/src/token-transfer-guard.mjs +0 -188
  374. package/src/tui/app.mjs +0 -438
  375. package/src/tui/backend.mjs +0 -215
  376. package/src/tui/format.mjs +0 -272
  377. package/src/tui/gateway-client.mjs +0 -177
  378. package/src/tui/input.mjs +0 -501
  379. package/src/tui/memory.mjs +0 -105
  380. package/src/tui/meta-learn.mjs +0 -85
  381. package/src/tui/renderer.mjs +0 -443
  382. package/src/tui/scheduler.mjs +0 -205
  383. package/src/tui/skills-loader.mjs +0 -140
  384. package/src/tui/standalone-client.mjs +0 -752
  385. package/src/tui/theme.mjs +0 -81
  386. package/src/vault-attestation.mjs +0 -146
  387. package/src/venues.mjs +0 -206
  388. package/src/watch-preferences.mjs +0 -85
  389. /package/{profiles → dist/assets/profiles}/_template/SOUL.md +0 -0
  390. /package/{profiles → dist/assets/profiles}/_template/profile.json +0 -0
  391. /package/{profiles → dist/assets/profiles}/bitcoin-agent/SOUL.md +0 -0
  392. /package/{profiles → dist/assets/profiles}/bitcoin-agent/profile.json +0 -0
  393. /package/{profiles → dist/assets/profiles}/hyperliquid-agent/SOUL.md +0 -0
  394. /package/{profiles → dist/assets/profiles}/hyperliquid-agent/profile.json +0 -0
  395. /package/{profiles → dist/assets/profiles}/oracle/SOUL.md +0 -0
  396. /package/{profiles → dist/assets/profiles}/oracle/profile.json +0 -0
  397. /package/{profiles → dist/assets/profiles}/polymarket-agent/SOUL.md +0 -0
  398. /package/{profiles → dist/assets/profiles}/polymarket-agent/profile.json +0 -0
  399. /package/{profiles → dist/assets/profiles}/profile.schema.json +0 -0
  400. /package/{profiles → dist/assets/profiles}/protocol-builder/SOUL.md +0 -0
  401. /package/{profiles → dist/assets/profiles}/protocol-builder/profile.json +0 -0
  402. /package/{profiles → dist/assets/profiles}/robinhood-agent/SOUL.md +0 -0
  403. /package/{profiles → dist/assets/profiles}/robinhood-agent/profile.json +0 -0
  404. /package/{profiles → dist/assets/profiles}/solana-agent/SOUL.md +0 -0
  405. /package/{profiles → dist/assets/profiles}/solana-agent/profile.json +0 -0
  406. /package/{profiles → dist/assets/profiles}/stable-agent/SOUL.md +0 -0
  407. /package/{profiles → dist/assets/profiles}/stable-agent/profile.json +0 -0
  408. /package/{skills → dist/assets/skills}/balance/SKILL.md +0 -0
  409. /package/{skills → dist/assets/skills}/bitcoin-l1.md +0 -0
  410. /package/{skills → dist/assets/skills}/chain-defi-ecosystem-profiling.md +0 -0
  411. /package/{skills → dist/assets/skills}/evm-contract-research.md +0 -0
  412. /package/{skills → dist/assets/skills}/hyperliquid.md +0 -0
  413. /package/{skills → dist/assets/skills}/multichain-exec-desk.md +0 -0
  414. /package/{skills → dist/assets/skills}/oracle-action-semantics/SKILL.md +0 -0
  415. /package/{skills → dist/assets/skills}/oracle-best-execution/SKILL.md +0 -0
  416. /package/{skills → dist/assets/skills}/oracle-best-execution.md +0 -0
  417. /package/{skills → dist/assets/skills}/oracle-bitcoin/SKILL.md +0 -0
  418. /package/{skills → dist/assets/skills}/oracle-chain-graphs-telegram-cards/SKILL.md +0 -0
  419. /package/{skills → dist/assets/skills}/oracle-chat/SKILL.md +0 -0
  420. /package/{skills → dist/assets/skills}/oracle-chat/chain.SKILL.md +0 -0
  421. /package/{skills → dist/assets/skills}/oracle-chat/setup.SKILL.md +0 -0
  422. /package/{skills → dist/assets/skills}/oracle-circuit-breaker/SKILL.md +0 -0
  423. /package/{skills → dist/assets/skills}/oracle-contract-research/SKILL.md +0 -0
  424. /package/{skills → dist/assets/skills}/oracle-desk/SKILL.md +0 -0
  425. /package/{skills → dist/assets/skills}/oracle-desk.md +0 -0
  426. /package/{skills → dist/assets/skills}/oracle-dex-launch/SKILL.md +0 -0
  427. /package/{skills → dist/assets/skills}/oracle-equities/SKILL.md +0 -0
  428. /package/{skills → dist/assets/skills}/oracle-grants/SKILL.md +0 -0
  429. /package/{skills → dist/assets/skills}/oracle-hypercore-staking/SKILL.md +0 -0
  430. /package/{skills → dist/assets/skills}/oracle-hyperliquid/SKILL.md +0 -0
  431. /package/{skills → dist/assets/skills}/oracle-meme-token-sniper/SKILL.md +0 -0
  432. /package/{skills → dist/assets/skills}/oracle-multichain-nft-launch/SKILL.md +0 -0
  433. /package/{skills → dist/assets/skills}/oracle-multichain-token-launch/SKILL.md +0 -0
  434. /package/{skills → dist/assets/skills}/oracle-nft-gacha-launch/SKILL.md +0 -0
  435. /package/{skills → dist/assets/skills}/oracle-nft-mint-gas-war/SKILL.md +0 -0
  436. /package/{skills → dist/assets/skills}/oracle-polymarket/SKILL.md +0 -0
  437. /package/{skills → dist/assets/skills}/oracle-protocol-builder/SKILL.md +0 -0
  438. /package/{skills → dist/assets/skills}/oracle-protocol-security/SKILL.md +0 -0
  439. /package/{skills → dist/assets/skills}/oracle-public-product/SKILL.md +0 -0
  440. /package/{skills → dist/assets/skills}/oracle-receipts/SKILL.md +0 -0
  441. /package/{skills → dist/assets/skills}/oracle-receipts.md +0 -0
  442. /package/{skills → dist/assets/skills}/oracle-rfq-tokenized-assets/SKILL.md +0 -0
  443. /package/{skills → dist/assets/skills}/oracle-smart-wallet-scanner/SKILL.md +0 -0
  444. /package/{skills → dist/assets/skills}/oracle-solana/SKILL.md +0 -0
  445. /package/{skills → dist/assets/skills}/oracle-solana-nft/SKILL.md +0 -0
  446. /package/{skills → dist/assets/skills}/oracle-token-research/SKILL.md +0 -0
  447. /package/{skills → dist/assets/skills}/solana.md +0 -0
@@ -1,296 +0,0 @@
1
- // Oracle Control — grant audit + active-permission indexer (Slice F).
2
- //
3
- // Deterministic, storage-agnostic reconstruction of grant state from an
4
- // append-only event log. Oracle Control is DETERMINISTIC AUTHORIZATION: this
5
- // module never guesses, never consults Oracle Router (advisory), and never
6
- // keeps its own mutable state. The chain (or whatever emits the event log)
7
- // is the sole source of truth; this file is a pure projection of that log
8
- // plus an explicit reference `now`.
9
- //
10
- // Hard boundaries for this file:
11
- // - No imports outside src/public-control/policy-schema.mjs (grant identity
12
- // + expiry helpers are reused from there, never duplicated).
13
- // - No import from the private executor stack, no RPC/chain calls, no I/O.
14
- // - Pure functions only: reconstructGrantIndex(events, opts) always returns
15
- // the same result for the same (events, opts.now) — same events replayed
16
- // after a "restart" (fresh process, no cache, no prior in-memory state)
17
- // reconstruct the identical active set. Input array order never matters.
18
- // - Audit/output records are built from an explicit field whitelist only.
19
- // Unknown/extra properties on raw events (or on a rejected grant payload)
20
- // are NEVER copied into output, so a poisoned event can't smuggle a
21
- // secret (API key, session key, keystore path, ...) into the index.
22
- //
23
- // Event log shape (each entry is a plain object; unknown extra fields are
24
- // ignored, never echoed):
25
- // { type: "grantCreated", at: <unix seconds>, grant: <raw grant>, grantId?: <string> }
26
- // { type: "grantActivated", at: <unix seconds>, grantId: <string> }
27
- // { type: "grantRevoked", at: <unix seconds>, grantId: <string>, reason?: <string> }
28
- // { type: "grantExpired", at: <unix seconds>, grantId: <string> }
29
- //
30
- // `grantId` on a grantCreated event is optional: when omitted it is derived
31
- // from the grant payload itself via policy-schema's grantId() (content
32
- // addressed identity). When supplied it is cross-checked against the derived
33
- // id and the event is rejected on mismatch (fail closed, no id spoofing).
34
- //
35
- // Status precedence (revocation beats expiry beats activity — mirrors the
36
- // same precedence used by session-key-model.mjs's sessionStatus): a grant
37
- // that was revoked and later would also have expired still reports revoked.
38
- // REVOKED > EXPIRED > ACTIVE > PENDING > UNKNOWN
39
- // Only ACTIVE grants are ever included in the `active` set.
40
-
41
- import { normalizeGrant, grantId as computeGrantId, isExpired, GrantValidationError } from "./policy-schema.mjs";
42
-
43
- export const GRANT_EVENT_TYPES = Object.freeze([
44
- "grantCreated",
45
- "grantActivated",
46
- "grantRevoked",
47
- "grantExpired",
48
- ]);
49
-
50
- export const GRANT_STATUS = Object.freeze({
51
- ACTIVE: "active",
52
- PENDING: "pending",
53
- REVOKED: "revoked",
54
- EXPIRED: "expired",
55
- UNKNOWN: "unknown",
56
- INVALID: "invalid",
57
- });
58
-
59
- /** Fields ever allowed into an audit/output record. Nothing outside this
60
- * whitelist can reach the returned index, no matter what a raw event or
61
- * grant payload carries. All of these are public-by-design fields already
62
- * enforced by policy-schema.mjs's REQUIRED_FIELDS. */
63
- const GRANT_SUMMARY_FIELDS = Object.freeze([
64
- "chainId",
65
- "agentAddress",
66
- "accountAddress",
67
- "actions",
68
- "targets",
69
- "maxValueWei",
70
- "maxGasWei",
71
- "maxSlippageBps",
72
- "expiresAt",
73
- "nonce",
74
- "revocationKey",
75
- ]);
76
-
77
- function isPlainObject(v) {
78
- return v != null && typeof v === "object" && !Array.isArray(v);
79
- }
80
-
81
- function toUnixSeconds(v) {
82
- if (typeof v === "bigint") return Number(v);
83
- if (typeof v === "number" && Number.isFinite(v)) return v;
84
- if (typeof v === "string" && v.trim() !== "" && Number.isFinite(Number(v.trim()))) {
85
- return Number(v.trim());
86
- }
87
- return null;
88
- }
89
-
90
- function toSafeString(v, maxLen = 512) {
91
- if (typeof v !== "string") return null;
92
- const s = v.trim();
93
- if (!s) return null;
94
- return s.length > maxLen ? s.slice(0, maxLen) : s;
95
- }
96
-
97
- function grantSummary(normalizedGrant) {
98
- const out = {};
99
- for (const field of GRANT_SUMMARY_FIELDS) out[field] = normalizedGrant[field];
100
- return out;
101
- }
102
-
103
- /**
104
- * Parse one raw event into a strictly-whitelisted internal shape. Never
105
- * throws. Extra/unknown properties on `raw` are silently dropped (this is
106
- * the point — it's the no-secret-leak boundary for whatever noise a chain
107
- * indexer/relay might attach to an event object).
108
- *
109
- * Returns { ok: true, event } or { ok: false, error: { type, at, grantId, message } }.
110
- */
111
- function parseEvent(raw, opts) {
112
- if (!isPlainObject(raw)) {
113
- return { ok: false, error: { type: null, at: null, grantId: null, message: "event must be a plain object" } };
114
- }
115
-
116
- const type = typeof raw.type === "string" ? raw.type : null;
117
- const at = toUnixSeconds(raw.at);
118
- const grantIdHint = typeof raw.grantId === "string" && raw.grantId.trim() ? raw.grantId.trim() : null;
119
-
120
- if (!GRANT_EVENT_TYPES.includes(type)) {
121
- return { ok: false, error: { type, at, grantId: grantIdHint, message: `unknown event type "${String(type)}"` } };
122
- }
123
- if (at == null) {
124
- return { ok: false, error: { type, at: null, grantId: grantIdHint, message: "event.at must be a unix-seconds number" } };
125
- }
126
-
127
- if (type === "grantCreated") {
128
- if (!isPlainObject(raw.grant)) {
129
- return { ok: false, error: { type, at, grantId: grantIdHint, message: "grantCreated requires a grant payload" } };
130
- }
131
- let normalized;
132
- try {
133
- // Identity + shape validation only — deliberately WITHOUT opts.now, so
134
- // a grant that has since expired can still be identified/reconstructed
135
- // (liveness is evaluated separately against the caller's `now`).
136
- normalized = normalizeGrant(raw.grant, { allowWildcardActions: opts.allowWildcardActions });
137
- } catch (e) {
138
- const message = e instanceof GrantValidationError ? e.message : String(e?.message || e);
139
- return { ok: false, error: { type, at, grantId: grantIdHint, message } };
140
- }
141
- const derivedId = computeGrantId(raw.grant, { allowWildcardActions: opts.allowWildcardActions });
142
- if (grantIdHint && grantIdHint !== derivedId) {
143
- return {
144
- ok: false,
145
- error: { type, at, grantId: grantIdHint, message: "grantId does not match content-derived grant identity (fail closed)" },
146
- };
147
- }
148
- return { ok: true, event: { type, at, grantId: derivedId, grant: normalized } };
149
- }
150
-
151
- // grantActivated / grantRevoked / grantExpired all reference an existing
152
- // grant purely by id — they never carry grant material.
153
- if (!grantIdHint) {
154
- return { ok: false, error: { type, at, grantId: null, message: `${type} requires a grantId` } };
155
- }
156
- const reason = type === "grantRevoked" ? toSafeString(raw.reason) : null;
157
- return { ok: true, event: { type, at, grantId: grantIdHint, reason } };
158
- }
159
-
160
- function minOf(list) {
161
- return list.length ? list.reduce((a, b) => Math.min(a, b)) : null;
162
- }
163
-
164
- /**
165
- * Reconstruct the deterministic grant index from a raw event log.
166
- *
167
- * @param {object[]} events raw event log (any order, any origin)
168
- * @param {object} opts
169
- * @param {number} opts.now REQUIRED unix-seconds reference time. All
170
- * reconstruction is relative to this instant;
171
- * pass the same `now` to get the same result.
172
- * @param {boolean} [opts.allowWildcardActions] forwarded to policy-schema.
173
- *
174
- * @returns {{
175
- * active: object[], grants currently ACTIVE, sorted by grantId
176
- * historical: object[], everything else (pending/revoked/expired/unknown/invalid), sorted by grantId
177
- * all: object[], active + historical, sorted by grantId
178
- * invalidEvents: object[] malformed/rejected raw events (whitelisted fields only)
179
- * }}
180
- */
181
- export function reconstructGrantIndex(events, opts = {}) {
182
- const now = toUnixSeconds(opts.now);
183
- if (now == null) throw new Error("grant-indexer: opts.now (unix seconds) is required");
184
- if (!Array.isArray(events)) throw new Error("grant-indexer: events must be an array");
185
-
186
- const invalidEvents = [];
187
- /** grantId -> { created: [], activated: [], revoked: [], expired: [] } */
188
- const byId = new Map();
189
-
190
- function bucket(id) {
191
- if (!byId.has(id)) {
192
- byId.set(id, { created: [], activated: [], revoked: [], expired: [] });
193
- }
194
- return byId.get(id);
195
- }
196
-
197
- for (const raw of events) {
198
- const parsed = parseEvent(raw, opts);
199
- if (!parsed.ok) {
200
- invalidEvents.push(parsed.error);
201
- continue;
202
- }
203
- const ev = parsed.event;
204
- // Chain is source of truth for what has already happened: an event dated
205
- // after the reconstruction instant hasn't occurred *yet* from this
206
- // vantage point, so it is excluded rather than applied early.
207
- if (ev.at > now) continue;
208
-
209
- const b = bucket(ev.grantId);
210
- if (ev.type === "grantCreated") {
211
- // Same content-derived id => identical normalized grant; duplicate
212
- // creations are idempotent. Keep the earliest-by-time occurrence so
213
- // createdAt is stable and order-independent.
214
- const existing = b.created[0];
215
- if (!existing || ev.at < existing.at) b.created = [ev];
216
- } else if (ev.type === "grantActivated") {
217
- b.activated.push(ev);
218
- } else if (ev.type === "grantRevoked") {
219
- b.revoked.push(ev);
220
- } else if (ev.type === "grantExpired") {
221
- b.expired.push(ev);
222
- }
223
- }
224
-
225
- const records = [];
226
- for (const [id, b] of byId.entries()) {
227
- const createdEvent = b.created[0] || null;
228
- const createdAt = createdEvent ? createdEvent.at : null;
229
- const activatedAt = minOf(b.activated.map((e) => e.at));
230
- const revokedAt = minOf(b.revoked.map((e) => e.at));
231
- const explicitExpiredAt = minOf(b.expired.map((e) => e.at));
232
-
233
- const grant = createdEvent ? createdEvent.grant : null;
234
- const schemaExpired = grant ? isExpired(grant, now) : false;
235
- const expiredAt = explicitExpiredAt != null ? explicitExpiredAt : schemaExpired ? grant.expiresAt : null;
236
-
237
- let status;
238
- if (!createdEvent) {
239
- // Lifecycle events (activated/revoked/expired) referencing a grantId
240
- // we never saw a valid grantCreated for. Never active — there is no
241
- // grant content to authorize against.
242
- status = GRANT_STATUS.UNKNOWN;
243
- } else if (b.revoked.length > 0) {
244
- status = GRANT_STATUS.REVOKED;
245
- } else if (explicitExpiredAt != null || schemaExpired) {
246
- status = GRANT_STATUS.EXPIRED;
247
- } else if (b.activated.length > 0) {
248
- status = GRANT_STATUS.ACTIVE;
249
- } else {
250
- status = GRANT_STATUS.PENDING;
251
- }
252
-
253
- const record = {
254
- grantId: id,
255
- status,
256
- createdAt,
257
- activatedAt,
258
- revokedAt,
259
- expiredAt,
260
- reason: b.revoked.length > 0 ? toSafeString(b.revoked.map((e) => e.reason).find(Boolean) || null) : null,
261
- grant: grant ? grantSummary(grant) : null,
262
- };
263
- records.push(record);
264
- }
265
-
266
- records.sort((a, b) => (a.grantId < b.grantId ? -1 : a.grantId > b.grantId ? 1 : 0));
267
- invalidEvents.sort((a, b) => {
268
- const at = (a.at ?? -Infinity) - (b.at ?? -Infinity);
269
- if (at !== 0) return at;
270
- const ai = a.grantId || "";
271
- const bi = b.grantId || "";
272
- return ai < bi ? -1 : ai > bi ? 1 : 0;
273
- });
274
-
275
- const active = records.filter((r) => r.status === GRANT_STATUS.ACTIVE);
276
- const historical = records.filter((r) => r.status !== GRANT_STATUS.ACTIVE);
277
-
278
- return Object.freeze({
279
- active: Object.freeze(active),
280
- historical: Object.freeze(historical),
281
- all: Object.freeze(records),
282
- invalidEvents: Object.freeze(invalidEvents),
283
- });
284
- }
285
-
286
- /** Convenience: is a single grant id active right now, per the given log? */
287
- export function isGrantActive(events, grantId, opts = {}) {
288
- const { active } = reconstructGrantIndex(events, opts);
289
- return active.some((r) => r.grantId === grantId);
290
- }
291
-
292
- /** Convenience: look up one grant's reconstructed record (or null). */
293
- export function getGrantRecord(events, grantId, opts = {}) {
294
- const { all } = reconstructGrantIndex(events, opts);
295
- return all.find((r) => r.grantId === grantId) || null;
296
- }
@@ -1,69 +0,0 @@
1
- // Oracle Control — human-readable grant renderer (Slice A).
2
- //
3
- // Renders a validated public grant into deterministic plain text a user can
4
- // review before approving. Rendering is a PRESENTATION of the deterministic
5
- // policy only — it never adds, removes, or reinterprets authority, and it only
6
- // ever surfaces public grant material (no keys, tokens, paths, or secrets).
7
- //
8
- // Output is byte-for-byte deterministic for a given grant: fixed field order,
9
- // fixed labels, UTC timestamps derived purely from expiresAt, and the sha256
10
- // grant id from the canonical form. Tests assert exact output.
11
-
12
- import { normalizeGrant, grantId, isReadonlyAction, GRANT_VERSION } from "./policy-schema.mjs";
13
-
14
- const LABEL_WIDTH = 14;
15
-
16
- function row(label, value) {
17
- return `${(label + ":").padEnd(LABEL_WIDTH)}${value}`;
18
- }
19
-
20
- /** Deterministic UTC ISO string from unix seconds. */
21
- function isoUtc(unixSeconds) {
22
- return new Date(unixSeconds * 1000).toISOString();
23
- }
24
-
25
- /** "50 bps (0.50%)" — deterministic fixed-2 percentage. */
26
- export function formatBps(bps) {
27
- return `${bps} bps (${(bps / 100).toFixed(2)}%)`;
28
- }
29
-
30
- /** Group wei digits for readability: 1000000 -> "1,000,000". Deterministic. */
31
- export function formatWei(weiString) {
32
- const s = String(weiString);
33
- return `${s.replace(/\B(?=(\d{3})+(?!\d))/g, ",")} wei`;
34
- }
35
-
36
- /**
37
- * Render a grant as deterministic human-readable text.
38
- * Validates/normalizes first (throws GrantValidationError on bad input), so a
39
- * grant that cannot be rendered is a grant that cannot be authorized.
40
- */
41
- export function renderGrant(input, opts = {}) {
42
- const g = normalizeGrant(input, opts);
43
- const id = grantId(g);
44
- const readonlyOnly = g.actions.every((a) => isReadonlyAction(a));
45
-
46
- const lines = [
47
- "ORACLE CONTROL GRANT (deterministic authorization)",
48
- "==================================================",
49
- row("Grant ID", id),
50
- row("Version", String(GRANT_VERSION)),
51
- row("Chain", String(g.chainId)),
52
- row("Agent", g.agentAddress),
53
- row("Account", g.accountAddress),
54
- row("Actions", g.actions.join(", ")),
55
- row("Targets", g.targets.length ? g.targets.join(", ") : "(none — read/simulate only)"),
56
- row("Max value", formatWei(g.maxValueWei)),
57
- row("Max gas", formatWei(g.maxGasWei)),
58
- row("Max slippage", formatBps(g.maxSlippageBps)),
59
- row("Expires", `${isoUtc(g.expiresAt)} (unix ${g.expiresAt})`),
60
- row("Nonce", g.nonce),
61
- row("Revocation", g.revocationKey),
62
- "--------------------------------------------------",
63
- readonlyOnly
64
- ? "Scope: read/simulate only — this grant cannot move funds."
65
- : "Scope: includes state-changing actions bounded by the caps above.",
66
- "Self-custodial: this grant contains public data only and can be revoked at any time via the revocation key.",
67
- ];
68
- return lines.join("\n");
69
- }
@@ -1,322 +0,0 @@
1
- // Oracle Control — public grant policy schema (Slice A).
2
- //
3
- // Oracle Control is DETERMINISTIC AUTHORIZATION: a grant either validates or it
4
- // does not, with no advisory judgement involved (that is Oracle Router's job,
5
- // and Router output must never gate anything here).
6
- //
7
- // This module is dependency-free (node builtins only, no npm packages) and
8
- // fully deterministic: same input object => same normalized grant => same
9
- // canonical string => same grant id, regardless of key order, address casing,
10
- // array ordering, or numeric representation (number / bigint / decimal string).
11
- //
12
- // Self-custody boundary: a grant only ever references PUBLIC material —
13
- // chain id, the agent's public address, the user's own account address,
14
- // action scopes, target contracts, spend/gas/slippage ceilings, expiry,
15
- // nonce, and a public revocation key identifier. Nothing here may carry or
16
- // derive private keys, bearer tokens, keystore paths, or session secrets.
17
- //
18
- // Fail-closed rules enforced by validateGrant / normalizeGrant:
19
- // - every required field must be present (expiry, chain, agent, account, ...)
20
- // - unknown fields are rejected (no smuggling extra authority)
21
- // - broad wildcard actions are rejected by default ("*", "*:*", "verb:*")
22
- // - empty targets are only allowed when EVERY action is read/simulate scoped
23
- // - expiresAt is required and, when a reference `now` is supplied, must be
24
- // strictly in the future
25
-
26
- import { createHash } from "node:crypto";
27
-
28
- export const GRANT_VERSION = 1;
29
-
30
- /** Every field is required. Order here is documentation only; canonical form
31
- * always sorts keys alphabetically. */
32
- export const REQUIRED_FIELDS = Object.freeze([
33
- "chainId",
34
- "agentAddress",
35
- "accountAddress",
36
- "actions",
37
- "targets",
38
- "maxValueWei",
39
- "maxGasWei",
40
- "maxSlippageBps",
41
- "expiresAt",
42
- "nonce",
43
- "revocationKey",
44
- ]);
45
-
46
- /** Action verbs that never touch state; only these may have empty targets. */
47
- export const READONLY_ACTION_VERBS = Object.freeze(["read", "simulate"]);
48
-
49
- export const MAX_SLIPPAGE_BPS = 10_000;
50
- export const MAX_GRANT_TTL_SECONDS = 24 * 60 * 60;
51
- export const MAX_NONCE_LENGTH = 128;
52
- export const MAX_REVOCATION_KEY_LENGTH = 256;
53
- export const MAX_ACTIONS = 64;
54
- export const MAX_TARGETS = 256;
55
-
56
- const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
57
- // verb:resource — lowercase verb, resource may carry dots/dashes/underscores.
58
- const ACTION_RE = /^[a-z][a-z0-9_-]*:[a-z0-9._-]+$/;
59
- // wildcard-bearing action (only accepted when explicitly opted in, and never
60
- // with a wildcard verb).
61
- const ACTION_WILDCARD_RE = /^[a-z][a-z0-9_-]*:\*$/;
62
- const NONCE_RE = /^[A-Za-z0-9._:-]+$/;
63
- const REVOCATION_KEY_RE = /^[A-Za-z0-9._:-]+$/;
64
- const DECIMAL_RE = /^(0|[1-9][0-9]*)$/;
65
-
66
- export class GrantValidationError extends Error {
67
- constructor(errors) {
68
- const lines = errors.map((e) => `${e.field}: ${e.message}`);
69
- super(`invalid grant (${errors.length} error${errors.length === 1 ? "" : "s"}): ${lines.join("; ")}`);
70
- this.name = "GrantValidationError";
71
- this.errors = errors;
72
- }
73
- }
74
-
75
- function isPlainObject(v) {
76
- return v != null && typeof v === "object" && !Array.isArray(v);
77
- }
78
-
79
- /** Parse a non-negative integer amount (number | bigint | decimal string)
80
- * into a canonical decimal string. Returns null when unparseable. */
81
- function toAmountString(v) {
82
- if (typeof v === "bigint") {
83
- return v >= 0n ? v.toString(10) : null;
84
- }
85
- if (typeof v === "number") {
86
- if (!Number.isSafeInteger(v) || v < 0) return null;
87
- return String(v);
88
- }
89
- if (typeof v === "string") {
90
- const s = v.trim();
91
- if (!DECIMAL_RE.test(s)) return null;
92
- return s;
93
- }
94
- return null;
95
- }
96
-
97
- function toSafeInt(v) {
98
- if (typeof v === "bigint") {
99
- if (v < 0n || v > BigInt(Number.MAX_SAFE_INTEGER)) return null;
100
- return Number(v);
101
- }
102
- if (typeof v === "number") {
103
- if (!Number.isSafeInteger(v) || v < 0) return null;
104
- return v;
105
- }
106
- if (typeof v === "string") {
107
- const s = v.trim();
108
- if (!DECIMAL_RE.test(s)) return null;
109
- const n = Number(s);
110
- return Number.isSafeInteger(n) ? n : null;
111
- }
112
- return null;
113
- }
114
-
115
- /** True when the action verb is read-only (read/simulate). */
116
- export function isReadonlyAction(action) {
117
- const verb = String(action).split(":")[0];
118
- return READONLY_ACTION_VERBS.includes(verb);
119
- }
120
-
121
- /**
122
- * Validate a grant. Never throws; returns { ok, errors, grant } where `grant`
123
- * is the normalized form when ok, else null.
124
- *
125
- * Options:
126
- * allowWildcardActions (default false) — permit "verb:*" actions. Broad
127
- * wildcards ("*", "*:*", wildcard verbs) are ALWAYS
128
- * rejected regardless of this flag.
129
- * now (unix seconds) — when provided, expiresAt must be
130
- * strictly greater than it. Pass explicitly for
131
- * deterministic validation; omitted means no liveness
132
- * check (pure shape validation).
133
- */
134
- export function validateGrant(input, opts = {}) {
135
- const errors = [];
136
- const push = (field, message) => errors.push({ field, message });
137
-
138
- if (!isPlainObject(input)) {
139
- push("grant", "grant must be a plain object");
140
- return { ok: false, errors, grant: null };
141
- }
142
-
143
- // Fail closed on unknown fields.
144
- for (const key of Object.keys(input)) {
145
- if (!REQUIRED_FIELDS.includes(key)) push(key, "unknown field is not allowed");
146
- }
147
- for (const key of REQUIRED_FIELDS) {
148
- if (!(key in input) || input[key] == null) push(key, "required field is missing");
149
- }
150
- if (errors.length) return { ok: false, errors, grant: null };
151
-
152
- const out = {};
153
-
154
- // chainId
155
- const chainId = toSafeInt(input.chainId);
156
- if (chainId == null || chainId <= 0) push("chainId", "must be a positive integer chain id");
157
- else out.chainId = chainId;
158
-
159
- // addresses
160
- for (const field of ["agentAddress", "accountAddress"]) {
161
- const v = input[field];
162
- if (typeof v !== "string" || !ADDRESS_RE.test(v.trim())) {
163
- push(field, "must be a 0x-prefixed 20-byte hex address");
164
- } else {
165
- out[field] = v.trim().toLowerCase();
166
- }
167
- }
168
-
169
- // actions
170
- if (!Array.isArray(input.actions) || input.actions.length === 0) {
171
- push("actions", "must be a non-empty array of action scopes");
172
- } else if (input.actions.length > MAX_ACTIONS) {
173
- push("actions", `too many actions (max ${MAX_ACTIONS})`);
174
- } else {
175
- const actions = [];
176
- for (const raw of input.actions) {
177
- const a = typeof raw === "string" ? raw.trim() : null;
178
- if (!a) {
179
- push("actions", "action entries must be non-empty strings");
180
- continue;
181
- }
182
- if (a.includes("*")) {
183
- if (opts.allowWildcardActions === true && ACTION_WILDCARD_RE.test(a)) {
184
- actions.push(a);
185
- } else {
186
- push("actions", `broad wildcard action "${a}" is rejected (fail closed)`);
187
- }
188
- continue;
189
- }
190
- if (!ACTION_RE.test(a)) {
191
- push("actions", `malformed action "${a}" (expected verb:resource)`);
192
- continue;
193
- }
194
- actions.push(a);
195
- }
196
- out.actions = [...new Set(actions)].sort();
197
- }
198
-
199
- // targets
200
- if (!Array.isArray(input.targets)) {
201
- push("targets", "must be an array of contract addresses");
202
- } else if (input.targets.length > MAX_TARGETS) {
203
- push("targets", `too many targets (max ${MAX_TARGETS})`);
204
- } else {
205
- const targets = [];
206
- for (const raw of input.targets) {
207
- const t = typeof raw === "string" ? raw.trim() : null;
208
- if (!t || !ADDRESS_RE.test(t)) {
209
- push("targets", `target "${raw}" must be a 0x-prefixed 20-byte hex address (wildcards not allowed)`);
210
- continue;
211
- }
212
- targets.push(t.toLowerCase());
213
- }
214
- out.targets = [...new Set(targets)].sort();
215
- if (out.targets.length === 0 && Array.isArray(out.actions)) {
216
- const nonReadonly = out.actions.filter((a) => !isReadonlyAction(a));
217
- if (nonReadonly.length > 0 || out.actions.length === 0) {
218
- push(
219
- "targets",
220
- `empty targets are only allowed for read/simulate scopes (blocked by: ${nonReadonly.join(", ") || "no valid actions"})`
221
- );
222
- }
223
- }
224
- }
225
-
226
- // amounts
227
- for (const field of ["maxValueWei", "maxGasWei"]) {
228
- const amt = toAmountString(input[field]);
229
- if (amt == null) push(field, "must be a non-negative integer wei amount");
230
- else out[field] = amt;
231
- }
232
-
233
- // slippage
234
- const bps = toSafeInt(input.maxSlippageBps);
235
- if (bps == null || bps > MAX_SLIPPAGE_BPS) {
236
- push("maxSlippageBps", `must be an integer between 0 and ${MAX_SLIPPAGE_BPS}`);
237
- } else {
238
- out.maxSlippageBps = bps;
239
- }
240
-
241
- // expiry — required (checked above); must be a positive unix-seconds int and,
242
- // when `now` is supplied, strictly in the future.
243
- // Date-renderable ceiling in unix seconds. JS Date maxes at 8.64e15 ms, so a
244
- // seconds value above 8.64e12 overflows the renderer (isoUtc does *1000).
245
- // Also well within the AA lane's uint48 bound, so a schema-valid expiry always
246
- // fits both the on-chain validUntil field and the human render. Reject rather
247
- // than render a bogus far-future date (fail closed on the seconds-vs-ms mistake).
248
- const MAX_UNIX_SECONDS = 8_640_000_000_000;
249
- const expiresAt = toSafeInt(input.expiresAt);
250
- if (expiresAt == null || expiresAt <= 0) {
251
- push("expiresAt", "must be a positive unix-seconds timestamp");
252
- } else if (expiresAt > MAX_UNIX_SECONDS) {
253
- push("expiresAt", "exceeds the max unix-seconds timestamp (pass seconds, not milliseconds)");
254
- } else {
255
- out.expiresAt = expiresAt;
256
- if (opts.now != null) {
257
- const now = toSafeInt(opts.now);
258
- if (now == null) push("expiresAt", "opts.now must be a unix-seconds integer");
259
- else if (expiresAt <= now) push("expiresAt", `grant is expired (expiresAt ${expiresAt} <= now ${now})`);
260
- else if (expiresAt - now > MAX_GRANT_TTL_SECONDS) {
261
- push("expiresAt", `grant exceeds the hard 24-hour TTL (${MAX_GRANT_TTL_SECONDS} seconds)`);
262
- }
263
- }
264
- }
265
-
266
- // nonce
267
- const nonce =
268
- typeof input.nonce === "string"
269
- ? input.nonce.trim()
270
- : toSafeInt(input.nonce) != null
271
- ? String(toSafeInt(input.nonce))
272
- : null;
273
- if (!nonce || nonce.length > MAX_NONCE_LENGTH || !NONCE_RE.test(nonce)) {
274
- push("nonce", `must be a non-empty string (max ${MAX_NONCE_LENGTH} chars, [A-Za-z0-9._:-])`);
275
- } else {
276
- out.nonce = nonce;
277
- }
278
-
279
- // revocationKey — a PUBLIC identifier used to revoke this grant; never a secret.
280
- const rk = typeof input.revocationKey === "string" ? input.revocationKey.trim() : null;
281
- if (!rk || rk.length > MAX_REVOCATION_KEY_LENGTH || !REVOCATION_KEY_RE.test(rk)) {
282
- push("revocationKey", `must be a non-empty public identifier (max ${MAX_REVOCATION_KEY_LENGTH} chars, [A-Za-z0-9._:-])`);
283
- } else {
284
- out.revocationKey = rk;
285
- }
286
-
287
- if (errors.length) return { ok: false, errors, grant: null };
288
- return { ok: true, errors: [], grant: out };
289
- }
290
-
291
- /** Validate + normalize; throws GrantValidationError on any failure. */
292
- export function normalizeGrant(input, opts = {}) {
293
- const { ok, errors, grant } = validateGrant(input, opts);
294
- if (!ok) throw new GrantValidationError(errors);
295
- return grant;
296
- }
297
-
298
- /**
299
- * Deterministic canonical JSON for a grant: validates/normalizes first, then
300
- * serializes with alphabetically sorted keys (arrays already sorted + deduped
301
- * by normalization). Identical grants — regardless of input key order, address
302
- * casing, or numeric representation — always produce the identical string.
303
- */
304
- export function canonicalizeGrant(input, opts = {}) {
305
- const g = normalizeGrant(input, opts);
306
- const keys = Object.keys(g).sort();
307
- const parts = keys.map((k) => `${JSON.stringify(k)}:${JSON.stringify(g[k])}`);
308
- return `{"version":${GRANT_VERSION},${parts.join(",")}}`;
309
- }
310
-
311
- /** sha256 hex of the canonical form — stable public identifier for a grant. */
312
- export function grantId(input, opts = {}) {
313
- return createHash("sha256").update(canonicalizeGrant(input, opts), "utf8").digest("hex");
314
- }
315
-
316
- /** Deterministic expiry check against an explicit unix-seconds `now`. */
317
- export function isExpired(grant, nowSeconds) {
318
- const now = toSafeInt(nowSeconds);
319
- const exp = toSafeInt(isPlainObject(grant) ? grant.expiresAt : null);
320
- if (now == null || exp == null) return true; // fail closed
321
- return exp <= now;
322
- }