@oracle-agent/oracle 0.14.1 → 0.16.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 (450) 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 +60 -0
  7. package/dist/bin/oracle-data-mcp.mjs +9 -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 +63 -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/strategy.mjs +18 -0
  44. package/dist/cli/commands/swap.mjs +36 -0
  45. package/dist/cli/commands/upgrade.mjs +2 -0
  46. package/dist/cli/commands/vault.mjs +1 -0
  47. package/dist/cli/commands/version.mjs +2 -0
  48. package/dist/data/desk-data.mjs +55 -0
  49. package/dist/data/names.mjs +2 -0
  50. package/dist/data/providers/farming.mjs +2 -0
  51. package/dist/equities/index.mjs +1 -0
  52. package/dist/index.mjs +60 -0
  53. package/dist/nft-gas-war-guard.mjs +1 -0
  54. package/dist/onboarding/harness-configs.mjs +4 -0
  55. package/dist/portfolio-risk.mjs +1 -0
  56. package/dist/prepare-envelope.mjs +1 -0
  57. package/dist/public-api/agent-grants.mjs +3 -0
  58. package/dist/public-control/policy-schema.mjs +1 -0
  59. package/dist/router/index.mjs +2 -0
  60. package/dist/scanner/index.mjs +4 -0
  61. package/dist/signals/index.mjs +1 -0
  62. package/dist/strategy/index.mjs +3 -0
  63. package/dist/watch-preferences.mjs +1 -0
  64. package/package.json +33 -78
  65. package/public/oracle-splash/_variants/ice.html +5 -18
  66. package/public/oracle-splash/_variants/ivory.html +5 -18
  67. package/public/oracle-splash/downloads/index.html +4 -4
  68. package/public/oracle-splash/index.html +113 -34
  69. package/CONTRIBUTING.md +0 -99
  70. package/bin/desk-server.mjs +0 -473
  71. package/bin/oracle-data-mcp.mjs +0 -1252
  72. package/bin/oracle-equities.mjs +0 -168
  73. package/bin/oracle-init.mjs +0 -525
  74. package/bin/oracle-public-server.mjs +0 -36
  75. package/bin/oracle-route.mjs +0 -254
  76. package/bin/oracle-scan.mjs +0 -192
  77. package/bin/oracle-upgrade.mjs +0 -42
  78. package/bin/oracle.mjs +0 -34
  79. package/docs/adding-a-chain.md +0 -229
  80. package/docs/architecture.md +0 -139
  81. package/docs/buzz-integration.md +0 -115
  82. package/docs/cli.md +0 -143
  83. package/docs/connectors.md +0 -13
  84. package/docs/hyperliquid-builder-code.md +0 -50
  85. package/docs/locals-only-fee-waiver.md +0 -34
  86. package/docs/oracle-pack-standard.md +0 -29
  87. package/docs/profiles.md +0 -151
  88. package/docs/public-surface.md +0 -206
  89. package/examples/add-a-chain.mjs +0 -65
  90. package/examples/oracle-pack-template.mjs +0 -38
  91. package/examples/research-a-token.mjs +0 -70
  92. package/plugins/oracle-owner-gate/__init__.py +0 -288
  93. package/plugins/oracle-owner-gate/plugin.yaml +0 -9
  94. package/protocols/templates/safe-erc20/SECURITY.md +0 -16
  95. package/protocols/templates/safe-erc20/foundry.toml +0 -12
  96. package/protocols/templates/safe-erc20/remappings.txt +0 -2
  97. package/protocols/templates/safe-erc20/src/SafeERC20.sol +0 -53
  98. package/protocols/templates/safe-erc20/test/SafeERC20.t.sol +0 -72
  99. package/scripts/adversarial-bench.mjs +0 -114
  100. package/scripts/build-dist.mjs +0 -119
  101. package/scripts/build-inscription.py +0 -230
  102. package/scripts/check-doc-drift.mjs +0 -123
  103. package/scripts/check-test-count.mjs +0 -105
  104. package/scripts/e2e-hl-markets.mjs +0 -21
  105. package/scripts/e2e-hl-perps.mjs +0 -48
  106. package/scripts/e2e-hypercore-staking.mjs +0 -128
  107. package/scripts/e2e-solana-bitcoin.mjs +0 -183
  108. package/scripts/guard-publish.mjs +0 -22
  109. package/scripts/protocol-template-gate.mjs +0 -15
  110. package/scripts/public-api-scan.mjs +0 -23
  111. package/scripts/publish-dist.mjs +0 -67
  112. package/scripts/record-hero-cli.sh +0 -80
  113. package/scripts/secret-scan.mjs +0 -169
  114. package/scripts/smoke-dist-bins.mjs +0 -89
  115. package/scripts/verify-dist.mjs +0 -29
  116. package/scripts/verify-v3-venues.mjs +0 -233
  117. package/skins/oracle.yaml +0 -56
  118. package/src/action-receipts.mjs +0 -265
  119. package/src/action-semantics.mjs +0 -62
  120. package/src/address-book.mjs +0 -208
  121. package/src/agent-auth.mjs +0 -206
  122. package/src/approval-guard.mjs +0 -282
  123. package/src/attestation-secret.mjs +0 -94
  124. package/src/audit-log.mjs +0 -196
  125. package/src/auth/oauth.mjs +0 -672
  126. package/src/auto-slippage.mjs +0 -378
  127. package/src/capability-posture.mjs +0 -125
  128. package/src/cards.mjs +0 -422
  129. package/src/chains.mjs +0 -62
  130. package/src/cli/chain-catalog.mjs +0 -215
  131. package/src/cli/chain-state.mjs +0 -74
  132. package/src/cli/commands/auth.mjs +0 -193
  133. package/src/cli/commands/bootstrap.mjs +0 -87
  134. package/src/cli/commands/chain.mjs +0 -143
  135. package/src/cli/commands/chat.mjs +0 -465
  136. package/src/cli/commands/credential.mjs +0 -9
  137. package/src/cli/commands/data-mcp.mjs +0 -18
  138. package/src/cli/commands/data.mjs +0 -75
  139. package/src/cli/commands/doctor.mjs +0 -136
  140. package/src/cli/commands/equities.mjs +0 -41
  141. package/src/cli/commands/farm.mjs +0 -129
  142. package/src/cli/commands/fees.mjs +0 -109
  143. package/src/cli/commands/follow.mjs +0 -130
  144. package/src/cli/commands/harness.mjs +0 -63
  145. package/src/cli/commands/help.mjs +0 -7
  146. package/src/cli/commands/init.mjs +0 -27
  147. package/src/cli/commands/mcp.mjs +0 -154
  148. package/src/cli/commands/model.mjs +0 -106
  149. package/src/cli/commands/plugins.mjs +0 -275
  150. package/src/cli/commands/prepare.mjs +0 -12
  151. package/src/cli/commands/public.mjs +0 -20
  152. package/src/cli/commands/resolve.mjs +0 -64
  153. package/src/cli/commands/route.mjs +0 -12
  154. package/src/cli/commands/runner.mjs +0 -9
  155. package/src/cli/commands/scan.mjs +0 -12
  156. package/src/cli/commands/setup.mjs +0 -294
  157. package/src/cli/commands/sign.mjs +0 -21
  158. package/src/cli/commands/signer.mjs +0 -9
  159. package/src/cli/commands/swap.mjs +0 -102
  160. package/src/cli/commands/upgrade.mjs +0 -12
  161. package/src/cli/commands/vault.mjs +0 -9
  162. package/src/cli/commands/version.mjs +0 -22
  163. package/src/cli/first-run.mjs +0 -23
  164. package/src/cli/kernel.mjs +0 -262
  165. package/src/cli/mcp-targets/chatgpt.mjs +0 -51
  166. package/src/cli/mcp-targets/claude-code.mjs +0 -39
  167. package/src/cli/mcp-targets/claude-desktop.mjs +0 -24
  168. package/src/cli/mcp-targets/codex.mjs +0 -41
  169. package/src/cli/mcp-targets/cursor.mjs +0 -32
  170. package/src/cli/mcp-targets/shared.mjs +0 -71
  171. package/src/cli/mcp-targets/vscode.mjs +0 -39
  172. package/src/cli/messaging-platforms.mjs +0 -217
  173. package/src/cli/model-config.mjs +0 -78
  174. package/src/cli/operator-dispatch.mjs +0 -194
  175. package/src/cli/oracle-harness.py +0 -415
  176. package/src/cli/paths.mjs +0 -107
  177. package/src/cli/runtime.mjs +0 -351
  178. package/src/cli/setup-state.mjs +0 -168
  179. package/src/cli/spawn-child.mjs +0 -43
  180. package/src/data/catalog.mjs +0 -858
  181. package/src/data/desk-data.mjs +0 -877
  182. package/src/data/http.mjs +0 -253
  183. package/src/data/names.mjs +0 -188
  184. package/src/data/provider-endpoint.mjs +0 -94
  185. package/src/data/providers/aave-v3.mjs +0 -139
  186. package/src/data/providers/across-bridge.mjs +0 -41
  187. package/src/data/providers/aerodrome.mjs +0 -245
  188. package/src/data/providers/aevo.mjs +0 -33
  189. package/src/data/providers/approval-indexer.mjs +0 -133
  190. package/src/data/providers/approvals.mjs +0 -594
  191. package/src/data/providers/babylon-staking.mjs +0 -149
  192. package/src/data/providers/balancer.mjs +0 -209
  193. package/src/data/providers/beefy-yields.mjs +0 -45
  194. package/src/data/providers/birdeye-tokens.mjs +0 -33
  195. package/src/data/providers/bisq-markets.mjs +0 -145
  196. package/src/data/providers/bitcoin-esplora.mjs +0 -216
  197. package/src/data/providers/bitcoin-meta.mjs +0 -378
  198. package/src/data/providers/blockscout.mjs +0 -14
  199. package/src/data/providers/bob-rootstock.mjs +0 -198
  200. package/src/data/providers/braiins-insights.mjs +0 -103
  201. package/src/data/providers/bridge-aggregator.mjs +0 -259
  202. package/src/data/providers/bridges.mjs +0 -242
  203. package/src/data/providers/bungee.mjs +0 -29
  204. package/src/data/providers/celestia-da.mjs +0 -38
  205. package/src/data/providers/changenow.mjs +0 -135
  206. package/src/data/providers/compound.mjs +0 -32
  207. package/src/data/providers/connext.mjs +0 -28
  208. package/src/data/providers/convex.mjs +0 -30
  209. package/src/data/providers/cow-protocol.mjs +0 -110
  210. package/src/data/providers/cowswap.mjs +0 -415
  211. package/src/data/providers/curve.mjs +0 -201
  212. package/src/data/providers/das-assets.mjs +0 -59
  213. package/src/data/providers/debridge.mjs +0 -109
  214. package/src/data/providers/defillama.mjs +0 -88
  215. package/src/data/providers/dexscreener.mjs +0 -43
  216. package/src/data/providers/drift.mjs +0 -32
  217. package/src/data/providers/dydx.mjs +0 -41
  218. package/src/data/providers/evm-rpc.mjs +0 -225
  219. package/src/data/providers/farming.mjs +0 -291
  220. package/src/data/providers/geckoterminal.mjs +0 -34
  221. package/src/data/providers/gmx.mjs +0 -496
  222. package/src/data/providers/hiro-stacks.mjs +0 -150
  223. package/src/data/providers/hl-assets.mjs +0 -165
  224. package/src/data/providers/hl-info.mjs +0 -108
  225. package/src/data/providers/hl-markets.mjs +0 -210
  226. package/src/data/providers/hl-outcome.mjs +0 -61
  227. package/src/data/providers/hl-perps.mjs +0 -419
  228. package/src/data/providers/hl-staking.mjs +0 -353
  229. package/src/data/providers/hl-ws.mjs +0 -119
  230. package/src/data/providers/hyperevm-dex.mjs +0 -49
  231. package/src/data/providers/jito-mev.mjs +0 -117
  232. package/src/data/providers/jupiter-venues.mjs +0 -119
  233. package/src/data/providers/jupiter.mjs +0 -315
  234. package/src/data/providers/kamino-strategies.mjs +0 -143
  235. package/src/data/providers/l2beat-tvl.mjs +0 -42
  236. package/src/data/providers/lifi.mjs +0 -166
  237. package/src/data/providers/liquid-esplora.mjs +0 -152
  238. package/src/data/providers/magiceden-ordinals.mjs +0 -141
  239. package/src/data/providers/magiceden-sol.mjs +0 -406
  240. package/src/data/providers/mempool-lightning.mjs +0 -144
  241. package/src/data/providers/mempool-mining.mjs +0 -142
  242. package/src/data/providers/meteora-dlmm.mjs +0 -168
  243. package/src/data/providers/morpho.mjs +0 -177
  244. package/src/data/providers/nft-gallery.mjs +0 -163
  245. package/src/data/providers/nft-portfolio.mjs +0 -494
  246. package/src/data/providers/odos.mjs +0 -156
  247. package/src/data/providers/ondo.mjs +0 -31
  248. package/src/data/providers/oneinch.mjs +0 -174
  249. package/src/data/providers/opensea-multichain.mjs +0 -136
  250. package/src/data/providers/opensea-nft.mjs +0 -371
  251. package/src/data/providers/orca.mjs +0 -29
  252. package/src/data/providers/ordinals-runes.mjs +0 -180
  253. package/src/data/providers/pancakeswap.mjs +0 -28
  254. package/src/data/providers/paraswap.mjs +0 -125
  255. package/src/data/providers/pendle.mjs +0 -188
  256. package/src/data/providers/poly-clob.mjs +0 -283
  257. package/src/data/providers/poly-public.mjs +0 -96
  258. package/src/data/providers/poly-ws.mjs +0 -103
  259. package/src/data/providers/polygon-staking.mjs +0 -227
  260. package/src/data/providers/portfolio-history.mjs +0 -394
  261. package/src/data/providers/portfolio.mjs +0 -600
  262. package/src/data/providers/pyth-price-feeds.mjs +0 -172
  263. package/src/data/providers/raydium.mjs +0 -35
  264. package/src/data/providers/relayscan-mev.mjs +0 -72
  265. package/src/data/providers/rfq.mjs +0 -15
  266. package/src/data/providers/rh-agent.mjs +0 -59
  267. package/src/data/providers/sanctum-lst.mjs +0 -180
  268. package/src/data/providers/satflow.mjs +0 -341
  269. package/src/data/providers/signed-material-guard.mjs +0 -108
  270. package/src/data/providers/solana-rpc.mjs +0 -197
  271. package/src/data/providers/solend-lending.mjs +0 -166
  272. package/src/data/providers/spark-lend.mjs +0 -42
  273. package/src/data/providers/squid-router.mjs +0 -29
  274. package/src/data/providers/stargate.mjs +0 -29
  275. package/src/data/providers/sushiswap.mjs +0 -30
  276. package/src/data/providers/symbiotic-restaking.mjs +0 -41
  277. package/src/data/providers/thorchain.mjs +0 -36
  278. package/src/data/providers/traderjoe.mjs +0 -28
  279. package/src/data/providers/twap.mjs +0 -152
  280. package/src/data/providers/uniswap-v3.mjs +0 -457
  281. package/src/data/providers/uniswap-v4.mjs +0 -414
  282. package/src/data/providers/vertex.mjs +0 -41
  283. package/src/data/providers/wallet-watch.mjs +0 -264
  284. package/src/data/providers/wormhole.mjs +0 -28
  285. package/src/data/providers/yearn.mjs +0 -31
  286. package/src/data/providers/zerox.mjs +0 -167
  287. package/src/data/public-api-scan.mjs +0 -61
  288. package/src/data/quote-placeholder.mjs +0 -31
  289. package/src/desk/swap-prepare.mjs +0 -194
  290. package/src/equities/bridge.mjs +0 -388
  291. package/src/equities/fixtures/bridge_routes.json +0 -159
  292. package/src/equities/fixtures/dst_transitions.json +0 -29
  293. package/src/equities/fixtures/equity_dex_pools.json +0 -154
  294. package/src/equities/fixtures/hip3_l2books.json +0 -2711
  295. package/src/equities/fixtures/nyse_holidays.json +0 -1
  296. package/src/equities/fixtures/oracles.json +0 -100
  297. package/src/equities/fixtures/rh_pool_state.json +0 -244
  298. package/src/equities/fixtures/solana_xstocks.json +0 -704
  299. package/src/equities/fixtures/ton_xstocks.json +0 -35
  300. package/src/equities/fixtures/universe.json +0 -411
  301. package/src/equities/fixtures/venues.json +0 -3542
  302. package/src/equities/fixtures.mjs +0 -405
  303. package/src/equities/funding.mjs +0 -142
  304. package/src/equities/index.mjs +0 -166
  305. package/src/equities/liveness.mjs +0 -148
  306. package/src/equities/marketHours.mjs +0 -249
  307. package/src/equities/normalize.mjs +0 -295
  308. package/src/equities/num.mjs +0 -146
  309. package/src/equities/prepare.mjs +0 -95
  310. package/src/equities/rank.mjs +0 -260
  311. package/src/equities/sourceNum.mjs +0 -69
  312. package/src/equities/types.mjs +0 -220
  313. package/src/equities/venues/arcus-perp.mjs +0 -84
  314. package/src/equities/venues/arcus-spot.mjs +0 -105
  315. package/src/equities/venues/hyperliquid-hip3.mjs +0 -224
  316. package/src/equities/venues/rh-uniswap.mjs +0 -216
  317. package/src/equities/venues/solana-xstocks.mjs +0 -139
  318. package/src/equities/venues/ton-stonfi.mjs +0 -139
  319. package/src/exact-integer.mjs +0 -72
  320. package/src/exec-policy.mjs +0 -419
  321. package/src/flags.mjs +0 -15
  322. package/src/fresh-window.mjs +0 -76
  323. package/src/gmx-attestation.mjs +0 -176
  324. package/src/index.mjs +0 -107
  325. package/src/licensing/locals-only.mjs +0 -25
  326. package/src/nft-gas-war-guard.mjs +0 -139
  327. package/src/onboarding/agent-keys.mjs +0 -161
  328. package/src/onboarding/harness-configs.mjs +0 -102
  329. package/src/onboarding/index.mjs +0 -18
  330. package/src/onboarding/tiers.mjs +0 -139
  331. package/src/oracle-env.mjs +0 -55
  332. package/src/package-manifest.mjs +0 -42
  333. package/src/plugins/agent-plugin-loader.mjs +0 -225
  334. package/src/portfolio-risk.mjs +0 -170
  335. package/src/prepare-envelope.mjs +0 -204
  336. package/src/profile-upgrade.mjs +0 -277
  337. package/src/protocol-execution.mjs +0 -84
  338. package/src/protocol-templates/gate.mjs +0 -179
  339. package/src/protocol-templates/prepare-deploy.mjs +0 -81
  340. package/src/public-api/agent-grants.mjs +0 -57
  341. package/src/public-api/buzz-integration.mjs +0 -267
  342. package/src/public-api/connect-agent.mjs +0 -424
  343. package/src/public-api/grants.mjs +0 -142
  344. package/src/public-api/http.mjs +0 -707
  345. package/src/public-api/metering.mjs +0 -111
  346. package/src/public-control/aa-adapter.mjs +0 -402
  347. package/src/public-control/build-registry.mjs +0 -227
  348. package/src/public-control/bundler-client.mjs +0 -357
  349. package/src/public-control/grant-indexer.mjs +0 -296
  350. package/src/public-control/policy-render.mjs +0 -69
  351. package/src/public-control/policy-schema.mjs +0 -322
  352. package/src/public-control/runtime-config.mjs +0 -275
  353. package/src/public-control/session-key-model.mjs +0 -379
  354. package/src/public-control/session-orchestrator.mjs +0 -418
  355. package/src/rfq/intent.mjs +0 -180
  356. package/src/rfq/sources.mjs +0 -181
  357. package/src/route-attestation.mjs +0 -132
  358. package/src/router/best-execution.mjs +0 -326
  359. package/src/router/index.mjs +0 -250
  360. package/src/router/integrator-fee.mjs +0 -135
  361. package/src/router/prepare-bridge.mjs +0 -293
  362. package/src/router/prepare-route.mjs +0 -398
  363. package/src/router/proposal.mjs +0 -311
  364. package/src/router/risk-classifier.mjs +0 -119
  365. package/src/router/route-sources.mjs +0 -364
  366. package/src/scanner/chains.config.mjs +0 -543
  367. package/src/scanner/contract.mjs +0 -270
  368. package/src/scanner/evm-scanner.mjs +0 -394
  369. package/src/scanner/index.mjs +0 -9
  370. package/src/scanner/v2-venue.mjs +0 -351
  371. package/src/scanner/v3-venue.mjs +0 -308
  372. package/src/scopes.mjs +0 -44
  373. package/src/sell-simulation.mjs +0 -167
  374. package/src/signals/engine.mjs +0 -146
  375. package/src/signals/index.mjs +0 -1
  376. package/src/token-transfer-guard.mjs +0 -188
  377. package/src/tui/app.mjs +0 -438
  378. package/src/tui/backend.mjs +0 -215
  379. package/src/tui/format.mjs +0 -272
  380. package/src/tui/gateway-client.mjs +0 -177
  381. package/src/tui/input.mjs +0 -501
  382. package/src/tui/memory.mjs +0 -105
  383. package/src/tui/meta-learn.mjs +0 -85
  384. package/src/tui/renderer.mjs +0 -443
  385. package/src/tui/scheduler.mjs +0 -205
  386. package/src/tui/skills-loader.mjs +0 -140
  387. package/src/tui/standalone-client.mjs +0 -752
  388. package/src/tui/theme.mjs +0 -81
  389. package/src/vault-attestation.mjs +0 -146
  390. package/src/venues.mjs +0 -206
  391. package/src/watch-preferences.mjs +0 -85
  392. /package/{profiles → dist/assets/profiles}/_template/SOUL.md +0 -0
  393. /package/{profiles → dist/assets/profiles}/_template/profile.json +0 -0
  394. /package/{profiles → dist/assets/profiles}/bitcoin-agent/SOUL.md +0 -0
  395. /package/{profiles → dist/assets/profiles}/bitcoin-agent/profile.json +0 -0
  396. /package/{profiles → dist/assets/profiles}/hyperliquid-agent/SOUL.md +0 -0
  397. /package/{profiles → dist/assets/profiles}/hyperliquid-agent/profile.json +0 -0
  398. /package/{profiles → dist/assets/profiles}/oracle/SOUL.md +0 -0
  399. /package/{profiles → dist/assets/profiles}/oracle/profile.json +0 -0
  400. /package/{profiles → dist/assets/profiles}/polymarket-agent/SOUL.md +0 -0
  401. /package/{profiles → dist/assets/profiles}/polymarket-agent/profile.json +0 -0
  402. /package/{profiles → dist/assets/profiles}/profile.schema.json +0 -0
  403. /package/{profiles → dist/assets/profiles}/protocol-builder/SOUL.md +0 -0
  404. /package/{profiles → dist/assets/profiles}/protocol-builder/profile.json +0 -0
  405. /package/{profiles → dist/assets/profiles}/robinhood-agent/SOUL.md +0 -0
  406. /package/{profiles → dist/assets/profiles}/robinhood-agent/profile.json +0 -0
  407. /package/{profiles → dist/assets/profiles}/solana-agent/SOUL.md +0 -0
  408. /package/{profiles → dist/assets/profiles}/solana-agent/profile.json +0 -0
  409. /package/{profiles → dist/assets/profiles}/stable-agent/SOUL.md +0 -0
  410. /package/{profiles → dist/assets/profiles}/stable-agent/profile.json +0 -0
  411. /package/{skills → dist/assets/skills}/balance/SKILL.md +0 -0
  412. /package/{skills → dist/assets/skills}/bitcoin-l1.md +0 -0
  413. /package/{skills → dist/assets/skills}/chain-defi-ecosystem-profiling.md +0 -0
  414. /package/{skills → dist/assets/skills}/evm-contract-research.md +0 -0
  415. /package/{skills → dist/assets/skills}/hyperliquid.md +0 -0
  416. /package/{skills → dist/assets/skills}/multichain-exec-desk.md +0 -0
  417. /package/{skills → dist/assets/skills}/oracle-action-semantics/SKILL.md +0 -0
  418. /package/{skills → dist/assets/skills}/oracle-best-execution/SKILL.md +0 -0
  419. /package/{skills → dist/assets/skills}/oracle-best-execution.md +0 -0
  420. /package/{skills → dist/assets/skills}/oracle-bitcoin/SKILL.md +0 -0
  421. /package/{skills → dist/assets/skills}/oracle-chain-graphs-telegram-cards/SKILL.md +0 -0
  422. /package/{skills → dist/assets/skills}/oracle-chat/SKILL.md +0 -0
  423. /package/{skills → dist/assets/skills}/oracle-chat/chain.SKILL.md +0 -0
  424. /package/{skills → dist/assets/skills}/oracle-chat/setup.SKILL.md +0 -0
  425. /package/{skills → dist/assets/skills}/oracle-circuit-breaker/SKILL.md +0 -0
  426. /package/{skills → dist/assets/skills}/oracle-contract-research/SKILL.md +0 -0
  427. /package/{skills → dist/assets/skills}/oracle-desk/SKILL.md +0 -0
  428. /package/{skills → dist/assets/skills}/oracle-desk.md +0 -0
  429. /package/{skills → dist/assets/skills}/oracle-dex-launch/SKILL.md +0 -0
  430. /package/{skills → dist/assets/skills}/oracle-equities/SKILL.md +0 -0
  431. /package/{skills → dist/assets/skills}/oracle-grants/SKILL.md +0 -0
  432. /package/{skills → dist/assets/skills}/oracle-hypercore-staking/SKILL.md +0 -0
  433. /package/{skills → dist/assets/skills}/oracle-hyperliquid/SKILL.md +0 -0
  434. /package/{skills → dist/assets/skills}/oracle-meme-token-sniper/SKILL.md +0 -0
  435. /package/{skills → dist/assets/skills}/oracle-multichain-nft-launch/SKILL.md +0 -0
  436. /package/{skills → dist/assets/skills}/oracle-multichain-token-launch/SKILL.md +0 -0
  437. /package/{skills → dist/assets/skills}/oracle-nft-gacha-launch/SKILL.md +0 -0
  438. /package/{skills → dist/assets/skills}/oracle-nft-mint-gas-war/SKILL.md +0 -0
  439. /package/{skills → dist/assets/skills}/oracle-polymarket/SKILL.md +0 -0
  440. /package/{skills → dist/assets/skills}/oracle-protocol-builder/SKILL.md +0 -0
  441. /package/{skills → dist/assets/skills}/oracle-protocol-security/SKILL.md +0 -0
  442. /package/{skills → dist/assets/skills}/oracle-public-product/SKILL.md +0 -0
  443. /package/{skills → dist/assets/skills}/oracle-receipts/SKILL.md +0 -0
  444. /package/{skills → dist/assets/skills}/oracle-receipts.md +0 -0
  445. /package/{skills → dist/assets/skills}/oracle-rfq-tokenized-assets/SKILL.md +0 -0
  446. /package/{skills → dist/assets/skills}/oracle-smart-wallet-scanner/SKILL.md +0 -0
  447. /package/{skills → dist/assets/skills}/oracle-solana/SKILL.md +0 -0
  448. /package/{skills → dist/assets/skills}/oracle-solana-nft/SKILL.md +0 -0
  449. /package/{skills → dist/assets/skills}/oracle-token-research/SKILL.md +0 -0
  450. /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
- }