@thirdfy/agent-cli 0.2.76 → 0.2.78
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 +43 -0
- package/README.md +5 -4
- package/package.json +4 -4
- package/src/cli/manifest.mjs +4 -4
- package/src/cli/options/bootstrap.mjs +7 -1
- package/src/cli/program.mjs +1 -0
- package/src/cli/register.mjs +3 -0
- package/src/commands/avantis.mjs +7 -4
- package/src/commands/credits.mjs +3 -1
- package/src/commands/telemetry.mjs +5 -1
- package/src/commands/venue.mjs +5 -2
- package/src/core/actionTimeouts.mjs +4 -5
- package/src/core/context.mjs +5 -5
- package/src/core/hostedRuntimeIdempotency.mjs +28 -0
- package/src/core/http.mjs +58 -7
- package/src/core/venueBrand.mjs +76 -0
- package/src/runtime/actions/selection.mjs +66 -19
- package/src/runtime/certification/executionCertify.mjs +3 -1
- package/src/runtime/certification/venueActionSchemas.mjs +5 -3
- package/src/runtime/execution/amounts.mjs +63 -1
- package/src/runtime/execution/lanes.mjs +4 -2
- package/src/runtime/execution/payloads.mjs +3 -4
- package/src/runtime/execution/runners.mjs +94 -5
- package/src/runtime/main.mjs +3 -1
- package/src/runtime/providerHints.mjs +92 -65
- package/src/runtime/validation/actionParamsGate.mjs +56 -3
|
@@ -3,6 +3,7 @@ import { createHash } from 'node:crypto';
|
|
|
3
3
|
import { CLI_MANIFEST } from '../../cli/manifest.mjs';
|
|
4
4
|
import { validateParamsSchema, sanitizeParamsForSchemaValidation } from '../validation/paramsSchema.mjs';
|
|
5
5
|
import { VENUE_WRITE_ACTION_FIXTURES } from './venueActionSchemas.mjs';
|
|
6
|
+
import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
|
|
6
7
|
|
|
7
8
|
const require = createRequire(import.meta.url);
|
|
8
9
|
|
|
@@ -23,7 +24,8 @@ const AUTHORITY_INPUT_CHECKS = [
|
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
function normalizeActionKey(action) {
|
|
26
|
-
|
|
27
|
+
// Brand-insensitive: a live `place_veranta_perps_order` row replaces the `place_avantis_perps_order` fixture.
|
|
28
|
+
return toLegacyVenueActionKey(action).replace(/_/g, '-');
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
function isWriteAction(action) {
|
|
@@ -218,8 +218,10 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
|
|
|
218
218
|
validSample: { orderId: '0xabc' },
|
|
219
219
|
},
|
|
220
220
|
{
|
|
221
|
+
// Veranta (formerly Avantis). Fixture ids keep the Avantis spelling so the default schema digest
|
|
222
|
+
// is unchanged; the Veranta names are aliases.
|
|
221
223
|
action: 'place_avantis_perps_order',
|
|
222
|
-
aliases: ['place-avantis-perps-order'],
|
|
224
|
+
aliases: ['place-avantis-perps-order', 'place_veranta_perps_order', 'place-veranta-perps-order'],
|
|
223
225
|
provider: 'avantis',
|
|
224
226
|
authorityInputs: ['agentApiKey', 'estimatedAmountUsd'],
|
|
225
227
|
paramsSchema: {
|
|
@@ -249,7 +251,7 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
|
|
|
249
251
|
},
|
|
250
252
|
{
|
|
251
253
|
action: 'cancel_avantis_perps_order',
|
|
252
|
-
aliases: ['cancel-avantis-perps-order'],
|
|
254
|
+
aliases: ['cancel-avantis-perps-order', 'cancel_veranta_perps_order', 'cancel-veranta-perps-order'],
|
|
253
255
|
provider: 'avantis',
|
|
254
256
|
authorityInputs: ['agentApiKey'],
|
|
255
257
|
paramsSchema: {
|
|
@@ -267,7 +269,7 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
|
|
|
267
269
|
},
|
|
268
270
|
{
|
|
269
271
|
action: 'close_avantis_position',
|
|
270
|
-
aliases: ['close-avantis-position'],
|
|
272
|
+
aliases: ['close-avantis-position', 'close_veranta_position', 'close-veranta-position'],
|
|
271
273
|
provider: 'avantis',
|
|
272
274
|
authorityInputs: ['agentApiKey', 'estimatedAmountUsd'],
|
|
273
275
|
paramsSchema: {
|
|
@@ -3,7 +3,62 @@ import { createCliError } from '../../core/envelope.mjs';
|
|
|
3
3
|
export function createExecutionAmounts() {
|
|
4
4
|
function shouldApplySwapAmountContract(actionName) {
|
|
5
5
|
const action = String(actionName || '').trim().toLowerCase();
|
|
6
|
-
return action === 'swap' || /(^|:|-)swap$/.test(action);
|
|
6
|
+
return action === 'swap' || action === 'execute_optimal_swap' || /(^|:|-)swap$/.test(action);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function normalizeContractActionKey(actionName) {
|
|
10
|
+
return String(actionName || '').trim().toLowerCase().replace(/_/g, '-');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** `deposit_earn_position` takes `amount` as a decimal token-unit string (contract `earn_deposit_decimal_amount`). */
|
|
14
|
+
function shouldApplyEarnDepositAmountContract(actionName) {
|
|
15
|
+
return /(^|:)deposit-earn-position$/.test(normalizeContractActionKey(actionName));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function hasParamValue(value) {
|
|
19
|
+
return value !== undefined && value !== null && String(value).trim() !== '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Accepts `amountHuman` or `amountInHuman` in place of `amount`, as thirdfy-mcp's params gate does
|
|
24
|
+
* (`earn_deposit_decimal_amount`). Exactly one decimal amount; raw base units are refused, because
|
|
25
|
+
* the API reads `amount` as token units.
|
|
26
|
+
*/
|
|
27
|
+
function normalizeEarnDepositAmountContract(params) {
|
|
28
|
+
const hasAmount = hasParamValue(params.amount);
|
|
29
|
+
const hasAmountHuman = hasParamValue(params.amountHuman);
|
|
30
|
+
const hasAmountInHuman = hasParamValue(params.amountInHuman);
|
|
31
|
+
const humanInputs = [hasAmount, hasAmountHuman, hasAmountInHuman].filter(Boolean).length;
|
|
32
|
+
if (humanInputs > 1) {
|
|
33
|
+
throw createCliError(
|
|
34
|
+
'EARN_AMOUNT_AMBIGUOUS',
|
|
35
|
+
'Provide exactly one earn deposit amount: amount, amountHuman, or amountInHuman.'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
if (humanInputs === 1) {
|
|
39
|
+
if (hasParamValue(params.amountInRaw) || hasParamValue(params.amountIn)) {
|
|
40
|
+
throw createCliError(
|
|
41
|
+
'EARN_AMOUNT_UNIT_AMBIGUOUS',
|
|
42
|
+
'Provide exactly one earn deposit amount; do not combine amount/amountHuman/amountInHuman with amountInRaw or amountIn.'
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
const amount = String(hasAmount ? params.amount : hasAmountHuman ? params.amountHuman : params.amountInHuman).trim();
|
|
46
|
+
if (!/^\d+(\.\d+)?$/.test(amount)) {
|
|
47
|
+
throw createCliError(
|
|
48
|
+
'EARN_AMOUNT_INVALID',
|
|
49
|
+
'Earn deposits require amount as a decimal token-unit string (example: "100.5").'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const { amountHuman: _amountHuman, amountInHuman: _amountInHuman, ...rest } = params;
|
|
53
|
+
return { ...rest, amount };
|
|
54
|
+
}
|
|
55
|
+
if (hasParamValue(params.amountInRaw) || hasParamValue(params.amountIn)) {
|
|
56
|
+
throw createCliError(
|
|
57
|
+
'EARN_AMOUNT_UNIT_INVALID',
|
|
58
|
+
'deposit_earn_position expects amount as a decimal token-unit string. Do not pass raw base-unit amountInRaw/amountIn for earn deposits.'
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return params;
|
|
7
62
|
}
|
|
8
63
|
|
|
9
64
|
function normalizeIntegerRawAmount(input, code = 'AMOUNT_UNIT_INVALID') {
|
|
@@ -164,6 +219,11 @@ function normalizeSwapAmountContract(params) {
|
|
|
164
219
|
|
|
165
220
|
function prepareActionParamsForFlags(flags, resolvedAction) {
|
|
166
221
|
const parsedParams = parseJsonFlag(flags, 'params');
|
|
222
|
+
if (shouldApplyEarnDepositAmountContract(resolvedAction)) {
|
|
223
|
+
flags.__preparedParams = normalizeEarnDepositAmountContract(parsedParams || {});
|
|
224
|
+
flags.__swapInput = null;
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
167
227
|
if (!shouldApplySwapAmountContract(resolvedAction)) {
|
|
168
228
|
flags.__preparedParams = parsedParams;
|
|
169
229
|
flags.__swapInput = null;
|
|
@@ -187,6 +247,8 @@ function getPreparedParams(flags) {
|
|
|
187
247
|
decimalToRawUnits,
|
|
188
248
|
rawUnitsToDecimalString,
|
|
189
249
|
normalizeSwapAmountContract,
|
|
250
|
+
shouldApplyEarnDepositAmountContract,
|
|
251
|
+
normalizeEarnDepositAmountContract,
|
|
190
252
|
prepareActionParamsForFlags,
|
|
191
253
|
getPreparedParams,
|
|
192
254
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createCliError } from '../../core/envelope.mjs';
|
|
2
|
+
import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
|
|
2
3
|
export function createExecutionLanes() {
|
|
3
4
|
const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
|
|
4
5
|
'place_hyperliquid_perps_order',
|
|
@@ -29,6 +30,7 @@ const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
|
|
|
29
30
|
'condor-executor-create',
|
|
30
31
|
'condor_executor_stop',
|
|
31
32
|
'condor-executor-stop',
|
|
33
|
+
// Veranta (formerly Avantis): keyed by the Avantis spelling; Veranta names map onto it below.
|
|
32
34
|
'place_avantis_perps_order',
|
|
33
35
|
'place-avantis-perps-order',
|
|
34
36
|
'cancel_avantis_perps_order',
|
|
@@ -57,11 +59,11 @@ const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
|
|
|
57
59
|
'cancel-avantis-mm-order',
|
|
58
60
|
]);
|
|
59
61
|
function enforceOffchainVenueLaneCompatibility(action, runMode) {
|
|
60
|
-
const normalizedAction =
|
|
62
|
+
const normalizedAction = toLegacyVenueActionKey(action);
|
|
61
63
|
if (runMode !== 'self' || !OFFCHAIN_VENUE_ORDER_ACTIONS.has(normalizedAction)) return;
|
|
62
64
|
throw createCliError(
|
|
63
65
|
'OFFCHAIN_VENUE_ORDER_SELF_UNSUPPORTED',
|
|
64
|
-
'Polymarket CLOB, Hyperliquid perps, Lighter perps,
|
|
66
|
+
'Polymarket CLOB, Hyperliquid perps, Lighter perps, Veranta perps, Hummingbot executor, and Condor executor actions are offchain signed API actions, not EVM build-tx actions. Use self for setup/funding signatures, then run venue orders with --run-mode thirdfy, --run-mode hybrid, or --run-mode agent_wallet.',
|
|
65
67
|
{
|
|
66
68
|
recommendedRunModes: ['thirdfy', 'hybrid', 'agent_wallet'],
|
|
67
69
|
setupOnlyRunMode: 'self',
|
|
@@ -4,6 +4,7 @@ import { resolveExecutionContextFromEnv } from '../../core/executionContext.mjs'
|
|
|
4
4
|
import { requireFlag, parseJsonFlag } from '../../core/args.mjs';
|
|
5
5
|
import { loadProfileConfig } from '../../core/context.mjs';
|
|
6
6
|
import { createCliError } from '../../core/envelope.mjs';
|
|
7
|
+
import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
|
|
7
8
|
import { normalizeRunMode, normalizeManagedRunMode, normalizeHybridWalletMode } from '../../core/runMode.mjs';
|
|
8
9
|
import { assertHostedRuntimePolymarketWriteIdempotency } from './hostedRuntimeWriteGate.mjs';
|
|
9
10
|
import { resolveTokenInForFunding } from '../../core/fundingTokenIn.mjs';
|
|
@@ -29,10 +30,8 @@ export function mergeConfirmWritesIntoParams(params, flags) {
|
|
|
29
30
|
export function resolveExecuteEstimatedAmountUsd(action, flags) {
|
|
30
31
|
const fromFlags = Number(flags?.estimatedAmountUsd);
|
|
31
32
|
if (Number.isFinite(fromFlags) && fromFlags > 0) return fromFlags;
|
|
32
|
-
const key =
|
|
33
|
-
|
|
34
|
-
.toLowerCase()
|
|
35
|
-
.replace(/-/g, '_');
|
|
33
|
+
const key = toLegacyVenueActionKey(action).replace(/-/g, '_');
|
|
34
|
+
// Veranta onboarding (either spelling).
|
|
36
35
|
if (key === 'complete_avantis_onboarding') return 1;
|
|
37
36
|
return undefined;
|
|
38
37
|
}
|
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
getEffectiveRunMode,
|
|
10
10
|
parseBooleanFlag,
|
|
11
11
|
} from '../../core/runMode.mjs';
|
|
12
|
-
import { apiGet, apiPost } from '../../core/http.mjs';
|
|
12
|
+
import { apiGet, apiPost, responseHeadersFor } from '../../core/http.mjs';
|
|
13
|
+
import { idempotentReplayFields } from '../../core/hostedRuntimeIdempotency.mjs';
|
|
13
14
|
import { withActionTimeout } from '../../core/actionTimeouts.mjs';
|
|
14
15
|
import { isReadOnlyResolvedAction } from '../../core/readOnlyActions.mjs';
|
|
15
16
|
import { resolveWalletExecuteChainId } from '../../core/walletExecuteChainId.mjs';
|
|
@@ -32,6 +33,7 @@ export function createExecutionRunners(deps) {
|
|
|
32
33
|
buildBitfinexOnboardingCommand,
|
|
33
34
|
inferBitfinexPermissionProfileFromAction,
|
|
34
35
|
resolveAgentApiKey,
|
|
36
|
+
resolveAgentKey,
|
|
35
37
|
loadEthers,
|
|
36
38
|
signAndBroadcastWithOws,
|
|
37
39
|
readEffectBalances,
|
|
@@ -146,9 +148,52 @@ function normalizeManagedExecuteResponse(response, { preflight, payload, isRead
|
|
|
146
148
|
shaped?.amountNormalization || shaped?.data?.amountNormalization || null;
|
|
147
149
|
normalized.raw = shaped;
|
|
148
150
|
normalized.idempotencyKey = payload.executionIdempotencyKey || null;
|
|
151
|
+
// A replay means nothing new ran; say so, or the caller reads it as a fresh fill.
|
|
152
|
+
if (!isRead) {
|
|
153
|
+
Object.assign(
|
|
154
|
+
normalized,
|
|
155
|
+
idempotentReplayFields(response, responseHeadersFor(response), 'executionIdempotencyKey'),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
149
158
|
return normalized;
|
|
150
159
|
}
|
|
151
160
|
|
|
161
|
+
const ROBINHOOD_MAINNET_CHAIN_ID = 4663;
|
|
162
|
+
|
|
163
|
+
/** Marker a read on the fast path carries instead of an execution-wallet preflight. */
|
|
164
|
+
const READ_FAST_PATH_PREFLIGHT = Object.freeze({ success: true, skipped: 'read_fast_path' });
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* agent_wallet reads skip the /execution-wallet preflight: the API resolves the agent's execution
|
|
168
|
+
* wallet itself, so the extra round trip only matters when a read uses its answer: a strict wallet
|
|
169
|
+
* match, a funding check, or the Robinhood delegated lane. An expected wallet equal to the agent key
|
|
170
|
+
* is not a separate wallet to match first; the wallet the API reports is checked after the read.
|
|
171
|
+
* Mirrors thirdfy-mcp `walletExecute` readFastPath.
|
|
172
|
+
*/
|
|
173
|
+
function shouldUseManagedReadFastPath(flags, resolved, options = {}) {
|
|
174
|
+
if (!isReadOnlyAction(resolved)) return false;
|
|
175
|
+
if (options.requireFundingCheck === true) return false;
|
|
176
|
+
const runMode = String(flags.runMode || 'agent_wallet').trim().toLowerCase();
|
|
177
|
+
if (runMode !== 'agent_wallet') return false;
|
|
178
|
+
const expectedWallet = String(flags.walletAddress || flags.executionWalletAddress || '').trim().toLowerCase();
|
|
179
|
+
const agentKey = String(typeof resolveAgentKey === 'function' ? resolveAgentKey(flags) : flags.agentKey || '')
|
|
180
|
+
.trim()
|
|
181
|
+
.toLowerCase();
|
|
182
|
+
if (expectedWallet && expectedWallet !== agentKey) return false;
|
|
183
|
+
let chainId;
|
|
184
|
+
try {
|
|
185
|
+
chainId = resolveWalletExecuteChainId({
|
|
186
|
+
action: String(resolved?.resolvedAction || flags.action || '').trim(),
|
|
187
|
+
chainId: flags.chainId,
|
|
188
|
+
params: getPreparedParams(flags),
|
|
189
|
+
});
|
|
190
|
+
} catch {
|
|
191
|
+
// Unresolvable chain (e.g. a CCTP quote without sourceChainId): the preflight path reports it.
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
return Number(chainId) !== ROBINHOOD_MAINNET_CHAIN_ID;
|
|
195
|
+
}
|
|
196
|
+
|
|
152
197
|
async function runPreflightByMode(runMode, ctx, flags, resolved, options = {}) {
|
|
153
198
|
const resolvedAction = resolved.resolvedAction;
|
|
154
199
|
if (runMode === 'agent_wallet') {
|
|
@@ -439,6 +484,9 @@ async function executeThirdfyRun(ctx, flags, resolved, options, runMode = 'third
|
|
|
439
484
|
const response = await apiPost(actionCtx, '/api/v1/agent/execute-intent', payload);
|
|
440
485
|
const normalized = normalizeIntentResponse(response);
|
|
441
486
|
normalized.idempotencyKey = payload.idempotencyKey;
|
|
487
|
+
if (!isReadOnlyAction(resolved)) {
|
|
488
|
+
Object.assign(normalized, idempotentReplayFields(response, responseHeadersFor(response), 'idempotencyKey'));
|
|
489
|
+
}
|
|
442
490
|
return normalized;
|
|
443
491
|
}
|
|
444
492
|
|
|
@@ -465,7 +513,7 @@ async function runReadOnlyActionViaIntentRail(ctx, flags, resolved, options, pre
|
|
|
465
513
|
intentResult.routeFallback = 'execute_intent_read_only';
|
|
466
514
|
if (preflight) {
|
|
467
515
|
intentResult.executionWalletPreflight = preflight;
|
|
468
|
-
intentResult.executionWalletAddress = preflight.executionWalletAddress || null;
|
|
516
|
+
intentResult.executionWalletAddress = preflight.executionWalletAddress || intentResult.executionWalletAddress || null;
|
|
469
517
|
}
|
|
470
518
|
intentResult.signerMethod = 'managed_wallet_server';
|
|
471
519
|
return intentResult;
|
|
@@ -479,8 +527,11 @@ async function executeManagedWalletRun(ctx, flags, resolved, options) {
|
|
|
479
527
|
// (often as a JSON string under data.result). Reads skip funding gates; routing still fails closed.
|
|
480
528
|
const isRead = isReadOnlyAction(resolved);
|
|
481
529
|
const skipPreflight = Boolean(options?.skipPreflight);
|
|
482
|
-
|
|
483
|
-
|
|
530
|
+
// A seeded preflight (Hyperliquid/Lighter/Veranta address defaults) already ran; reuse it.
|
|
531
|
+
const readFastPath =
|
|
532
|
+
!skipPreflight && !options?.managedPreflight && shouldUseManagedReadFastPath(flags, resolved, options);
|
|
533
|
+
let preflight = readFastPath ? READ_FAST_PATH_PREFLIGHT : null;
|
|
534
|
+
if (!skipPreflight && !readFastPath) {
|
|
484
535
|
preflight = await resolveManagedExecutionPreflight(ctx, flags, resolved, {
|
|
485
536
|
runMode: 'agent_wallet',
|
|
486
537
|
requireFundingCheck: !isRead,
|
|
@@ -510,8 +561,19 @@ async function executeManagedWalletRun(ctx, flags, resolved, options) {
|
|
|
510
561
|
});
|
|
511
562
|
const actionCtx = withActionTimeout(ctx, resolved);
|
|
512
563
|
const response = await apiPost(actionCtx, '/api/v1/agent/execute', payload);
|
|
564
|
+
if (readFastPath) {
|
|
565
|
+
const mismatch = fastPathWalletMismatch(flags, response);
|
|
566
|
+
if (mismatch) return mismatch;
|
|
567
|
+
}
|
|
513
568
|
if (shouldFallbackAgentWalletToExecuteIntent(resolved, response)) {
|
|
514
|
-
|
|
569
|
+
// On the fast path the wallet comes from the /execute answer, not a preflight.
|
|
570
|
+
const fallbackPreflight = readFastPath
|
|
571
|
+
? {
|
|
572
|
+
...READ_FAST_PATH_PREFLIGHT,
|
|
573
|
+
executionWalletAddress: response?.executionWalletAddress || response?.data?.executionWalletAddress || null,
|
|
574
|
+
}
|
|
575
|
+
: preflight;
|
|
576
|
+
const intentResult = await runReadOnlyActionViaIntentRail(ctx, flags, resolved, options, fallbackPreflight);
|
|
515
577
|
if (isEmptyQueuedExecuteIntent(intentResult.raw || intentResult)) {
|
|
516
578
|
intentResult.success = false;
|
|
517
579
|
intentResult.status = 'failed';
|
|
@@ -527,6 +589,33 @@ async function executeManagedWalletRun(ctx, flags, resolved, options) {
|
|
|
527
589
|
return normalizeManagedExecuteResponse(response, { preflight, payload, isRead });
|
|
528
590
|
}
|
|
529
591
|
|
|
592
|
+
/**
|
|
593
|
+
* The fast path only runs when the expected wallet is empty or equals the agent key. The preflight
|
|
594
|
+
* path refuses a wallet that differs from the execution wallet, so apply the same rule to the wallet
|
|
595
|
+
* the API reports for the read.
|
|
596
|
+
*/
|
|
597
|
+
function fastPathWalletMismatch(flags, response) {
|
|
598
|
+
const expectedWallet = String(flags.walletAddress || flags.executionWalletAddress || '').trim().toLowerCase();
|
|
599
|
+
if (!expectedWallet || parseBooleanFlag(flags.allowWalletMismatch, false)) return null;
|
|
600
|
+
const apiWallet = String(response?.executionWalletAddress || response?.data?.executionWalletAddress || '')
|
|
601
|
+
.trim()
|
|
602
|
+
.toLowerCase();
|
|
603
|
+
if (!apiWallet || apiWallet === expectedWallet) return null;
|
|
604
|
+
const normalized = normalizeIntentResponse({
|
|
605
|
+
success: false,
|
|
606
|
+
status: 'failed',
|
|
607
|
+
mode: 'agent_wallet',
|
|
608
|
+
blockedReason: 'EXECUTION_WALLET_MISMATCH',
|
|
609
|
+
blockedStage: 'routing',
|
|
610
|
+
error: `Funded wallet (${expectedWallet}) does not match execution wallet (${apiWallet}). Use --allow-wallet-mismatch to override.`,
|
|
611
|
+
preflightBlocked: true,
|
|
612
|
+
executionWalletAddress: apiWallet,
|
|
613
|
+
});
|
|
614
|
+
normalized.executionWalletAddress = apiWallet;
|
|
615
|
+
normalized.executionWalletPreflight = READ_FAST_PATH_PREFLIGHT;
|
|
616
|
+
return normalized;
|
|
617
|
+
}
|
|
618
|
+
|
|
530
619
|
async function executeSelfRun(ctx, flags, resolved, options) {
|
|
531
620
|
const skipPreflight = Boolean(options?.skipPreflight);
|
|
532
621
|
const blockedReasonCode = String(options?.blockedReasonCode || 'SELF_MODE_REQUIRES_BUILD_TX');
|
package/src/runtime/main.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
collectRootFlags,
|
|
7
7
|
handleRootOnlyFlows,
|
|
8
8
|
collectVersionFlagsFromArgv,
|
|
9
|
+
normalizeLegacyShortFlags,
|
|
9
10
|
} from '../cli/program.mjs';
|
|
10
11
|
import { createInitialRuntimeContext, applyFlagsToRuntimeContext } from './context.mjs';
|
|
11
12
|
import { createProviderHints } from './providerHints.mjs';
|
|
@@ -87,6 +88,7 @@ export function createThirdfyAgentRuntime() {
|
|
|
87
88
|
buildBitfinexOnboardingCommand: onboarding.buildBitfinexOnboardingCommand,
|
|
88
89
|
inferBitfinexPermissionProfileFromAction: onboarding.inferBitfinexPermissionProfileFromAction,
|
|
89
90
|
resolveAgentApiKey: payloads.resolveAgentApiKey,
|
|
91
|
+
resolveAgentKey: payloads.resolveAgentKey,
|
|
90
92
|
loadEthers: ows.loadEthers,
|
|
91
93
|
signAndBroadcastWithOws: ows.signAndBroadcastWithOws,
|
|
92
94
|
readEffectBalances: ows.readEffectBalances,
|
|
@@ -154,7 +156,7 @@ export function createThirdfyAgentRuntime() {
|
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
try {
|
|
157
|
-
await cliProgram.parseAsync(process.argv, { from: 'node' });
|
|
159
|
+
await cliProgram.parseAsync(normalizeLegacyShortFlags(process.argv), { from: 'node' });
|
|
158
160
|
const versionHandled = await handleRootOnlyFlows(cliProgram, rootFlowDeps);
|
|
159
161
|
if (versionHandled) return;
|
|
160
162
|
} catch (error) {
|
|
@@ -1,9 +1,27 @@
|
|
|
1
|
+
import { normalizeVenueProviderId } from '../core/venueBrand.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Veranta (formerly Avantis) naming for the `avantis` / `avantis-vault` hints, static or from the
|
|
5
|
+
* capabilities manifest. Empty for every other provider.
|
|
6
|
+
*/
|
|
7
|
+
function verantaNamingFields(provider) {
|
|
8
|
+
if (provider !== 'avantis' && provider !== 'avantis-vault') return {};
|
|
9
|
+
const alias = provider === 'avantis' ? 'veranta' : 'veranta-vault';
|
|
10
|
+
return {
|
|
11
|
+
displayName: 'Veranta',
|
|
12
|
+
formerName: 'Avantis',
|
|
13
|
+
providerAliases: [alias],
|
|
14
|
+
actionNaming: `Veranta (formerly Avantis). Veranta action names (*_veranta_*) are canonical; Avantis names (*_avantis_*) still resolve. Provider id stays ${provider}; --provider ${alias} is an alias.`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
1
18
|
/**
|
|
2
19
|
* Static and capabilities-backed provider discovery hints for CLI actions filtering.
|
|
3
20
|
*/
|
|
4
21
|
export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
5
22
|
function getTradingProviderHint(providerId) {
|
|
6
|
-
|
|
23
|
+
// `veranta` / `veranta-vault` resolve to the internal `avantis` / `avantis-vault` hints.
|
|
24
|
+
const provider = normalizeVenueProviderId(providerId || '');
|
|
7
25
|
const manifestHint = getCachedProviderHintFromCapabilities(provider);
|
|
8
26
|
const setupHints =
|
|
9
27
|
manifestHint?.setupHints && typeof manifestHint.setupHints === 'object' ? manifestHint.setupHints : {};
|
|
@@ -206,68 +224,69 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
206
224
|
if (provider === 'avantis') {
|
|
207
225
|
return {
|
|
208
226
|
provider,
|
|
227
|
+
...verantaNamingFields(provider),
|
|
209
228
|
category: 'leveraged_perps',
|
|
210
|
-
canonicalWriteAction: '
|
|
229
|
+
canonicalWriteAction: 'place_veranta_perps_order',
|
|
211
230
|
readActions: [
|
|
212
|
-
'
|
|
213
|
-
'
|
|
214
|
-
'
|
|
215
|
-
'
|
|
216
|
-
'
|
|
217
|
-
'
|
|
218
|
-
'
|
|
219
|
-
'
|
|
220
|
-
'
|
|
221
|
-
'
|
|
222
|
-
'
|
|
223
|
-
'
|
|
224
|
-
'
|
|
225
|
-
'
|
|
231
|
+
'get_veranta_markets',
|
|
232
|
+
'get_veranta_pair',
|
|
233
|
+
'get_veranta_price',
|
|
234
|
+
'get_veranta_spread',
|
|
235
|
+
'get_veranta_positions',
|
|
236
|
+
'get_veranta_open_orders',
|
|
237
|
+
'get_veranta_allowance',
|
|
238
|
+
'get_veranta_delegation_status',
|
|
239
|
+
'get_veranta_funding_status',
|
|
240
|
+
'get_veranta_setup_status',
|
|
241
|
+
'get_veranta_onboarding_plan',
|
|
242
|
+
'get_veranta_builder_fee_status',
|
|
243
|
+
'get_veranta_referral_status',
|
|
244
|
+
'get_veranta_referral_stats',
|
|
226
245
|
],
|
|
227
246
|
orderManagementActions: [
|
|
228
|
-
'
|
|
229
|
-
'
|
|
230
|
-
'
|
|
231
|
-
'
|
|
232
|
-
'
|
|
233
|
-
'
|
|
234
|
-
'
|
|
235
|
-
'
|
|
236
|
-
'
|
|
237
|
-
'
|
|
238
|
-
'
|
|
239
|
-
'
|
|
240
|
-
'
|
|
241
|
-
'
|
|
242
|
-
'
|
|
247
|
+
'place_veranta_perps_order',
|
|
248
|
+
'cancel_veranta_perps_order',
|
|
249
|
+
'close_veranta_position',
|
|
250
|
+
'update_veranta_tp_sl',
|
|
251
|
+
'cancel_veranta_tp_sl',
|
|
252
|
+
'place_veranta_twap_order',
|
|
253
|
+
'close_veranta_twap_order',
|
|
254
|
+
'cancel_veranta_twap_order',
|
|
255
|
+
'place_veranta_coin_perps_order',
|
|
256
|
+
'increase_veranta_coin_position',
|
|
257
|
+
'close_veranta_coin_position',
|
|
258
|
+
'place_veranta_mm_order',
|
|
259
|
+
'cancel_veranta_mm_order',
|
|
260
|
+
'get_veranta_open_orders',
|
|
261
|
+
'get_veranta_positions',
|
|
243
262
|
],
|
|
244
263
|
setupActions: [
|
|
245
|
-
'
|
|
246
|
-
'
|
|
247
|
-
'
|
|
248
|
-
'
|
|
249
|
-
'
|
|
250
|
-
'
|
|
251
|
-
'
|
|
252
|
-
'
|
|
253
|
-
'
|
|
254
|
-
'
|
|
264
|
+
'get_veranta_funding_status',
|
|
265
|
+
'get_veranta_setup_status',
|
|
266
|
+
'get_veranta_onboarding_plan',
|
|
267
|
+
'prepare_veranta_onboarding',
|
|
268
|
+
'complete_veranta_onboarding',
|
|
269
|
+
'approve_veranta_builder_fee',
|
|
270
|
+
'revoke_veranta_builder_fee',
|
|
271
|
+
'set_veranta_referral_code',
|
|
272
|
+
'register_veranta_referral_code',
|
|
273
|
+
'claim_veranta_referral_rebate',
|
|
255
274
|
],
|
|
256
275
|
statusActions: [
|
|
257
|
-
'
|
|
258
|
-
'
|
|
259
|
-
'
|
|
260
|
-
'
|
|
276
|
+
'get_veranta_funding_status',
|
|
277
|
+
'get_veranta_setup_status',
|
|
278
|
+
'get_veranta_builder_fee_status',
|
|
279
|
+
'get_veranta_delegation_status',
|
|
261
280
|
],
|
|
262
281
|
recoveryActions: [
|
|
263
|
-
'
|
|
264
|
-
'
|
|
282
|
+
'get_veranta_positions',
|
|
283
|
+
'close_veranta_position',
|
|
265
284
|
],
|
|
266
285
|
decommissionExample:
|
|
267
|
-
'
|
|
286
|
+
'get_veranta_positions -> close_veranta_position -> withdraw-to-user on execution wallet (Base USDC)',
|
|
268
287
|
credentialType: 'avantis_delegate',
|
|
269
288
|
credentialUx:
|
|
270
|
-
'Thirdfy mints and stores the encrypted
|
|
289
|
+
'Thirdfy mints and stores the encrypted Veranta delegate. CLI callers use Thirdfy auth only. Do not paste a private key.',
|
|
271
290
|
recommendedFundingChainId: 8453,
|
|
272
291
|
supportedFundingSourceChains: [
|
|
273
292
|
{ chainId: 8453, name: 'Base', path: 'native USDC in managed execution wallet (recommended)' },
|
|
@@ -283,32 +302,33 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
283
302
|
funding:
|
|
284
303
|
'Fund the managed execution wallet on Base (8453) with native USDC. Minimum collateral follows the live catalog notional floor.',
|
|
285
304
|
laneCompatibility:
|
|
286
|
-
'Use agent_wallet for solo managed-wallet onboarding and perps writes after avantis_delegate credential is stored. Use thirdfy or hybrid for delegated capital. Thirdfy builds EIP-712 intents; the delegate credential signs batched-market submissions. Relayer is default. Builder-fee collection stays unwired until
|
|
305
|
+
'Use agent_wallet for solo managed-wallet onboarding and perps writes after avantis_delegate credential is stored. Use thirdfy or hybrid for delegated capital. Thirdfy builds EIP-712 intents; the delegate credential signs batched-market submissions. Relayer is default. Builder-fee collection stays unwired until Veranta supplies a fee-enabled template.',
|
|
287
306
|
setupExample:
|
|
288
|
-
'actions --provider
|
|
307
|
+
'actions --provider veranta -> get_veranta_funding_status -> complete_veranta_onboarding (agent_wallet on Base 8453) -> get_veranta_setup_status before place_veranta_perps_order. Do not call approve_veranta_builder_fee until setup status shows a wired template.',
|
|
289
308
|
example:
|
|
290
|
-
'thirdfy-agent actions --provider
|
|
309
|
+
'thirdfy-agent actions --provider veranta && thirdfy-agent preflight --action place_veranta_perps_order --params \'{"pairIndex":0,"side":"long","collateralUsdc":10,"leverage":10,"orderType":"market"}\'',
|
|
291
310
|
};
|
|
292
311
|
}
|
|
293
312
|
if (provider === 'avantis-vault') {
|
|
294
313
|
return {
|
|
295
314
|
provider,
|
|
315
|
+
...verantaNamingFields(provider),
|
|
296
316
|
category: 'earn',
|
|
297
|
-
canonicalReadAction: '
|
|
298
|
-
canonicalWriteAction: '
|
|
299
|
-
readActions: ['
|
|
317
|
+
canonicalReadAction: 'get_veranta_vault_info',
|
|
318
|
+
canonicalWriteAction: 'deposit_veranta_vault',
|
|
319
|
+
readActions: ['get_veranta_vault_info', 'get_veranta_vault_position'],
|
|
300
320
|
orderManagementActions: [
|
|
301
|
-
'
|
|
302
|
-
'
|
|
303
|
-
'
|
|
304
|
-
'
|
|
321
|
+
'deposit_veranta_vault',
|
|
322
|
+
'request_veranta_vault_withdrawal',
|
|
323
|
+
'redeem_veranta_vault',
|
|
324
|
+
'claim_veranta_vault',
|
|
305
325
|
],
|
|
306
326
|
recommendedFundingChainId: 8453,
|
|
307
327
|
supportedChains: [8453],
|
|
308
328
|
funding: 'Fund the managed execution wallet on Base with native USDC before vault deposit.',
|
|
309
|
-
laneCompatibility: 'Avantis LP vault on Base. Use agent_wallet, thirdfy, or hybrid. Relayer is default.',
|
|
329
|
+
laneCompatibility: 'Veranta (formerly Avantis) LP vault on Base. Use agent_wallet, thirdfy, or hybrid. Relayer is default.',
|
|
310
330
|
example:
|
|
311
|
-
'thirdfy-agent actions --provider
|
|
331
|
+
'thirdfy-agent actions --provider veranta-vault && thirdfy-agent run --action get_veranta_vault_info --provider veranta-vault --json',
|
|
312
332
|
};
|
|
313
333
|
}
|
|
314
334
|
if (provider === 'bitfinex') {
|
|
@@ -901,12 +921,12 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
901
921
|
}
|
|
902
922
|
|
|
903
923
|
function getCachedProviderHintFromCapabilities(provider) {
|
|
904
|
-
const normalizedProvider =
|
|
924
|
+
const normalizedProvider = normalizeVenueProviderId(provider || '');
|
|
905
925
|
if (!normalizedProvider) return null;
|
|
906
926
|
const manifests = Array.isArray(getNegotiatedCapabilitiesCache()?.providers?.manifests)
|
|
907
927
|
? getNegotiatedCapabilitiesCache().providers.manifests
|
|
908
928
|
: [];
|
|
909
|
-
const match = manifests.find((entry) =>
|
|
929
|
+
const match = manifests.find((entry) => normalizeVenueProviderId(entry?.providerId || '') === normalizedProvider);
|
|
910
930
|
if (!match) return null;
|
|
911
931
|
const canonicalWriteActions = {
|
|
912
932
|
trading: 'swap',
|
|
@@ -958,13 +978,20 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
958
978
|
custodyMode: match.custodyMode || null,
|
|
959
979
|
description: match.description || null,
|
|
960
980
|
source: 'cli_capabilities_manifest',
|
|
981
|
+
...verantaNamingFields(normalizedProvider),
|
|
961
982
|
...(match.setupHints && typeof match.setupHints === 'object' ? { setupHints: match.setupHints } : {}),
|
|
962
983
|
};
|
|
963
984
|
}
|
|
964
985
|
|
|
986
|
+
// Veranta hints carry `providerAliases`; show the public name in messages (`--provider veranta`).
|
|
987
|
+
function providerLabelForHint(providerHint) {
|
|
988
|
+
const alias = Array.isArray(providerHint?.providerAliases) ? providerHint.providerAliases[0] : null;
|
|
989
|
+
return alias || providerHint.provider;
|
|
990
|
+
}
|
|
991
|
+
|
|
965
992
|
function formatProviderAmbiguousActionHint(providerHint) {
|
|
966
993
|
if (!providerHint) return '';
|
|
967
|
-
const p = providerHint
|
|
994
|
+
const p = providerLabelForHint(providerHint);
|
|
968
995
|
const w = providerHint.canonicalWriteAction;
|
|
969
996
|
if (w) return ` Provider ${p} expects ${w}.`;
|
|
970
997
|
return ` Provider ${p} did not declare a canonical write action in capabilities; run actions --provider ${p} first.`;
|
|
@@ -972,7 +999,7 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
972
999
|
|
|
973
1000
|
function formatProviderUnknownActionHint(providerHint) {
|
|
974
1001
|
if (!providerHint) return '';
|
|
975
|
-
const p = providerHint
|
|
1002
|
+
const p = providerLabelForHint(providerHint);
|
|
976
1003
|
const w = providerHint.canonicalWriteAction;
|
|
977
1004
|
if (w) return ` Provider ${p} expects ${w}; run actions --provider ${p} first.`;
|
|
978
1005
|
return ` Provider ${p} did not declare a canonical write action in capabilities; run actions --provider ${p} first.`;
|