@thirdfy/agent-cli 0.1.31 → 0.1.33

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.1.33] - 2026-05-12
8
+
9
+ ### Added
10
+
11
+ - **Bitfinex CEX delegation:** Permission profiles for funding vs spot, `--permission-profile` on credential upsert, onboarding hints when credentials are missing from Thirdfy delegation context, and `get-bitfinex-ledgers-hist` read-check documentation.
12
+
13
+ ### Changed
14
+
15
+ - **DogeOS provider discovery:** Tests and docs aligned with catalog-driven provider hints.
16
+
17
+ ## [0.1.32] - 2026-05-08
18
+
19
+ ### Added
20
+
21
+ - **Earn partners (Vaults.fyi and Yield.xyz):** `thirdfy-agent actions --provider vaults-fyi|yield-xyz` returns catalog-aligned hints (`get_vaults_fyi_vaults` / `get_yield_xyz_opportunities`, generic Earn reads, `deposit_earn_position` / `withdraw_earn_position`). Provider inference and `--provider earn` filtering include these providers alongside Morpho and Curve.
22
+
23
+ ### Changed
24
+
25
+ - Public docs (`docs/command-reference.md`, `docs/action-inventory-and-compatibility.md`, `docs/providers/index.md`) describe partner earn discovery and transaction-prep-first writes.
26
+ - **`--provider earn` hints:** Umbrella `readActions` lists Morpho and Curve discovery actions (`get_morpho_vaults`, `get_curve_pools`) together with partner reads so Earn hints stay symmetric across sub-providers.
27
+
7
28
  ## [0.1.31] - 2026-05-07
8
29
 
9
30
  ### Fixed
@@ -19,9 +19,23 @@ const DEFAULT_API_BASE = 'https://api.thirdfy.com';
19
19
  const DEFAULT_TIMEOUT_MS = 30000;
20
20
  const DEFAULT_CATALOG_CACHE_TTL_MS = 60_000;
21
21
  const DEFAULT_BITFINEX_BASE_URL = 'https://api.bitfinex.com';
