@thirdfy/agent-cli 0.2.72 → 0.2.73
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 +10 -0
- package/README.md +4 -3
- package/package.json +1 -1
- package/src/core/actionTimeouts.mjs +5 -0
- package/src/core/readOnlyActions.mjs +1 -0
- package/src/core/walletExecuteChainId.mjs +49 -0
- package/src/runtime/actions/selection.mjs +2 -0
- package/src/runtime/execution/payloads.mjs +8 -2
- package/src/runtime/execution/runners.mjs +9 -5
- package/src/runtime/providerHints.mjs +31 -24
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.2.73] - 2026-09-21
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Hyperliquid Circle CCTP ids: `quote_hyperliquid_cctp_deposit` (read) and `deposit_hyperliquid_cctp` (write, 180s timeout). Managed execute signs those on `params.sourceChainId` and fails closed without it. Provider hints follow API `setupHints` and `get_hyperliquid_funding_status` `nextAction` instead of hardcoded Arbitrum / Li.Fi. Pin: `test/walletExecuteChainId.test.cjs`, `test/read-only-action-classifier.test.cjs`. Pairs with `thirdfy-mcp` **0.0.123**.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Managed execute signs `execute_bridge` / `get_bridge_quote` on `params.fromChain` even when `--chain-id` defaulted to Base. Preflight uses the same chain resolver as the execute payload. `execute_bridge`, `get_bridge_quote`, and `swap` use the 180s timeout. Pin: `test/walletExecuteChainId.test.cjs`, `test/actionTimeouts.test.cjs`. Pairs with `thirdfy-mcp` **0.0.123**.
|
|
16
|
+
|
|
7
17
|
## [0.2.72] - 2026-09-17
|
|
8
18
|
|
|
9
19
|
### Added
|
package/README.md
CHANGED
|
@@ -40,9 +40,10 @@ Run without global install:
|
|
|
40
40
|
npx @thirdfy/agent-cli --help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
## What's new in v0.2.
|
|
43
|
+
## What's new in v0.2.73
|
|
44
44
|
|
|
45
|
-
- Hyperliquid
|
|
45
|
+
- Hyperliquid Circle CCTP ids `quote_hyperliquid_cctp_deposit` and `deposit_hyperliquid_cctp`. Managed execute signs those on `params.sourceChainId` with no default chain.
|
|
46
|
+
- Provider hints follow API `setupHints` and `get_hyperliquid_funding_status` `nextAction` instead of hardcoded Arbitrum / Li.Fi.
|
|
46
47
|
- See [CHANGELOG.md](./CHANGELOG.md) and GitHub Releases for older versions.
|
|
47
48
|
|
|
48
49
|
## Quick start
|
|
@@ -197,7 +198,7 @@ Auth expectations by mode:
|
|
|
197
198
|
- `hybrid`: requires execution identity and governed mirror semantics (`--hybrid-wallet-mode self|agent_wallet`, default `self`).
|
|
198
199
|
- `thirdfy`: requires execution identity and delegated governance path.
|
|
199
200
|
- `agent_wallet`: requires execution identity and owner `userDid`, but does not require subscriber delegation when the owner identity matches the agent wallet/creator. Thirdfy credits/free quota still gate execution; Privy gas sponsorship only covers network gas when enabled.
|
|
200
|
-
- Hyperliquid setup uses `complete_hyperliquid_onboarding` for runtime-owned funding/setup.
|
|
201
|
+
- Hyperliquid setup uses `complete_hyperliquid_onboarding` for runtime-owned funding/setup. Call `get_hyperliquid_funding_status` and follow `nextAction` plus `nextActionParams`. Do not invent a funding chain. Pass **`--estimated-amount-usd`** as a top-level flag (not inside `--params`). The normal solo `agent_wallet` path uses the managed execution wallet as the Hyperliquid main wallet. If margin already sits on a different Hyperliquid main wallet, pass that wallet as `mainWalletAddress` / order `userAddress`, and pass the approved execution/API wallet as `apiWalletAddress`. When USDC sits on your linked email-login wallet (`primaryEvmWallet`) instead of the managed execution wallet, Thirdfy API returns **`FUND_AGENT_EXECUTION_WALLET`** with `fundedWallet` and `executionWalletAddress`; move USDC to `executionWalletAddress` on the chain named by `nextActionParams.sourceChainId` and retry.
|
|
201
202
|
- custody mode defaults are profile-aware:
|
|
202
203
|
- `managed` default for `thirdfy`
|
|
203
204
|
- `external` default for `self` and `hybrid`
|
package/package.json
CHANGED
|
@@ -16,6 +16,11 @@ const LONG_RUNNING_ACTIONS = new Set([
|
|
|
16
16
|
'get_cmc_perp_contract_analysis',
|
|
17
17
|
'get_cmc_market_regime',
|
|
18
18
|
'get_cmc_derivatives_crowding',
|
|
19
|
+
'execute_bridge',
|
|
20
|
+
'get_bridge_quote',
|
|
21
|
+
'swap',
|
|
22
|
+
'execute_optimal_swap',
|
|
23
|
+
'deposit_hyperliquid_cctp',
|
|
19
24
|
]);
|
|
20
25
|
|
|
21
26
|
function normalizeActionId(action) {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const DEFAULT_CHAIN_ID = 8453;
|
|
2
|
+
|
|
3
|
+
function normalizeActionId(action) {
|
|
4
|
+
return String(action || '')
|
|
5
|
+
.trim()
|
|
6
|
+
.replace(/-/g, '_')
|
|
7
|
+
.toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function positiveChainId(value) {
|
|
11
|
+
const n = Number(value);
|
|
12
|
+
if (!Number.isFinite(n) || n <= 0) return undefined;
|
|
13
|
+
return Math.trunc(n);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isBridgeAction(action) {
|
|
17
|
+
const key = normalizeActionId(action);
|
|
18
|
+
return key === 'execute_bridge' || key === 'get_bridge_quote' || key === 'check_bridge_status';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isHyperliquidCctpAction(action) {
|
|
22
|
+
const key = normalizeActionId(action);
|
|
23
|
+
return key === 'deposit_hyperliquid_cctp' || key === 'quote_hyperliquid_cctp_deposit';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Signing / wallet-preflight chain for managed execute.
|
|
28
|
+
* Bridge writes sign on fromChain. CCTP burns sign on sourceChainId. Do not default Base.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveWalletExecuteChainId({ action, chainId, params } = {}) {
|
|
31
|
+
const record = params && typeof params === 'object' ? params : {};
|
|
32
|
+
if (isHyperliquidCctpAction(action)) {
|
|
33
|
+
const sourceChainId = positiveChainId(record.sourceChainId);
|
|
34
|
+
if (!sourceChainId) {
|
|
35
|
+
throw new Error('HYPERLIQUID_CCTP_SOURCE_CHAIN_REQUIRED');
|
|
36
|
+
}
|
|
37
|
+
return sourceChainId;
|
|
38
|
+
}
|
|
39
|
+
if (isBridgeAction(action)) {
|
|
40
|
+
return (
|
|
41
|
+
positiveChainId(record.fromChain) ||
|
|
42
|
+
positiveChainId(record.fromChainId) ||
|
|
43
|
+
positiveChainId(chainId) ||
|
|
44
|
+
positiveChainId(record.chainId) ||
|
|
45
|
+
DEFAULT_CHAIN_ID
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return positiveChainId(chainId) || positiveChainId(record.chainId) || DEFAULT_CHAIN_ID;
|
|
49
|
+
}
|
|
@@ -93,6 +93,8 @@ function inferActionProvider(action) {
|
|
|
93
93
|
'get-hyperliquid-referral-status': 'hyperliquid',
|
|
94
94
|
'deposit-hyperliquid-staking': 'hyperliquid',
|
|
95
95
|
'deposit-hyperliquid-bridge': 'hyperliquid',
|
|
96
|
+
'deposit-hyperliquid-cctp': 'hyperliquid',
|
|
97
|
+
'quote-hyperliquid-cctp-deposit': 'hyperliquid',
|
|
96
98
|
'place-hyperliquid-perps-order': 'hyperliquid',
|
|
97
99
|
'cancel-hyperliquid-perps-order': 'hyperliquid',
|
|
98
100
|
'update-hyperliquid-leverage': 'hyperliquid',
|
|
@@ -7,6 +7,7 @@ import { createCliError } from '../../core/envelope.mjs';
|
|
|
7
7
|
import { normalizeRunMode, normalizeManagedRunMode, normalizeHybridWalletMode } from '../../core/runMode.mjs';
|
|
8
8
|
import { assertHostedRuntimePolymarketWriteIdempotency } from './hostedRuntimeWriteGate.mjs';
|
|
9
9
|
import { resolveTokenInForFunding } from '../../core/fundingTokenIn.mjs';
|
|
10
|
+
import { resolveWalletExecuteChainId } from '../../core/walletExecuteChainId.mjs';
|
|
10
11
|
|
|
11
12
|
function isTruthyFlag(value) {
|
|
12
13
|
return value === true || String(value || '').trim().toLowerCase() === 'true';
|
|
@@ -129,16 +130,21 @@ function buildManagedExecutePayload(flags, options) {
|
|
|
129
130
|
delete managedParams.tokenInDecimals;
|
|
130
131
|
managedParams.amountIn = flags.__swapInput.amountInHuman;
|
|
131
132
|
}
|
|
133
|
+
const action = String(options.resolvedAction || requireFlag(flags, 'action', 'Missing --action')).trim();
|
|
132
134
|
const payload = {
|
|
133
135
|
agentApiKey: resolveAgentApiKey(flags, runMode),
|
|
134
|
-
action
|
|
136
|
+
action,
|
|
135
137
|
userDid: resolveEffectiveUserDid(flags, {
|
|
136
138
|
runMode,
|
|
137
139
|
hybridWalletMode: options.hybridWalletMode,
|
|
138
140
|
preferDelegationIdentity: options.preferDelegationIdentity,
|
|
139
141
|
}),
|
|
140
142
|
params: managedParams,
|
|
141
|
-
chainId:
|
|
143
|
+
chainId: resolveWalletExecuteChainId({
|
|
144
|
+
action,
|
|
145
|
+
chainId: flags.chainId,
|
|
146
|
+
params: managedParams,
|
|
147
|
+
}),
|
|
142
148
|
executionStrategy: {
|
|
143
149
|
runMode,
|
|
144
150
|
mirrorOnly: false,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { apiGet, apiPost } from '../../core/http.mjs';
|
|
13
13
|
import { withActionTimeout } from '../../core/actionTimeouts.mjs';
|
|
14
14
|
import { isReadOnlyResolvedAction } from '../../core/readOnlyActions.mjs';
|
|
15
|
+
import { resolveWalletExecuteChainId } from '../../core/walletExecuteChainId.mjs';
|
|
15
16
|
import { isEmptyQueuedExecuteIntent, shouldFallbackManagedReadToExecuteIntent } from './managedReadExecuteRail.mjs';
|
|
16
17
|
import { createRequire } from 'module';
|
|
17
18
|
|
|
@@ -637,7 +638,13 @@ async function executeHybridRun(ctx, flags, resolved, options) {
|
|
|
637
638
|
}
|
|
638
639
|
|
|
639
640
|
async function resolveManagedExecutionPreflight(ctx, flags, resolved, options = {}) {
|
|
640
|
-
const
|
|
641
|
+
const action = String(resolved?.resolvedAction || flags.action || '').trim();
|
|
642
|
+
const params = getPreparedParams(flags);
|
|
643
|
+
const chainId = resolveWalletExecuteChainId({
|
|
644
|
+
action,
|
|
645
|
+
chainId: flags.chainId,
|
|
646
|
+
params,
|
|
647
|
+
});
|
|
641
648
|
const runMode = String(options.runMode || 'agent_wallet').trim().toLowerCase();
|
|
642
649
|
const agentApiKey = resolveAgentApiKey(flags, runMode);
|
|
643
650
|
const userDid = resolveEffectiveUserDid(flags, {
|
|
@@ -657,10 +664,7 @@ async function resolveManagedExecutionPreflight(ctx, flags, resolved, options =
|
|
|
657
664
|
signerMethod: 'managed_wallet_server',
|
|
658
665
|
};
|
|
659
666
|
}
|
|
660
|
-
const tokenIn = resolveTokenInForFunding(
|
|
661
|
-
getPreparedParams(flags),
|
|
662
|
-
resolved?.resolvedAction || flags.action
|
|
663
|
-
);
|
|
667
|
+
const tokenIn = resolveTokenInForFunding(params, action);
|
|
664
668
|
const query = new URLSearchParams({
|
|
665
669
|
userDid,
|
|
666
670
|
chainId: String(chainId),
|
|
@@ -5,18 +5,8 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
5
5
|
function getTradingProviderHint(providerId) {
|
|
6
6
|
const provider = String(providerId || '').trim().toLowerCase();
|
|
7
7
|
const manifestHint = getCachedProviderHintFromCapabilities(provider);
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
provider,
|
|
12
|
-
category: 'onchain_spot',
|
|
13
|
-
canonicalWriteAction: 'swap',
|
|
14
|
-
supportedChains: [1, 10, 130, 137, 8453, 84532, 42161, 4663],
|
|
15
|
-
laneCompatibility:
|
|
16
|
-
'Kyber quote/build is live on Ethereum, Optimism, Unichain, Polygon, Base, and Arbitrum; sponsored Kyber execution is validated on Base only. Robinhood Chain (4663) uses Li.Fi Intents for spot swaps with user-pays gas. Gnosis FX uses the fx provider via CoW.',
|
|
17
|
-
credentialUx: 'Use normal Thirdfy login/delegation; do not use this provider for Hyperliquid, Lighter, or Polymarket.',
|
|
18
|
-
};
|
|
19
|
-
}
|
|
8
|
+
const setupHints =
|
|
9
|
+
manifestHint?.setupHints && typeof manifestHint.setupHints === 'object' ? manifestHint.setupHints : {};
|
|
20
10
|
if (provider === 'hyperliquid') {
|
|
21
11
|
return {
|
|
22
12
|
provider,
|
|
@@ -34,6 +24,7 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
34
24
|
'get_hyperliquid_onboarding_plan',
|
|
35
25
|
'get_hyperliquid_referral_status',
|
|
36
26
|
'get_hyperliquid_builder_fee_status',
|
|
27
|
+
'quote_hyperliquid_cctp_deposit',
|
|
37
28
|
],
|
|
38
29
|
orderManagementActions: [
|
|
39
30
|
'place_hyperliquid_perps_order',
|
|
@@ -48,13 +39,16 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
48
39
|
'get_hyperliquid_onboarding_plan',
|
|
49
40
|
'prepare_hyperliquid_onboarding',
|
|
50
41
|
'complete_hyperliquid_onboarding',
|
|
42
|
+
'quote_hyperliquid_cctp_deposit',
|
|
43
|
+
'deposit_hyperliquid_cctp',
|
|
44
|
+
'deposit_hyperliquid_bridge',
|
|
45
|
+
'transfer_hyperliquid_spot_to_perps',
|
|
51
46
|
'get_bridge_quote',
|
|
52
47
|
'execute_bridge',
|
|
53
48
|
'approve_hyperliquid_agent',
|
|
54
49
|
'get_hyperliquid_builder_fee_status',
|
|
55
50
|
'approve_hyperliquid_builder_fee',
|
|
56
51
|
'set_hyperliquid_referrer',
|
|
57
|
-
'deposit_hyperliquid_bridge',
|
|
58
52
|
'deposit_hyperliquid_staking',
|
|
59
53
|
'claim_hyperliquid_testnet_faucet',
|
|
60
54
|
],
|
|
@@ -73,17 +67,14 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
73
67
|
credentialType: 'hyperliquid_api_wallet',
|
|
74
68
|
credentialUx:
|
|
75
69
|
'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.',
|
|
76
|
-
recommendedFundingChainId: 42161,
|
|
77
70
|
supportedFundingSourceChains: [
|
|
78
|
-
{ chainId:
|
|
79
|
-
{ chainId:
|
|
80
|
-
{ chainId:
|
|
81
|
-
{ chainId:
|
|
71
|
+
{ chainId: 8453, name: 'Base' },
|
|
72
|
+
{ chainId: 42161, name: 'Arbitrum One' },
|
|
73
|
+
{ chainId: 137, name: 'Polygon' },
|
|
74
|
+
{ chainId: 1, name: 'Ethereum' },
|
|
82
75
|
],
|
|
83
76
|
setupBlockers: [
|
|
84
77
|
'HYPERLIQUID_SOURCE_FUNDS_REQUIRED',
|
|
85
|
-
'HYPERLIQUID_ARBITRUM_USDC_REQUIRED',
|
|
86
|
-
'HYPERLIQUID_BRIDGE2_DEPOSIT_REQUIRED',
|
|
87
78
|
'HYPERLIQUID_API_WALLET_APPROVAL_REQUIRED',
|
|
88
79
|
'HYPERLIQUID_BUILDER_APPROVAL_REQUIRED',
|
|
89
80
|
'HYPERLIQUID_POLICY_REQUIRED',
|
|
@@ -94,17 +85,32 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
94
85
|
'HYPERLIQUID_BUILDER_WALLET_UNFUNDED',
|
|
95
86
|
'HYPERLIQUID_API_WALLET_NOT_PROVIDED',
|
|
96
87
|
],
|
|
97
|
-
sourceBridgeProvider: 'lifi',
|
|
98
88
|
funding:
|
|
99
|
-
'
|
|
89
|
+
'Follow nextAction and nextActionParams from get_hyperliquid_funding_status. Do not invent a funding chain.',
|
|
100
90
|
laneCompatibility:
|
|
101
|
-
'Use self/BYOW only for setup signatures or
|
|
91
|
+
'Use self/BYOW only for setup signatures or source-chain funding transactions. Perps order placement is an offchain venue write and should use thirdfy, hybrid, or agent_wallet. The default solo agent_wallet path uses the managed execution wallet as the Hyperliquid master address. If margin already sits on a distinct Hyperliquid main wallet, pass that as userAddress/mainWalletAddress and pass the approved execution/API wallet as apiWalletAddress.',
|
|
102
92
|
setupExample:
|
|
103
|
-
'actions --provider hyperliquid -> get_hyperliquid_funding_status -> complete_hyperliquid_onboarding for runtime-owned setup, or get_hyperliquid_onboarding_plan/prepare_hyperliquid_onboarding for operator previews ->
|
|
93
|
+
'actions --provider hyperliquid -> get_hyperliquid_funding_status -> complete_hyperliquid_onboarding for runtime-owned setup, or get_hyperliquid_onboarding_plan/prepare_hyperliquid_onboarding for operator previews -> follow nextAction with nextActionParams -> get_hyperliquid_setup_status before place_hyperliquid_perps_order',
|
|
104
94
|
monetization:
|
|
105
95
|
'Builder monetization is active only when the API operator configured a builder address and the builder treasury has Hyperliquid margin. When active, the main wallet signs approve_hyperliquid_builder_fee once and orders attach builder fees. When the treasury is unfunded, get_hyperliquid_setup_status can return ready=true with warning HYPERLIQUID_BUILDER_WALLET_UNFUNDED, get_hyperliquid_builder_fee_status returns monetizationEnabled=false with reason BUILDER_WALLET_UNFUNDED, and place_hyperliquid_perps_order does not require builder approval.',
|
|
106
96
|
example:
|
|
107
97
|
'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"}\'',
|
|
98
|
+
...(manifestHint || {}),
|
|
99
|
+
...setupHints,
|
|
100
|
+
provider,
|
|
101
|
+
source: manifestHint ? 'cli_capabilities_manifest' : 'static_provider_hint',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (manifestHint) return manifestHint;
|
|
105
|
+
if (provider === 'trading') {
|
|
106
|
+
return {
|
|
107
|
+
provider,
|
|
108
|
+
category: 'onchain_spot',
|
|
109
|
+
canonicalWriteAction: 'swap',
|
|
110
|
+
supportedChains: [1, 10, 130, 137, 8453, 84532, 42161, 4663],
|
|
111
|
+
laneCompatibility:
|
|
112
|
+
'Kyber quote/build is live on Ethereum, Optimism, Unichain, Polygon, Base, and Arbitrum; sponsored Kyber execution is validated on Base only. Robinhood Chain (4663) uses Li.Fi Intents for spot swaps with user-pays gas. Gnosis FX uses the fx provider via CoW.',
|
|
113
|
+
credentialUx: 'Use normal Thirdfy login/delegation; do not use this provider for Hyperliquid, Lighter, or Polymarket.',
|
|
108
114
|
};
|
|
109
115
|
}
|
|
110
116
|
if (provider === 'lighter') {
|
|
@@ -952,6 +958,7 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
952
958
|
custodyMode: match.custodyMode || null,
|
|
953
959
|
description: match.description || null,
|
|
954
960
|
source: 'cli_capabilities_manifest',
|
|
961
|
+
...(match.setupHints && typeof match.setupHints === 'object' ? { setupHints: match.setupHints } : {}),
|
|
955
962
|
};
|
|
956
963
|
}
|
|
957
964
|
|