@thirdfy/agent-cli 0.1.48 → 0.2.2

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 (61) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +25 -23
  3. package/bin/thirdfy-agent.mjs +3 -5106
  4. package/package.json +17 -4
  5. package/scripts/validate-npm-pack.mjs +43 -0
  6. package/src/cli/errors.mjs +103 -0
  7. package/src/cli/flags.mjs +66 -0
  8. package/src/cli/help.mjs +89 -0
  9. package/src/cli/manifest.mjs +422 -0
  10. package/src/cli/options/agent.mjs +9 -0
  11. package/src/cli/options/analytics.mjs +5 -0
  12. package/src/cli/options/auth.mjs +10 -0
  13. package/src/cli/options/bootstrap.mjs +19 -0
  14. package/src/cli/options/chains.mjs +3 -0
  15. package/src/cli/options/credentials.mjs +15 -0
  16. package/src/cli/options/delegation.mjs +22 -0
  17. package/src/cli/options/execution.mjs +24 -0
  18. package/src/cli/options/global.mjs +16 -0
  19. package/src/cli/options/index.mjs +37 -0
  20. package/src/cli/options/polymarket.mjs +3 -0
  21. package/src/cli/options/tracking.mjs +18 -0
  22. package/src/cli/options/wallet.mjs +7 -0
  23. package/src/cli/program.mjs +5 -0
  24. package/src/cli/register.mjs +118 -0
  25. package/src/commands/agent.mjs +194 -0
  26. package/src/commands/bootstrap.mjs +762 -0
  27. package/src/commands/credentials.mjs +102 -0
  28. package/src/commands/delegation.mjs +355 -0
  29. package/src/commands/discovery.mjs +77 -0
  30. package/src/commands/doctor.mjs +320 -0
  31. package/src/commands/execute.mjs +255 -0
  32. package/src/commands/hyperliquid.mjs +51 -0
  33. package/src/commands/index.mjs +6 -0
  34. package/src/commands/polymarket.mjs +89 -0
  35. package/src/commands/prompt.mjs +33 -0
  36. package/src/commands/telemetry.mjs +146 -0
  37. package/src/commands/wallet.mjs +129 -0
  38. package/src/core/args.mjs +68 -0
  39. package/src/core/constants.mjs +31 -0
  40. package/src/core/context.mjs +145 -0
  41. package/src/core/envelope.mjs +20 -0
  42. package/src/core/http.mjs +77 -0
  43. package/src/core/index.mjs +6 -0
  44. package/src/core/runMode.mjs +72 -0
  45. package/src/runtime/actions/selection.mjs +339 -0
  46. package/src/runtime/agentExtract.mjs +52 -0
  47. package/src/runtime/authHelpers.mjs +53 -0
  48. package/src/runtime/bitfinexProbe.mjs +121 -0
  49. package/src/runtime/context.mjs +28 -0
  50. package/src/runtime/createThirdfyAgentRuntime.mjs +2 -0
  51. package/src/runtime/errors.mjs +3 -0
  52. package/src/runtime/execution/amounts.mjs +193 -0
  53. package/src/runtime/execution/lanes.mjs +75 -0
  54. package/src/runtime/execution/payloads.mjs +148 -0
  55. package/src/runtime/execution/runners.mjs +702 -0
  56. package/src/runtime/handlers.mjs +181 -0
  57. package/src/runtime/index.mjs +4 -0
  58. package/src/runtime/main.mjs +204 -0
  59. package/src/runtime/onboardingHints.mjs +54 -0
  60. package/src/runtime/owsSigning.mjs +118 -0
  61. package/src/runtime/providerHints.mjs +414 -0
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Static and capabilities-backed provider discovery hints for CLI actions filtering.
3
+ */
4
+ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
5
+ function getTradingProviderHint(providerId) {
6
+ const provider = String(providerId || '').trim().toLowerCase();
7
+ const manifestHint = getCachedProviderHintFromCapabilities(provider);
8
+ if (manifestHint) return manifestHint;
9
+ if (provider === 'trading') {
10
+ return {
11
+ provider,
12
+ category: 'onchain_spot',
13
+ canonicalWriteAction: 'swap',
14
+ supportedChains: [1, 10, 130, 137, 8453, 84532, 42161],
15
+ laneCompatibility:
16
+ 'Kyber quote/build is live on Ethereum, Optimism, Unichain, Polygon, Base, and Arbitrum; sponsored Kyber execution is validated on Base only. Gnosis FX uses the fx provider via CoW.',
17
+ credentialUx: 'Use normal Thirdfy login/delegation; do not use this provider for Hyperliquid or Polymarket.',
18
+ };
19
+ }
20
+ if (provider === 'hyperliquid') {
21
+ return {
22
+ provider,
23
+ category: 'leveraged_perps',
24
+ canonicalWriteAction: 'place_hyperliquid_perps_order',
25
+ readActions: [
26
+ 'get_hyperliquid_perps_meta',
27
+ 'get_hyperliquid_all_mids',
28
+ 'search_hyperliquid_markets',
29
+ 'get_hyperliquid_l2_book',
30
+ 'get_hyperliquid_candles',
31
+ 'get_hyperliquid_user_state',
32
+ 'get_hyperliquid_open_orders',
33
+ 'get_hyperliquid_funding_status',
34
+ 'get_hyperliquid_onboarding_plan',
35
+ 'get_hyperliquid_referral_status',
36
+ 'get_hyperliquid_builder_fee_status',
37
+ ],
38
+ orderManagementActions: [
39
+ 'place_hyperliquid_perps_order',
40
+ 'cancel_hyperliquid_perps_order',
41
+ 'get_hyperliquid_open_orders',
42
+ ],
43
+ setupActions: [
44
+ 'get_hyperliquid_funding_status',
45
+ 'get_hyperliquid_setup_status',
46
+ 'get_hyperliquid_onboarding_plan',
47
+ 'prepare_hyperliquid_onboarding',
48
+ 'get_bridge_quote',
49
+ 'execute_bridge',
50
+ 'approve_hyperliquid_agent',
51
+ 'get_hyperliquid_builder_fee_status',
52
+ 'approve_hyperliquid_builder_fee',
53
+ 'set_hyperliquid_referrer',
54
+ 'deposit_hyperliquid_bridge',
55
+ 'deposit_hyperliquid_staking',
56
+ 'claim_hyperliquid_testnet_faucet',
57
+ ],
58
+ statusActions: [
59
+ 'get_hyperliquid_funding_status',
60
+ 'get_hyperliquid_setup_status',
61
+ 'get_hyperliquid_builder_fee_status',
62
+ 'get_hyperliquid_referral_status',
63
+ ],
64
+ credentialType: 'hyperliquid_api_wallet',
65
+ credentialUx:
66
+ 'Thirdfy manages Hyperliquid API-wallet setup for normal users. Delegated writes require encrypted privateKey-backed hyperliquid_api_wallet credentials; walletAddress-only records are rejected until Privy walletId-backed Hyperliquid signing ships.',
67
+ recommendedFundingChainId: 42161,
68
+ supportedFundingSourceChains: [
69
+ { chainId: 42161, name: 'Arbitrum One', path: 'native USDC -> Bridge2' },
70
+ { chainId: 8453, name: 'Base', path: 'Li.Fi -> Arbitrum native USDC -> Bridge2' },
71
+ { chainId: 1, name: 'Ethereum', path: 'Li.Fi -> Arbitrum native USDC -> Bridge2' },
72
+ { chainId: 137, name: 'Polygon', path: 'Li.Fi -> Arbitrum native USDC -> Bridge2' },
73
+ ],
74
+ setupBlockers: [
75
+ 'HYPERLIQUID_SOURCE_FUNDS_REQUIRED',
76
+ 'HYPERLIQUID_ARBITRUM_USDC_REQUIRED',
77
+ 'HYPERLIQUID_BRIDGE2_DEPOSIT_REQUIRED',
78
+ 'HYPERLIQUID_API_WALLET_APPROVAL_REQUIRED',
79
+ 'HYPERLIQUID_BUILDER_APPROVAL_REQUIRED',
80
+ 'HYPERLIQUID_POLICY_REQUIRED',
81
+ 'HYPERLIQUID_CREDITS_REQUIRED',
82
+ 'HYPERLIQUID_API_WALLET_SIGNER_REQUIRED',
83
+ ],
84
+ sourceBridgeProvider: 'lifi',
85
+ funding:
86
+ 'Best path: Base/Ethereum/Polygon USDC -> Li.Fi bridge to native Arbitrum USDC -> Hyperliquid Bridge2. If the user already has native Circle USDC on Arbitrum One (42161), skip Li.Fi and call deposit_hyperliquid_bridge. Minimum 5 USDC; Bridge2 credits the sender/source wallet.',
87
+ laneCompatibility:
88
+ 'Use self only for setup signatures or Arbitrum funding transactions. Perps orders should use thirdfy, hybrid, or agent_wallet.',
89
+ setupExample:
90
+ 'actions --provider hyperliquid -> search_hyperliquid_markets (or get_hyperliquid_perps_meta + get_hyperliquid_all_mids) -> get_hyperliquid_funding_status -> get_hyperliquid_onboarding_plan -> prepare_hyperliquid_onboarding -> get_bridge_quote/execute_bridge if source is Base -> deposit_hyperliquid_bridge on 42161 -> approve_hyperliquid_agent -> approve_hyperliquid_builder_fee if required -> get_hyperliquid_setup_status',
91
+ example:
92
+ 'thirdfy-agent actions --provider hyperliquid && thirdfy-agent preflight --action place_hyperliquid_perps_order --params \'{"coin":"ETH","isBuy":true,"size":0.01,"limitPx":3500,"orderType":"market"}\'',
93
+ };
94
+ }
95
+ if (provider === 'bitfinex') {
96
+ return {
97
+ provider,
98
+ category: 'cex_funding',
99
+ canonicalReadAction: 'get_bitfinex_funding_ticker',
100
+ canonicalWriteAction: 'submit_bitfinex_funding_offer',
101
+ readActions: [
102
+ 'get_bitfinex_funding_ticker',
103
+ 'get_bitfinex_funding_stats_hist',
104
+ 'get_bitfinex_funding_book',
105
+ 'get_bitfinex_funding_info',
106
+ 'get_bitfinex_wallets',
107
+ 'list_bitfinex_funding_offers',
108
+ 'list_bitfinex_funding_credits',
109
+ ],
110
+ orderManagementActions: [
111
+ 'submit_bitfinex_funding_offer',
112
+ 'cancel_bitfinex_funding_offer',
113
+ 'submit_bitfinex_order',
114
+ 'update_bitfinex_order',
115
+ 'cancel_bitfinex_order',
116
+ ],
117
+ credentialType: 'bitfinex_api_key',
118
+ credentialRef: 'bitfinex:funding-primary',
119
+ credentialUx:
120
+ 'Save and verify Bitfinex funding credentials in EarnClaw/Thirdfy first. MCP/CLI order intents reference credentialRef only; raw keys are never sent through agentRun.',
121
+ laneCompatibility:
122
+ 'Bitfinex CEX writes require thirdfy, hybrid, or agent_wallet so Thirdfy can inject stored credentials and run policy/preflight. Reads and setup discovery can be listed without raw credentials.',
123
+ example:
124
+ 'thirdfy-agent actions --provider bitfinex && thirdfy-agent run --run-mode thirdfy --action submit_bitfinex_funding_offer --params \'{"symbol":"fUSD","amount":"100","rate":"0.0002","period":2,"credentialRef":"bitfinex:funding-primary"}\'',
125
+ };
126
+ }
127
+ if (provider === 'polymarket') {
128
+ return {
129
+ provider,
130
+ category: 'prediction_market',
131
+ canonicalWriteAction: 'place_polymarket_order',
132
+ readActions: [
133
+ 'get_polymarket_tags',
134
+ 'search_polymarket',
135
+ 'get_polymarket_events',
136
+ 'get_polymarket_event',
137
+ 'get_polymarket_markets',
138
+ 'get_polymarket_market',
139
+ 'get_polymarket_order_book',
140
+ 'get_polymarket_prices',
141
+ 'get_polymarket_price_history',
142
+ 'get_polymarket_setup_status',
143
+ 'get_polymarket_onboarding_plan',
144
+ 'get_polymarket_order',
145
+ 'get_polymarket_user_orders',
146
+ ],
147
+ discoveryFlow: [
148
+ 'get_polymarket_tags or search_polymarket',
149
+ 'get_polymarket_events active=true closed=false',
150
+ 'get_polymarket_market or get_polymarket_markets',
151
+ 'get_polymarket_order_book',
152
+ 'get_polymarket_prices or get_polymarket_price_history',
153
+ 'get_polymarket_setup_status and get_polymarket_onboarding_plan before place_polymarket_order',
154
+ ],
155
+ orderManagementActions: [
156
+ 'place_polymarket_order',
157
+ 'get_polymarket_order',
158
+ 'get_polymarket_user_orders',
159
+ 'cancel_polymarket_order',
160
+ ],
161
+ setupActions: [
162
+ 'get_polymarket_setup_status',
163
+ 'get_polymarket_onboarding_plan',
164
+ 'agent_wallet_setup:ensure_agent_wallet_deposit_wallet',
165
+ 'agent_wallet_setup:deploy_deposit_wallet_and_set_approvals',
166
+ 'agent_wallet_setup:create_or_derive_clob_credentials',
167
+ 'browser_setup:add_privy_signer',
168
+ 'browser_setup:create_or_derive_clob_credentials',
169
+ 'browser_setup:deploy_deposit_wallet_and_set_approvals',
170
+ ],
171
+ statusActions: ['get_polymarket_setup_status'],
172
+ credentialType: 'polymarket_clob',
173
+ credentialUx:
174
+ 'Autonomous agents use an agent-owned Privy wallet and Polymarket deposit wallet first: Thirdfy creates the agent wallet, deploys/approves the deposit wallet, and stores encrypted CLOB credentials once Polymarket key creation is available. User-owned browser setup remains optional for interactive capital.',
175
+ funding:
176
+ 'Fund the agent-owned Polymarket deposit wallet with Polygon pUSD after deployment and approvals. If users start with Base USDC, route Base USDC -> Polygon pUSD to the deposit wallet recipient before CLOB order placement.',
177
+ laneCompatibility:
178
+ 'Use agent_wallet as the preferred autonomous-agent path. Use self only for user-owned setup/funding signatures. CLOB orders should use thirdfy, hybrid, or agent_wallet after polymarket_clob credentials are stored.',
179
+ setupBlockers: [
180
+ 'POLYMARKET_DEPOSIT_WALLET_REQUIRED',
181
+ 'POLYMARKET_CLOB_CREDENTIALS_REQUIRED',
182
+ 'POLYMARKET_DEPOSIT_WALLET_DEPLOY_REQUIRED',
183
+ 'POLYMARKET_DEPOSIT_WALLET_APPROVALS_REQUIRED',
184
+ 'POLYMARKET_DEPOSIT_WALLET_PUSD_REQUIRED',
185
+ 'POLYMARKET_CREDITS_REQUIRED',
186
+ 'POLYMARKET_RELAYER_UNAVAILABLE',
187
+ 'POLYMARKET_CLOB_KEY_CREATION_BLOCKED_UNTIL_POLYMARKET_FIX',
188
+ ],
189
+ example:
190
+ 'thirdfy-agent actions --provider polymarket && thirdfy-agent preflight --action place_polymarket_order --params \'{"tokenID":"...","price":0.42,"size":10,"side":"BUY"}\'',
191
+ };
192
+ }
193
+ if (provider === 'prediction') {
194
+ return {
195
+ provider,
196
+ category: 'prediction',
197
+ canonicalWriteAction: 'place_prediction_order',
198
+ readActions: [
199
+ 'get_prediction_tags',
200
+ 'search_prediction_markets',
201
+ 'get_prediction_events',
202
+ 'get_prediction_markets',
203
+ 'get_prediction_market',
204
+ 'get_prediction_order_book',
205
+ ],
206
+ orderManagementActions: ['place_prediction_order', 'cancel_prediction_order'],
207
+ compatibilityProvider: 'polymarket',
208
+ };
209
+ }
210
+ if (provider === 'bridge') {
211
+ return {
212
+ provider,
213
+ category: 'bridge',
214
+ canonicalReadAction: 'get_bridge_quote',
215
+ canonicalWriteAction: 'execute_bridge',
216
+ readActions: ['get_bridge_quote', 'get_supported_bridge_chains', 'check_bridge_status'],
217
+ orderManagementActions: ['execute_bridge'],
218
+ supportedChains: [1, 8453, 42161, 10, 137, 56, 43114, 250, 100, 1284, 1285, 25, 288, 1088, 42220, 122, 324, 59144, 534352, 5000, 81457, 169],
219
+ laneCompatibility: 'Li.Fi bridge supports mainnet value movement only; testnets are not auto-mapped to mainnet.',
220
+ };
221
+ }
222
+ if (provider === 'earn') {
223
+ return {
224
+ provider,
225
+ category: 'earn',
226
+ canonicalReadAction: 'get_earn_opportunities',
227
+ canonicalWriteAction: 'deposit_earn_position',
228
+ readActions: [
229
+ 'get_earn_opportunities',
230
+ 'get_earn_provider',
231
+ 'get_earn_position',
232
+ 'get_morpho_vaults',
233
+ 'get_aegis_pools',
234
+ 'get_aegis_pool_state',
235
+ 'get_aegis_vault_state',
236
+ 'get_curve_pools',
237
+ 'get_vaults_fyi_vaults',
238
+ 'get_yield_xyz_opportunities',
239
+ ],
240
+ orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
241
+ laneCompatibility:
242
+ 'Generic earn writes use deposit_earn_position / withdraw_earn_position on normal Thirdfy policy/delegation rails; routing selects Morpho, Curve, Vaults.fyi, or Yield.xyz adapters per provider/opportunity.',
243
+ };
244
+ }
245
+ if (provider === 'capyfi') {
246
+ return {
247
+ provider,
248
+ category: 'earn',
249
+ canonicalReadAction: 'get_capyfi_markets',
250
+ canonicalWriteAction: 'deposit_earn_position',
251
+ readActions: ['get_capyfi_markets', 'get_earn_opportunities', 'get_earn_position'],
252
+ orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
253
+ supportedChains: [1, 480],
254
+ laneCompatibility: 'CapyFi Compound-v2 supply on Ethereum and World Chain; Gnosis pending deployment addresses.',
255
+ example:
256
+ 'thirdfy-agent run --action deposit_earn_position --params \'{"providerId":"capyfi","opportunityId":"capyfi-1-usdc","amount":"100","chainId":1,"tokenAddress":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}\'',
257
+ };
258
+ }
259
+ if (provider === 'fx') {
260
+ return {
261
+ provider,
262
+ category: 'fx',
263
+ canonicalReadAction: 'get_fx_supported_assets',
264
+ readActions: ['get_fx_supported_assets', 'quote_fx_swap', 'get_monerium_profile_status'],
265
+ supportedChains: [100],
266
+ laneCompatibility:
267
+ 'Gnosis FX: quote_fx_swap is read-only; on-chain execution uses the trading/cow path on chain 100. Monerium IBAN flows need credentials.',
268
+ };
269
+ }
270
+ if (provider === 'morpho') {
271
+ return {
272
+ provider,
273
+ category: 'earn',
274
+ canonicalReadAction: 'get_morpho_vaults',
275
+ canonicalWriteAction: 'deposit_earn_position',
276
+ readActions: ['get_morpho_vaults', 'get_earn_provider', 'get_earn_position'],
277
+ orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
278
+ supportedChains: [8453, 84532],
279
+ laneCompatibility: 'Morpho deposits require ERC-20 approvals to the vault/market routes surfaced by the catalog before deposit_earn_position.',
280
+ example:
281
+ 'thirdfy-agent actions --provider morpho && thirdfy-agent run --action deposit_earn_position --params \'{"providerId":"morpho","vaultAddress":"0x...","tokenAddress":"0x...","amount":"10","chainId":8453}\'',
282
+ };
283
+ }
284
+ if (provider === 'aegis') {
285
+ return {
286
+ provider,
287
+ category: 'earn',
288
+ canonicalReadAction: 'get_aegis_pools',
289
+ canonicalWriteAction: 'deposit_aegis_lender',
290
+ readActions: ['get_earn_opportunities', 'get_aegis_pools', 'get_aegis_pool_state', 'get_aegis_vault_state', 'get_earn_provider'],
291
+ orderManagementActions: [
292
+ 'deposit_aegis_lender',
293
+ 'redeem_aegis_lender',
294
+ 'open_aegis_vault_and_borrow',
295
+ 'repay_aegis_vault',
296
+ 'stabilize_aegis_vault',
297
+ ],
298
+ supportedChains: [8453, 130],
299
+ laneCompatibility:
300
+ 'Aegis reads are live on Base allowlisted pools and Unichain DFM defaults. Engine writes use AegisRouterV1 with router-only approvals and eth_call simulation; writes stay fail-closed until partner addresses, PoolKeys, Engine ABI/opcodes, and audit sign-off. Use agent_wallet / walletExecute for solo Hermes — not execute-intent.',
301
+ example:
302
+ 'thirdfy-agent actions --provider aegis --chain-id 8453 --json && thirdfy-agent run --action get_aegis_pool_state --run-mode agent_wallet --params \'{"pool":"tfy-usdc","chainId":8453}\'',
303
+ };
304
+ }
305
+ if (provider === 'curve') {
306
+ return {
307
+ provider,
308
+ category: 'earn',
309
+ canonicalReadAction: 'get_curve_pools',
310
+ canonicalWriteAction: 'deposit_earn_position',
311
+ readActions: ['get_curve_pools', 'get_earn_opportunities', 'get_earn_provider', 'get_earn_position'],
312
+ orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
313
+ supportedChains: [8453],
314
+ laneCompatibility:
315
+ 'Curve EVM writes are limited to the validated Base mainnet 4pool adapter; other pools remain discovery-only until per-pool calldata is validated.',
316
+ };
317
+ }
318
+ if (provider === 'vaults-fyi') {
319
+ return {
320
+ provider,
321
+ category: 'earn',
322
+ canonicalReadAction: 'get_vaults_fyi_vaults',
323
+ canonicalWriteAction: 'deposit_earn_position',
324
+ readActions: ['get_vaults_fyi_vaults', 'get_earn_opportunities', 'get_earn_provider', 'get_earn_position'],
325
+ orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
326
+ laneCompatibility:
327
+ 'Vaults.fyi writes are transaction-prep first. Thirdfy validates partner payloads, chain/user context, and allowed targets before returning or submitting prepared calls.',
328
+ example:
329
+ 'thirdfy-agent actions --provider vaults-fyi && thirdfy-agent preflight --action deposit_earn_position --params \'{"providerId":"vaults-fyi","opportunityId":"...","vaultAddress":"0x...","tokenAddress":"0x...","amount":"10","chainId":8453}\'',
330
+ };
331
+ }
332
+ if (provider === 'yield-xyz') {
333
+ return {
334
+ provider,
335
+ category: 'earn',
336
+ canonicalReadAction: 'get_yield_xyz_opportunities',
337
+ canonicalWriteAction: 'deposit_earn_position',
338
+ readActions: ['get_yield_xyz_opportunities', 'get_earn_opportunities', 'get_earn_provider', 'get_earn_position'],
339
+ orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
340
+ laneCompatibility:
341
+ 'Yield.xyz writes are transaction-prep first. Thirdfy calls Yield.xyz directly and validates returned unsigned transactions with @yieldxyz/shield before signing or returning prepared calls.',
342
+ example:
343
+ 'thirdfy-agent actions --provider yield-xyz && thirdfy-agent preflight --action deposit_earn_position --params \'{"providerId":"yield-xyz","opportunityId":"ethereum-eth-lido-staking","tokenAddress":"0x...","amount":"0.01","chainId":1}\'',
344
+ };
345
+ }
346
+ return null;
347
+ }
348
+
349
+ function getCachedProviderHintFromCapabilities(provider) {
350
+ const normalizedProvider = String(provider || '').trim().toLowerCase();
351
+ if (!normalizedProvider) return null;
352
+ const manifests = Array.isArray(getNegotiatedCapabilitiesCache()?.providers?.manifests)
353
+ ? getNegotiatedCapabilitiesCache().providers.manifests
354
+ : [];
355
+ const match = manifests.find((entry) => String(entry?.providerId || '').trim().toLowerCase() === normalizedProvider);
356
+ if (!match) return null;
357
+ const canonicalWriteActions = {
358
+ trading: 'swap',
359
+ hyperliquid: 'place_hyperliquid_perps_order',
360
+ polymarket: 'place_polymarket_order',
361
+ prediction: 'place_prediction_order',
362
+ earn: 'deposit_earn_position',
363
+ morpho: 'deposit_earn_position',
364
+ aegis: 'deposit_aegis_lender',
365
+ curve: 'deposit_earn_position',
366
+ 'vaults-fyi': 'deposit_earn_position',
367
+ 'yield-xyz': 'deposit_earn_position',
368
+ };
369
+ const canonicalReadActions = {
370
+ earn: 'get_earn_opportunities',
371
+ morpho: 'get_morpho_vaults',
372
+ aegis: 'get_aegis_pools',
373
+ curve: 'get_curve_pools',
374
+ 'vaults-fyi': 'get_vaults_fyi_vaults',
375
+ 'yield-xyz': 'get_yield_xyz_opportunities',
376
+ prediction: 'get_prediction_markets',
377
+ };
378
+ return {
379
+ provider: normalizedProvider,
380
+ category: match.category || match.domain || match.providerKind || null,
381
+ ...(canonicalReadActions[normalizedProvider] ? { canonicalReadAction: canonicalReadActions[normalizedProvider] } : {}),
382
+ ...(canonicalWriteActions[normalizedProvider] ? { canonicalWriteAction: canonicalWriteActions[normalizedProvider] } : {}),
383
+ providerKind: match.providerKind || null,
384
+ capabilities: Array.isArray(match.capabilities) ? match.capabilities : [],
385
+ supportedChains: Array.isArray(match.supportedChains) ? match.supportedChains : [],
386
+ custodyMode: match.custodyMode || null,
387
+ description: match.description || null,
388
+ source: 'cli_capabilities_manifest',
389
+ };
390
+ }
391
+
392
+ function formatProviderAmbiguousActionHint(providerHint) {
393
+ if (!providerHint) return '';
394
+ const p = providerHint.provider;
395
+ const w = providerHint.canonicalWriteAction;
396
+ if (w) return ` Provider ${p} expects ${w}.`;
397
+ return ` Provider ${p} did not declare a canonical write action in capabilities; run actions --provider ${p} first.`;
398
+ }
399
+
400
+ function formatProviderUnknownActionHint(providerHint) {
401
+ if (!providerHint) return '';
402
+ const p = providerHint.provider;
403
+ const w = providerHint.canonicalWriteAction;
404
+ if (w) return ` Provider ${p} expects ${w}; run actions --provider ${p} first.`;
405
+ return ` Provider ${p} did not declare a canonical write action in capabilities; run actions --provider ${p} first.`;
406
+ }
407
+
408
+ return {
409
+ getTradingProviderHint,
410
+ getCachedProviderHintFromCapabilities,
411
+ formatProviderAmbiguousActionHint,
412
+ formatProviderUnknownActionHint,
413
+ };
414
+ }