22
- const BITFINEX_REQUIRED_SCOPE_PERMISSIONS = {
23
- funding: { read: 1, write: 1 },
24
- wallets: { read: 1, write: 1 },
22
+ const BITFINEX_PERMISSION_PROFILES = {
23
+ funding: {
24
+ id: 'funding',
25
+ credentialRef: 'bitfinex:funding-primary',
26
+ required: {
27
+ funding: { read: 1, write: 1 },
28
+ wallets: { read: 1, write: 0 },
29
+ },
30
+ },
31
+ spot: {
32
+ id: 'spot',
33
+ credentialRef: 'bitfinex:spot-primary',
34
+ required: {
35
+ orders: { read: 1, write: 1 },
36
+ wallets: { read: 1, write: 0 },
37
+ },
38
+ },
25
39
  };
26
40
  const RUN_MODES = ['thirdfy', 'self', 'hybrid', 'agent_wallet'];
27
41
  const HYBRID_WALLET_MODES = ['self', 'agent_wallet'];
@@ -79,15 +93,21 @@ main().catch((error) => {
79
93
 
80
94
  function buildTopLevelOnboardingHint(payload) {
81
95
  const reason = String(payload?.blockedReason || payload?.error || '').trim();
96
+ const permissionProfile = inferBitfinexPermissionProfileFromAction(
97
+ payload?.resolvedAction || payload?.action || payload?.requestedAction
98
+ );
82
99
  if (!reason) return null;
83
100
  if (
84
- ['CEX_CREDENTIAL_NOT_FOUND', 'BITFINEX_CREDENTIALS_NOT_IN_CONTEXT', 'BITFINEX_ENV_CREDENTIALS_NOT_CONFIGURED'].includes(
85
- reason
86
- )
101
+ [
102
+ 'CEX_CREDENTIAL_NOT_FOUND',
103
+ 'BITFINEX_CREDENTIALS_NOT_IN_CONTEXT',
104
+ 'BITFINEX_CREDENTIALS_NOT_IN_THIRDFY_DELEGATION_CONTEXT',
105
+ 'BITFINEX_ENV_CREDENTIALS_NOT_CONFIGURED',
106
+ ].includes(reason)
87
107
  ) {
88
108
  return {
89
109
  type: 'bitfinex_credentials',
90
- command: buildBitfinexOnboardingCommand(),
110
+ command: buildBitfinexOnboardingCommand(permissionProfile),
91
111
  };
92
112
  }
93
113
  return null;
@@ -530,10 +550,18 @@ function getTradingProviderHint(providerId) {
530
550
  category: 'earn',
531
551
  canonicalReadAction: 'get_earn_opportunities',
532
552
  canonicalWriteAction: 'deposit_earn_position',
533
- readActions: ['get_earn_opportunities', 'get_earn_provider', 'get_earn_position'],
553
+ readActions: [
554
+ 'get_earn_opportunities',
555
+ 'get_earn_provider',
556
+ 'get_earn_position',
557
+ 'get_morpho_vaults',
558
+ 'get_curve_pools',
559
+ 'get_vaults_fyi_vaults',
560
+ 'get_yield_xyz_opportunities',
561
+ ],
534
562
  orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
535
563
  laneCompatibility:
536
- 'Generic earn writes use deposit_earn_position / withdraw_earn_position on normal Thirdfy policy/delegation rails; routing selects Morpho vault calldata or the validated Curve Base 4pool adapter on Base (8453) per opportunity.',
564
+ '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.',
537
565
  };
538
566
  }
539
567
  if (provider === 'morpho') {
@@ -563,6 +591,34 @@ function getTradingProviderHint(providerId) {
563
591
  'Curve EVM writes are limited to the validated Base mainnet 4pool adapter; other pools remain discovery-only until per-pool calldata is validated.',
564
592
  };
565
593
  }
594
+ if (provider === 'vaults-fyi') {
595
+ return {
596
+ provider,
597
+ category: 'earn',
598
+ canonicalReadAction: 'get_vaults_fyi_vaults',
599
+ canonicalWriteAction: 'deposit_earn_position',
600
+ readActions: ['get_vaults_fyi_vaults', 'get_earn_opportunities', 'get_earn_provider', 'get_earn_position'],
601
+ orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
602
+ laneCompatibility:
603
+ 'Vaults.fyi writes are transaction-prep first. Thirdfy validates partner payloads, chain/user context, and allowed targets before returning or submitting prepared calls.',
604
+ example:
605
+ '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}\'',
606
+ };
607
+ }
608
+ if (provider === 'yield-xyz') {
609
+ return {
610
+ provider,
611
+ category: 'earn',
612
+ canonicalReadAction: 'get_yield_xyz_opportunities',
613
+ canonicalWriteAction: 'deposit_earn_position',
614
+ readActions: ['get_yield_xyz_opportunities', 'get_earn_opportunities', 'get_earn_provider', 'get_earn_position'],
615
+ orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
616
+ laneCompatibility:
617
+ '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.',
618
+ example:
619
+ '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}\'',
620
+ };
621
+ }
566
622
  return null;
567
623
  }
568
624
 
@@ -582,11 +638,15 @@ function getCachedProviderHintFromCapabilities(provider) {
582
638
  earn: 'deposit_earn_position',
583
639
  morpho: 'deposit_earn_position',
584
640
  curve: 'deposit_earn_position',
641
+ 'vaults-fyi': 'deposit_earn_position',
642
+ 'yield-xyz': 'deposit_earn_position',
585
643
  };
586
644
  const canonicalReadActions = {
587
645
  earn: 'get_earn_opportunities',
588
646
  morpho: 'get_morpho_vaults',
589
647
  curve: 'get_curve_pools',
648
+ 'vaults-fyi': 'get_vaults_fyi_vaults',
649
+ 'yield-xyz': 'get_yield_xyz_opportunities',
590
650
  prediction: 'get_prediction_markets',
591
651
  };
592
652
  return {
@@ -1565,12 +1625,17 @@ async function commandDelegationRedeem(ctx, flags, capabilities) {
1565
1625
  async function commandCredentialsUpsert(ctx, flags, capabilities) {
1566
1626
  const authToken = getAuthToken(flags, 'Missing --auth-token (or THIRDFY_AUTH_TOKEN) for credentials upsert');
1567
1627
  const venue = requireFlag(flags, 'venue', 'Missing --venue');
1628
+ const permissionProfile = String(flags.permissionProfile || 'funding').trim().toLowerCase();
1629
+ const bitfinexProfile =
1630
+ String(venue).trim().toLowerCase() === 'bitfinex'
1631
+ ? BITFINEX_PERMISSION_PROFILES[permissionProfile] || BITFINEX_PERMISSION_PROFILES.funding
1632
+ : null;
1568
1633
  const payload = {
1569
1634
  venue,
1570
1635
  apiKey: requireFlag(flags, 'apiKey', 'Missing --api-key'),
1571
1636
  apiSecret: requireFlag(flags, 'apiSecret', 'Missing --api-secret'),
1572
1637
  credentialType: String(flags.credentialType || 'api_key'),
1573
- credentialRef: String(flags.credentialRef || ''),
1638
+ credentialRef: String(flags.credentialRef || bitfinexProfile?.credentialRef || ''),
1574
1639
  };
1575
1640
  if (flags.passphrase) payload.passphrase = String(flags.passphrase);
1576
1641
  if (flags.metadata) payload.metadata = parseJsonFlag(flags, 'metadata');
@@ -1586,6 +1651,7 @@ async function commandCredentialsUpsert(ctx, flags, capabilities) {
1586
1651
  permissionProbe = await probeBitfinexPermissions({
1587
1652
  apiKey: payload.apiKey,
1588
1653
  apiSecret: payload.apiSecret,
1654
+ permissionProfile: bitfinexProfile?.id || 'funding',
1589
1655
  timeoutMs: ctx.timeoutMs,
1590
1656
  baseUrl: String(flags.bitfinexBaseUrl || process.env.BITFINEX_BASE_URL || DEFAULT_BITFINEX_BASE_URL),
1591
1657
  });
@@ -2681,11 +2747,14 @@ function shouldUseBuildTxExecution(runMode, resolved) {
2681
2747
  return true;
2682
2748
  }
2683
2749
 
2684
- function buildBitfinexOnboardingCommand() {
2750
+ function buildBitfinexOnboardingCommand(permissionProfile = 'funding') {
2751
+ const profile = BITFINEX_PERMISSION_PROFILES[permissionProfile] || BITFINEX_PERMISSION_PROFILES.funding;
2685
2752
  return [
2686
2753
  'thirdfy-agent credentials upsert',
2687
2754
  '--auth-token "$THIRDFY_AUTH_TOKEN"',
2688
2755
  '--venue bitfinex',
2756
+ `--permission-profile ${profile.id}`,
2757
+ `--credential-ref ${profile.credentialRef}`,
2689
2758
  '--api-key "<BITFINEX_API_KEY>"',
2690
2759
  '--api-secret "<BITFINEX_API_SECRET>"',
2691
2760
  '--verify-permissions',
@@ -2693,6 +2762,15 @@ function buildBitfinexOnboardingCommand() {
2693
2762
  ].join(' ');
2694
2763
  }
2695
2764
 
2765
+ function inferBitfinexPermissionProfileFromAction(actionName) {
2766
+ const action = String(actionName || '')
2767
+ .trim()
2768
+ .toLowerCase()
2769
+ .replace(/[_\s]+/g, '-');
2770
+ if (!action.includes('bitfinex')) return 'funding';
2771
+ return action.includes('bitfinex-order') ? 'spot' : 'funding';
2772
+ }
2773
+
2696
2774
  function applyExecutionFallbackHints(normalized, { flags, runMode, resolvedAction }) {
2697
2775
  if (!normalized || typeof normalized !== 'object') return normalized;
2698
2776
  const next = { ...normalized };
@@ -2705,9 +2783,11 @@ function applyExecutionFallbackHints(normalized, { flags, runMode, resolvedActio
2705
2783
  }
2706
2784
  const resolvedActionLower = String(resolvedAction || '').trim().toLowerCase();
2707
2785
  const looksBitfinexAction = resolvedActionLower.includes('bitfinex');
2786
+ const permissionProfile = inferBitfinexPermissionProfileFromAction(resolvedActionLower);
2708
2787
  const cexCredentialMissingSignals = new Set([
2709
2788
  'CEX_CREDENTIAL_NOT_FOUND',
2710
2789
  'BITFINEX_CREDENTIALS_NOT_IN_CONTEXT',
2790
+ 'BITFINEX_CREDENTIALS_NOT_IN_THIRDFY_DELEGATION_CONTEXT',
2711
2791
  'BITFINEX_ENV_CREDENTIALS_NOT_CONFIGURED',
2712
2792
  ]);
2713
2793
  const hasMissingCredentialSignal = Array.from(reasons).some((code) => cexCredentialMissingSignals.has(code));
@@ -2716,7 +2796,7 @@ function applyExecutionFallbackHints(normalized, { flags, runMode, resolvedActio
2716
2796
  type: 'bitfinex_credentials',
2717
2797
  message:
2718
2798
  'Bitfinex credentials are missing for this delegated account. Onboard credentials first, then retry the same command.',
2719
- command: buildBitfinexOnboardingCommand(),
2799
+ command: buildBitfinexOnboardingCommand(permissionProfile),
2720
2800
  };
2721
2801
  }
2722
2802
  if (
@@ -3434,6 +3514,9 @@ function inferActionProvider(action) {
3434
3514
  'get-curve-pool-details': 'curve',
3435
3515
  'deposit-curve-pool': 'curve',
3436
3516
  'withdraw-curve-pool': 'curve',
3517
+ 'get-vaults-fyi-vaults': 'vaults-fyi',
3518
+ 'get-vaults-fyi-opportunities': 'vaults-fyi',
3519
+ 'get-yield-xyz-opportunities': 'yield-xyz',
3437
3520
  'get-prediction-markets': 'polymarket',
3438
3521
  'get-prediction-market': 'polymarket',
3439
3522
  'get-prediction-order-book': 'polymarket',
@@ -3449,9 +3532,11 @@ function inferActionProvider(action) {
3449
3532
  if (knownMap[key]) return knownMap[key];
3450
3533
  if (key.includes('hyperliquid')) return 'hyperliquid';
3451
3534
  if (key.includes('polymarket') || key.includes('prediction')) return 'polymarket';
3535
+ if (key.includes('vaults-fyi')) return 'vaults-fyi';
3536
+ if (key.includes('yield-xyz')) return 'yield-xyz';
3452
3537
  if (key.includes('morpho')) return 'morpho';
3453
3538
  if (key.includes('curve')) return 'curve';
3454
- if (key.includes('earn')) return 'earn';
3539
+ if (key.includes('earn') || key.includes('yield') || key.includes('vault')) return 'earn';
3455
3540
  if (key.includes('bridge')) return 'bridge';
3456
3541
  if (key.includes('dogeos-barkswap')) return 'dogeos-barkswap';
3457
3542
  if (key.includes('dogeos-laika')) return 'dogeos-laika';
@@ -3500,9 +3585,9 @@ function actionMatchesProvider(action, requestedProvider, allActions) {
3500
3585
  if (requestedProvider === 'bridge') return isProviderActionKey(action, 'bridge');
3501
3586
  if (requestedProvider.startsWith('dogeos')) return isProviderActionKey(action, requestedProvider);
3502
3587
  if (requestedProvider === 'earn') {
3503
- return provider === 'earn' || provider === 'morpho' || provider === 'curve' || isEarnCatalogAction(action);
3588
+ return provider === 'earn' || provider === 'morpho' || provider === 'curve' || provider === 'vaults-fyi' || provider === 'yield-xyz' || isEarnCatalogAction(action);
3504
3589
  }
3505
- if (requestedProvider === 'morpho' || requestedProvider === 'curve') {
3590
+ if (requestedProvider === 'morpho' || requestedProvider === 'curve' || requestedProvider === 'vaults-fyi' || requestedProvider === 'yield-xyz') {
3506
3591
  const hasProtocolSpecificEarnRows = allActions.some((candidate) => getActionProviderWithInference(candidate) === requestedProvider);
3507
3592
  return isProviderActionKey(action, requestedProvider) || (!hasProtocolSpecificEarnRows && isEarnCatalogAction(action));
3508
3593
  }
@@ -4015,9 +4100,10 @@ function parseBitfinexPermissionRows(rawData) {
4015
4100
  return parsed;
4016
4101
  }
4017
4102
 
4018
- function getMissingBitfinexScopes(scopePermissions) {
4103
+ function getMissingBitfinexScopes(scopePermissions, permissionProfile = 'funding') {
4104
+ const profile = BITFINEX_PERMISSION_PROFILES[permissionProfile] || BITFINEX_PERMISSION_PROFILES.funding;
4019
4105
  const missing = [];
4020
- for (const [scope, required] of Object.entries(BITFINEX_REQUIRED_SCOPE_PERMISSIONS)) {
4106
+ for (const [scope, required] of Object.entries(profile.required)) {
4021
4107
  const actual = scopePermissions[scope] || { read: 0, write: 0 };
4022
4108
  if (actual.read < required.read || actual.write < required.write) {
4023
4109
  missing.push({
@@ -4030,7 +4116,7 @@ function getMissingBitfinexScopes(scopePermissions) {
4030
4116
  return missing;
4031
4117
  }
4032
4118
 
4033
- async function probeBitfinexPermissions({ apiKey, apiSecret, timeoutMs, baseUrl }) {
4119
+ async function probeBitfinexPermissions({ apiKey, apiSecret, permissionProfile = 'funding', timeoutMs, baseUrl }) {
4034
4120
  const normalizedBaseUrl = String(baseUrl || DEFAULT_BITFINEX_BASE_URL).trim().replace(/\/+$/, '');
4035
4121
  const apiPath = '/v2/auth/r/permissions';
4036
4122
  const nonce = Date.now().toString();
@@ -4081,18 +4167,19 @@ async function probeBitfinexPermissions({ apiKey, apiSecret, timeoutMs, baseUrl
4081
4167
  );
4082
4168
  }
4083
4169
  const scopePermissions = parseBitfinexPermissionRows(parsed);
4084
- const missingScopes = getMissingBitfinexScopes(scopePermissions);
4170
+ const missingScopes = getMissingBitfinexScopes(scopePermissions, permissionProfile);
4085
4171
  if (missingScopes.length > 0) {
4086
4172
  throw createCliError(
4087
4173
  'BITFINEX_PERMISSION_SCOPE_MISSING',
4088
4174
  `Bitfinex key is missing required scopes (${missingScopes
4089
4175
  .map((entry) => entry.scope)
4090
- .join(', ')}). Enable funding+wallets read/write and retry onboarding.`,
4091
- { missingScopes, scopePermissions }
4176
+ .join(', ')}). Enable the ${permissionProfile} profile scopes and retry onboarding.`,
4177
+ { missingScopes, scopePermissions, permissionProfile }
4092
4178
  );
4093
4179
  }
4094
4180
  return {
4095
4181
  venue: 'bitfinex',
4182
+ permissionProfile,
4096
4183
  checked: true,
4097
4184
  scopes: scopePermissions,
4098
4185
  missingScopes: [],
@@ -4156,7 +4243,7 @@ Core commands:
4156
4243
  thirdfy-agent wallet sign --agent-api-key <key> --action <id> --params <json> [--chain-id <id>] [--json]
4157
4244
  thirdfy-agent wallet submit --signed-tx-hex <hex> [--chain-id <id>] [--rpc-url <url>] [--json]
4158
4245
  thirdfy-agent agent run --agent-api-key <key> --action <id> --params <json> [--signer-method managed_wallet_server|byow|fanout_intent] [--strategy-intent single_wallet|fanout] [--run-mode <mode>] [--user-did <did>] [--json]
4159
- thirdfy-agent credentials upsert --auth-token <token> --venue <id> --api-key <key> --api-secret <secret> [--verify-permissions] [--skip-permissions-probe] [--bitfinex-base-url <url>] [--json]
4246
+ thirdfy-agent credentials upsert --auth-token <token> --venue <id> --api-key <key> --api-secret <secret> [--permission-profile funding|spot] [--credential-ref <ref>] [--verify-permissions] [--skip-permissions-probe] [--bitfinex-base-url <url>] [--json]
4160
4247
  thirdfy-agent credentials status --auth-token <token> [--venue <id>] [--json]
4161
4248
  thirdfy-agent agent auth challenge --agent-key <key> [--wallet-address <addr>] [--chain-id <id>] [--json]
4162
4249
  thirdfy-agent agent auth verify --challenge-id <id> --signature <hex> --agent-key <key> [--wallet-address <addr>] [--json]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdfy/agent-cli",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
5
5
  "type": "module",
6
6
  "bin": {