@thirdfy/agent-cli 0.1.16 → 0.1.18
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 +9 -0
- package/README.md +27 -8
- package/bin/thirdfy-agent.mjs +213 -32
- package/package.json +2 -2
- package/src/utils/cli/intentNormalization.cjs +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.18] - 2026-04-21
|
|
8
|
+
|
|
9
|
+
**npm:** [`@thirdfy/agent-cli@0.1.18`](https://www.npmjs.com/package/@thirdfy/agent-cli/v/0.1.18)
|
|
10
|
+
**Git tag:** `v0.1.18`
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **Command reference:** `delegation redeem` documents `--run-mode thirdfy|hybrid|agent_wallet` and clarifies `--hybrid-wallet-mode` for hybrid runs (release PR #16).
|
|
15
|
+
|
|
7
16
|
## [0.1.14] - 2026-04-19
|
|
8
17
|
|
|
9
18
|
**npm:** [`@thirdfy/agent-cli@0.1.14`](https://www.npmjs.com/package/@thirdfy/agent-cli/v/0.1.14)
|
package/README.md
CHANGED
|
@@ -138,26 +138,30 @@ thirdfy-agent bootstrap complete --agent-key "0xYOUR_AGENT_KEY" --challenge-id "
|
|
|
138
138
|
|
|
139
139
|
## Run modes and profiles
|
|
140
140
|
|
|
141
|
-
`thirdfy-agent` supports one command surface with
|
|
141
|
+
`thirdfy-agent` supports one command surface with four run modes:
|
|
142
142
|
|
|
143
143
|
- `--run-mode thirdfy` -> Thirdfy-governed execute-intent rail (default for `profile=network`)
|
|
144
144
|
- `--run-mode self` -> self-custody rail (`build-tx` flow; default for `profile=personal`)
|
|
145
145
|
- `--run-mode hybrid` -> self-custody + governance mirror metadata (default for `profile=builder`)
|
|
146
|
+
- `--run-mode agent_wallet` -> managed wallet execution rail for Claude-friendly hosted execution
|
|
146
147
|
|
|
147
148
|
| Goal | Recommended mode | Why |
|
|
148
149
|
| --- | --- | --- |
|
|
149
150
|
| BYOW signing and local custody | `self` | Keeps signing and broadcast on your wallet path. |
|
|
150
151
|
| Managed delegated execution | `thirdfy` | Uses delegated governance rail for hosted execution. |
|
|
151
152
|
| Self execution plus governance mirror checks | `hybrid` | Preserves local execution while emitting managed-lane mirrors. |
|
|
153
|
+
| Managed wallet primary + governance mirror checks | `hybrid --hybrid-wallet-mode agent_wallet` | Uses managed wallet as primary hybrid path while still running Thirdfy mirror checks. |
|
|
152
154
|
|
|
153
155
|
Auth expectations by mode:
|
|
154
156
|
|
|
155
157
|
- `self`: requires execution identity (`agentApiKey`) today; keyless self lane is blocked with deterministic `SELF_OPEN_DISABLED`.
|
|
156
|
-
- `hybrid`: requires execution identity and governed mirror semantics.
|
|
158
|
+
- `hybrid`: requires execution identity and governed mirror semantics (`--hybrid-wallet-mode self|agent_wallet`, default `self`).
|
|
157
159
|
- `thirdfy`: requires execution identity and delegated governance path.
|
|
160
|
+
- `agent_wallet`: requires execution identity and delegated managed-wallet path.
|
|
158
161
|
- custody mode defaults are profile-aware:
|
|
159
162
|
- `managed` default for `thirdfy`
|
|
160
163
|
- `external` default for `self` and `hybrid`
|
|
164
|
+
- `managed` default for `agent_wallet`
|
|
161
165
|
|
|
162
166
|
Selection precedence:
|
|
163
167
|
|
|
@@ -192,6 +196,19 @@ thirdfy-agent run \
|
|
|
192
196
|
|
|
193
197
|
The default `run --run-mode self` behavior is still unsigned-only (advanced users can sign externally).
|
|
194
198
|
|
|
199
|
+
Hybrid managed-wallet variant (agent_wallet + Thirdfy mirror):
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
thirdfy-agent run \
|
|
203
|
+
--agent-api-key "$AGENT_API_KEY" \
|
|
204
|
+
--run-mode hybrid \
|
|
205
|
+
--hybrid-wallet-mode agent_wallet \
|
|
206
|
+
--action swap \
|
|
207
|
+
--params '{"tokenIn":"0x...","tokenOut":"0x...","amountInRaw":"1000000","chainId":8453}' \
|
|
208
|
+
--estimated-amount-usd 25 \
|
|
209
|
+
--json
|
|
210
|
+
```
|
|
211
|
+
|
|
195
212
|
Self lane effect-check controls:
|
|
196
213
|
|
|
197
214
|
```bash
|
|
@@ -273,7 +290,7 @@ Use `create` + `activate` as the default delegated execution lifecycle:
|
|
|
273
290
|
|
|
274
291
|
```bash
|
|
275
292
|
thirdfy-agent delegation create \
|
|
276
|
-
--
|
|
293
|
+
--owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" \
|
|
277
294
|
--agent-key "0xYOUR_AGENT_KEY" \
|
|
278
295
|
--owner-address "0xOWNER_WALLET" \
|
|
279
296
|
--token-address "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" \
|
|
@@ -282,7 +299,7 @@ thirdfy-agent delegation create \
|
|
|
282
299
|
|
|
283
300
|
# Sign the returned `data.delegation` payload in your wallet, then:
|
|
284
301
|
thirdfy-agent delegation activate \
|
|
285
|
-
--
|
|
302
|
+
--owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" \
|
|
286
303
|
--agent-key "0xYOUR_AGENT_KEY" \
|
|
287
304
|
--wallet-address "0xOWNER_WALLET" \
|
|
288
305
|
--session-account-address "0xSESSION_ACCOUNT" \
|
|
@@ -292,25 +309,25 @@ thirdfy-agent delegation activate \
|
|
|
292
309
|
--json
|
|
293
310
|
|
|
294
311
|
thirdfy-agent delegation status \
|
|
295
|
-
--
|
|
312
|
+
--owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" \
|
|
296
313
|
--agent-key "0xYOUR_AGENT_KEY" \
|
|
297
314
|
--verify \
|
|
298
315
|
--json
|
|
299
316
|
|
|
300
317
|
thirdfy-agent delegation inspect \
|
|
301
|
-
--
|
|
318
|
+
--owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" \
|
|
302
319
|
--agent-key "0xYOUR_AGENT_KEY" \
|
|
303
320
|
--verify \
|
|
304
321
|
--json
|
|
305
322
|
|
|
306
323
|
thirdfy-agent delegation revoke \
|
|
307
|
-
--
|
|
324
|
+
--owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" \
|
|
308
325
|
--agent-key "0xYOUR_AGENT_KEY" \
|
|
309
326
|
--reason "user_revoked" \
|
|
310
327
|
--json
|
|
311
328
|
|
|
312
329
|
thirdfy-agent delegation balance \
|
|
313
|
-
--
|
|
330
|
+
--owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN" \
|
|
314
331
|
--agent-key "0xYOUR_AGENT_KEY" \
|
|
315
332
|
--json
|
|
316
333
|
|
|
@@ -323,6 +340,8 @@ thirdfy-agent delegation redeem \
|
|
|
323
340
|
--json
|
|
324
341
|
```
|
|
325
342
|
|
|
343
|
+
`--auth-token "$THIRDFY_AUTH_TOKEN"` remains supported for all delegation commands, but owner-session token is preferred for least-privilege owner operations.
|
|
344
|
+
|
|
326
345
|
### MetaMask custodial-compatible lifecycle (new)
|
|
327
346
|
|
|
328
347
|
```bash
|
package/bin/thirdfy-agent.mjs
CHANGED
|
@@ -23,7 +23,8 @@ const BITFINEX_REQUIRED_SCOPE_PERMISSIONS = {
|
|
|
23
23
|
funding: { read: 1, write: 1 },
|
|
24
24
|
wallets: { read: 1, write: 1 },
|
|
25
25
|
};
|
|
26
|
-
const RUN_MODES = ['thirdfy', 'self', 'hybrid'];
|
|
26
|
+
const RUN_MODES = ['thirdfy', 'self', 'hybrid', 'agent_wallet'];
|
|
27
|
+
const HYBRID_WALLET_MODES = ['self', 'agent_wallet'];
|
|
27
28
|
const CUSTODY_MODES = ['managed', 'external'];
|
|
28
29
|
const EFFECT_CHECK_MODES = ['strict', 'relaxed', 'off'];
|
|
29
30
|
const PROFILE_DEFAULTS = {
|
|
@@ -157,6 +158,9 @@ async function main() {
|
|
|
157
158
|
case 'managed run':
|
|
158
159
|
await commandManagedRun(context, subFlags, capabilities);
|
|
159
160
|
return;
|
|
161
|
+
case 'managed setup':
|
|
162
|
+
await commandManagedSetup(context, subFlags, capabilities);
|
|
163
|
+
return;
|
|
160
164
|
case 'jeff trade':
|
|
161
165
|
await commandRun(context, subFlags, capabilities);
|
|
162
166
|
return;
|
|
@@ -549,8 +553,9 @@ async function commandPreflight(ctx, flags, capabilities) {
|
|
|
549
553
|
const resolved = await resolveActionSelection(ctx, flags);
|
|
550
554
|
prepareActionParamsForFlags(flags, resolved.resolvedAction);
|
|
551
555
|
const runMode = getEffectiveRunMode(ctx, flags);
|
|
556
|
+
const hybridWalletMode = runMode === 'hybrid' ? normalizeHybridWalletMode(flags.hybridWalletMode) : null;
|
|
552
557
|
enforceChainCompatibility(capabilities, flags, runMode);
|
|
553
|
-
const backendResult = await runPreflightByMode(runMode, ctx, flags, resolved);
|
|
558
|
+
const backendResult = await runPreflightByMode(runMode, ctx, flags, resolved, { hybridWalletMode });
|
|
554
559
|
const normalized = applyExecutionFallbackHints(backendResult.normalized, {
|
|
555
560
|
flags,
|
|
556
561
|
runMode,
|
|
@@ -569,6 +574,7 @@ async function commandPreflight(ctx, flags, capabilities) {
|
|
|
569
574
|
resolvedAction: resolved.resolvedAction,
|
|
570
575
|
preflightRoute: backendResult.route,
|
|
571
576
|
runMode,
|
|
577
|
+
hybridWalletMode,
|
|
572
578
|
profile: ctx.profile || null,
|
|
573
579
|
swapInput: flags.__swapInput || null,
|
|
574
580
|
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
@@ -581,10 +587,12 @@ async function commandRun(ctx, flags, capabilities) {
|
|
|
581
587
|
const resolved = await resolveActionSelection(ctx, flags);
|
|
582
588
|
prepareActionParamsForFlags(flags, resolved.resolvedAction);
|
|
583
589
|
const runMode = getEffectiveRunMode(ctx, flags);
|
|
590
|
+
const hybridWalletMode = runMode === 'hybrid' ? normalizeHybridWalletMode(flags.hybridWalletMode) : null;
|
|
584
591
|
enforceChainCompatibility(capabilities, flags, runMode);
|
|
585
592
|
const skipPreflight = Boolean(flags.skipPreflight);
|
|
586
593
|
const backendResult = await runExecutionByMode(runMode, ctx, flags, resolved, {
|
|
587
594
|
skipPreflight,
|
|
595
|
+
hybridWalletMode,
|
|
588
596
|
});
|
|
589
597
|
const normalized = applyExecutionFallbackHints(backendResult.normalized, {
|
|
590
598
|
flags,
|
|
@@ -622,6 +630,7 @@ async function commandRun(ctx, flags, capabilities) {
|
|
|
622
630
|
resolvedAction: resolved.resolvedAction,
|
|
623
631
|
idempotencyKey: backendResult.idempotencyKey || null,
|
|
624
632
|
runMode,
|
|
633
|
+
hybridWalletMode,
|
|
625
634
|
profile: ctx.profile || null,
|
|
626
635
|
skippedPreflight: skipPreflight,
|
|
627
636
|
swapInput: flags.__swapInput || null,
|
|
@@ -952,8 +961,8 @@ async function commandDelegationBalance(ctx, flags, capabilities) {
|
|
|
952
961
|
|
|
953
962
|
async function commandDelegationRedeem(ctx, flags, capabilities) {
|
|
954
963
|
const selectedRunMode = getEffectiveRunMode(ctx, flags);
|
|
955
|
-
if (!['thirdfy', 'hybrid'].includes(selectedRunMode)) {
|
|
956
|
-
throw createCliError('DELEGATION_REDEEM_MODE_INVALID', 'delegation redeem supports only --run-mode thirdfy|hybrid');
|
|
964
|
+
if (!['thirdfy', 'hybrid', 'agent_wallet'].includes(selectedRunMode)) {
|
|
965
|
+
throw createCliError('DELEGATION_REDEEM_MODE_INVALID', 'delegation redeem supports only --run-mode thirdfy|hybrid|agent_wallet');
|
|
957
966
|
}
|
|
958
967
|
const delegatedFlags = {
|
|
959
968
|
...flags,
|
|
@@ -1591,6 +1600,83 @@ async function commandManagedRun(ctx, flags, capabilities) {
|
|
|
1591
1600
|
if (!normalized.success) process.exit(1);
|
|
1592
1601
|
}
|
|
1593
1602
|
|
|
1603
|
+
async function commandManagedSetup(ctx, flags, capabilities) {
|
|
1604
|
+
const mode = normalizeManagedRunMode(flags.runMode || 'agent_wallet');
|
|
1605
|
+
const authHeaders = buildOwnerAuthHeaders(
|
|
1606
|
+
flags,
|
|
1607
|
+
'Missing --auth-token/--owner-session-token (or THIRDFY_AUTH_TOKEN/THIRDFY_OWNER_SESSION_TOKEN) for managed setup'
|
|
1608
|
+
);
|
|
1609
|
+
const agentKey = requireFlag(flags, 'agentKey', 'Missing --agent-key');
|
|
1610
|
+
const chainId = Number(flags.chainId || 8453);
|
|
1611
|
+
const tokenAddress = String(flags.tokenAddress || '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913').trim();
|
|
1612
|
+
const maxUsdPerDay = Number(flags.maxUsdPerDay || 1000);
|
|
1613
|
+
const initResponse = await requestWithRouteFallback(ctx, {
|
|
1614
|
+
method: 'POST',
|
|
1615
|
+
routes: ['/api/v1/agent/delegation/init-custodial'],
|
|
1616
|
+
body: { chainId },
|
|
1617
|
+
headers: { ...authHeaders },
|
|
1618
|
+
});
|
|
1619
|
+
const grantResponse = await requestWithRouteFallback(ctx, {
|
|
1620
|
+
method: 'POST',
|
|
1621
|
+
routes: ['/api/v1/agent/delegation/custodial-grant'],
|
|
1622
|
+
body: {
|
|
1623
|
+
chainId,
|
|
1624
|
+
agentKey,
|
|
1625
|
+
tokenAddress,
|
|
1626
|
+
maxUsdPerDay,
|
|
1627
|
+
periodDuration: Number(flags.periodDuration || 86_400),
|
|
1628
|
+
expirySeconds: Number(flags.expirySeconds || 86_400),
|
|
1629
|
+
},
|
|
1630
|
+
headers: { ...authHeaders },
|
|
1631
|
+
});
|
|
1632
|
+
let readiness = null;
|
|
1633
|
+
let readinessError = null;
|
|
1634
|
+
try {
|
|
1635
|
+
readiness = await apiGet(
|
|
1636
|
+
ctx,
|
|
1637
|
+
`/api/v1/agent/delegation/check?agentKey=${encodeURIComponent(agentKey)}&chainId=${encodeURIComponent(String(chainId))}`,
|
|
1638
|
+
{ ...authHeaders }
|
|
1639
|
+
);
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
readinessError = {
|
|
1642
|
+
message: error?.message || 'Delegation readiness check failed',
|
|
1643
|
+
statusCode: Number(error?.statusCode || 0) || undefined,
|
|
1644
|
+
payload: error?.payload && typeof error.payload === 'object' ? error.payload : undefined,
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
const success = initResponse?.success === true && grantResponse?.success === true;
|
|
1648
|
+
printEnvelope({
|
|
1649
|
+
success,
|
|
1650
|
+
code: success ? 'MANAGED_SETUP_READY' : 'MANAGED_SETUP_FAILED',
|
|
1651
|
+
message: success
|
|
1652
|
+
? readinessError
|
|
1653
|
+
? `Managed wallet setup completed, but readiness check failed. Run with --run-mode ${mode}.`
|
|
1654
|
+
: `Managed wallet setup completed. Run with --run-mode ${mode}.`
|
|
1655
|
+
: 'Managed wallet setup failed',
|
|
1656
|
+
data: {
|
|
1657
|
+
runMode: mode,
|
|
1658
|
+
init: initResponse,
|
|
1659
|
+
grant: grantResponse,
|
|
1660
|
+
readiness: readiness ? normalizeDelegationStatus(readiness) : null,
|
|
1661
|
+
readinessError,
|
|
1662
|
+
},
|
|
1663
|
+
meta: {
|
|
1664
|
+
apiBase: ctx.apiBase,
|
|
1665
|
+
lane: 'managed_easy_wallet',
|
|
1666
|
+
agentKey,
|
|
1667
|
+
chainId,
|
|
1668
|
+
tokenAddress,
|
|
1669
|
+
maxUsdPerDay,
|
|
1670
|
+
upgradeHint:
|
|
1671
|
+
mode === 'agent_wallet'
|
|
1672
|
+
? 'Use --run-mode self for strict BYOW local-sign, or --run-mode agent_wallet for managed execution.'
|
|
1673
|
+
: null,
|
|
1674
|
+
capabilitiesVersion: capabilities?.contractVersion || null,
|
|
1675
|
+
},
|
|
1676
|
+
});
|
|
1677
|
+
if (!success) process.exit(1);
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1594
1680
|
async function commandProfileInit(ctx, flags) {
|
|
1595
1681
|
const current = loadProfileConfig();
|
|
1596
1682
|
const profile = normalizeProfileName(flags.profile || flags.name || 'personal');
|
|
@@ -1900,7 +1986,7 @@ function normalizeRunMode(value, flagName = '--run-mode') {
|
|
|
1900
1986
|
}
|
|
1901
1987
|
|
|
1902
1988
|
function defaultCustodyModeForRunMode(runMode) {
|
|
1903
|
-
return runMode === 'thirdfy' ? 'managed' : 'external';
|
|
1989
|
+
return runMode === 'thirdfy' || runMode === 'agent_wallet' ? 'managed' : 'external';
|
|
1904
1990
|
}
|
|
1905
1991
|
|
|
1906
1992
|
function normalizeCustodyMode(value, runModeHint = 'thirdfy') {
|
|
@@ -1914,11 +2000,17 @@ function normalizeCustodyMode(value, runModeHint = 'thirdfy') {
|
|
|
1914
2000
|
function normalizeManagedRunMode(value) {
|
|
1915
2001
|
const mode = normalizeRunMode(value || 'thirdfy');
|
|
1916
2002
|
if (mode === 'self') {
|
|
1917
|
-
throw new Error('Managed lane does not support run-mode=self. Use thirdfy or
|
|
2003
|
+
throw new Error('Managed lane does not support run-mode=self. Use thirdfy, hybrid, or agent_wallet.');
|
|
1918
2004
|
}
|
|
1919
2005
|
return mode;
|
|
1920
2006
|
}
|
|
1921
2007
|
|
|
2008
|
+
function normalizeHybridWalletMode(value) {
|
|
2009
|
+
const normalized = String(value || '').trim().toLowerCase() || 'self';
|
|
2010
|
+
if (HYBRID_WALLET_MODES.includes(normalized)) return normalized;
|
|
2011
|
+
throw new Error(`Unsupported --hybrid-wallet-mode "${value}". Supported: ${HYBRID_WALLET_MODES.join(', ')}`);
|
|
2012
|
+
}
|
|
2013
|
+
|
|
1922
2014
|
function getProfileConfigPath() {
|
|
1923
2015
|
return path.join(os.homedir(), '.thirdfy', 'config.json');
|
|
1924
2016
|
}
|
|
@@ -2076,6 +2168,7 @@ function applyExecutionFallbackHints(normalized, { flags, runMode, resolvedActio
|
|
|
2076
2168
|
if (
|
|
2077
2169
|
looksBitfinexAction &&
|
|
2078
2170
|
runMode !== 'thirdfy' &&
|
|
2171
|
+
runMode !== 'agent_wallet' &&
|
|
2079
2172
|
(topBlockedReason === 'SELF_MODE_REQUIRES_BUILD_TX' || topBlockedReason === 'HYBRID_MODE_REQUIRES_BUILD_TX')
|
|
2080
2173
|
) {
|
|
2081
2174
|
next.onboardingHint = {
|
|
@@ -2094,7 +2187,7 @@ function shouldUseBuildTxPreflight(runMode, resolved) {
|
|
|
2094
2187
|
return meta.supportsExecuteIntent === false && meta.supportsBuildTx === true;
|
|
2095
2188
|
}
|
|
2096
2189
|
|
|
2097
|
-
async function runPreflightByMode(runMode, ctx, flags, resolved) {
|
|
2190
|
+
async function runPreflightByMode(runMode, ctx, flags, resolved, options = {}) {
|
|
2098
2191
|
const resolvedAction = resolved.resolvedAction;
|
|
2099
2192
|
if (runMode === 'self') {
|
|
2100
2193
|
const route = shouldUseBuildTxPreflight(runMode, resolved) ? 'build_tx' : 'execute_intent_validation';
|
|
@@ -2115,6 +2208,44 @@ async function runPreflightByMode(runMode, ctx, flags, resolved) {
|
|
|
2115
2208
|
normalized,
|
|
2116
2209
|
};
|
|
2117
2210
|
}
|
|
2211
|
+
if (runMode === 'hybrid' && options.hybridWalletMode === 'agent_wallet') {
|
|
2212
|
+
const managedPayload = buildIntentPayload(flags, {
|
|
2213
|
+
validationOnly: true,
|
|
2214
|
+
forceIdempotency: false,
|
|
2215
|
+
resolvedAction,
|
|
2216
|
+
runMode: 'agent_wallet',
|
|
2217
|
+
mirrorOnly: false,
|
|
2218
|
+
hybridWalletMode: 'agent_wallet',
|
|
2219
|
+
});
|
|
2220
|
+
const managedResponse = await apiPost(ctx, '/api/v1/agent/execute-intent', managedPayload);
|
|
2221
|
+
const managedNormalized = normalizeIntentResponse(managedResponse);
|
|
2222
|
+
const mirrorPayload = buildIntentPayload(flags, {
|
|
2223
|
+
validationOnly: true,
|
|
2224
|
+
forceIdempotency: false,
|
|
2225
|
+
resolvedAction,
|
|
2226
|
+
runMode: 'hybrid',
|
|
2227
|
+
mirrorOnly: true,
|
|
2228
|
+
hybridWalletMode: 'agent_wallet',
|
|
2229
|
+
});
|
|
2230
|
+
const mirrorResponse = await apiPost(ctx, '/api/v1/agent/execute-intent', mirrorPayload);
|
|
2231
|
+
const mirrorNormalized = normalizeIntentResponse(mirrorResponse);
|
|
2232
|
+
return {
|
|
2233
|
+
message:
|
|
2234
|
+
managedNormalized.success && mirrorNormalized.success
|
|
2235
|
+
? 'Hybrid preflight completed (agent_wallet + thirdfy mirror)'
|
|
2236
|
+
: 'Hybrid preflight failed (agent_wallet + thirdfy mirror)',
|
|
2237
|
+
route: 'execute_intent_validation_dual',
|
|
2238
|
+
normalized: normalizeIntentResponse({
|
|
2239
|
+
success: Boolean(managedNormalized.success && mirrorNormalized.success),
|
|
2240
|
+
status: mirrorNormalized.status || managedNormalized.status || 'failed',
|
|
2241
|
+
mode: 'hybrid',
|
|
2242
|
+
hybridWalletMode: 'agent_wallet',
|
|
2243
|
+
managed: managedNormalized,
|
|
2244
|
+
mirror: mirrorNormalized,
|
|
2245
|
+
error: managedNormalized.error || mirrorNormalized.error || null,
|
|
2246
|
+
}),
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2118
2249
|
const payload = buildIntentPayload(flags, {
|
|
2119
2250
|
validationOnly: true,
|
|
2120
2251
|
forceIdempotency: false,
|
|
@@ -2138,8 +2269,8 @@ async function runPreflightByMode(runMode, ctx, flags, resolved) {
|
|
|
2138
2269
|
}
|
|
2139
2270
|
|
|
2140
2271
|
async function runExecutionByMode(runMode, ctx, flags, resolved, options) {
|
|
2141
|
-
if (runMode === 'thirdfy') {
|
|
2142
|
-
const normalized = await executeThirdfyRun(ctx, flags, resolved, options);
|
|
2272
|
+
if (runMode === 'thirdfy' || runMode === 'agent_wallet') {
|
|
2273
|
+
const normalized = await executeThirdfyRun(ctx, flags, resolved, options, runMode);
|
|
2143
2274
|
const preflightBlocked = !normalized.success && Boolean(normalized.preflightBlocked);
|
|
2144
2275
|
return {
|
|
2145
2276
|
code: normalized.success ? 'RUN_QUEUED' : preflightBlocked ? 'PREFLIGHT_BLOCKED' : 'RUN_FAILED',
|
|
@@ -2172,10 +2303,15 @@ async function runExecutionByMode(runMode, ctx, flags, resolved, options) {
|
|
|
2172
2303
|
}
|
|
2173
2304
|
const normalized = await executeHybridRun(ctx, flags, resolved, options);
|
|
2174
2305
|
const preflightBlocked = !normalized.success && Boolean(normalized.preflightBlocked);
|
|
2306
|
+
const hybridWalletMode = normalizeHybridWalletMode(
|
|
2307
|
+
normalized.hybridWalletMode || options?.hybridWalletMode || flags.hybridWalletMode || 'self',
|
|
2308
|
+
);
|
|
2175
2309
|
return {
|
|
2176
2310
|
code: normalized.success ? 'HYBRID_READY' : preflightBlocked ? 'PREFLIGHT_BLOCKED' : 'HYBRID_FAILED',
|
|
2177
2311
|
message: normalized.success
|
|
2178
|
-
?
|
|
2312
|
+
? hybridWalletMode === 'agent_wallet'
|
|
2313
|
+
? 'Hybrid execution prepared (agent wallet + mirror preflight)'
|
|
2314
|
+
: 'Hybrid execution prepared (self tx + mirror preflight)'
|
|
2179
2315
|
: preflightBlocked
|
|
2180
2316
|
? 'Preflight blocked hybrid execution'
|
|
2181
2317
|
: 'Hybrid execution failed',
|
|
@@ -2184,15 +2320,16 @@ async function runExecutionByMode(runMode, ctx, flags, resolved, options) {
|
|
|
2184
2320
|
};
|
|
2185
2321
|
}
|
|
2186
2322
|
|
|
2187
|
-
async function executeThirdfyRun(ctx, flags, resolved, options) {
|
|
2323
|
+
async function executeThirdfyRun(ctx, flags, resolved, options, runMode = 'thirdfy') {
|
|
2188
2324
|
const skipPreflight = Boolean(options?.skipPreflight);
|
|
2189
2325
|
if (!skipPreflight) {
|
|
2190
2326
|
const preflightPayload = buildIntentPayload(flags, {
|
|
2191
2327
|
validationOnly: true,
|
|
2192
2328
|
forceIdempotency: false,
|
|
2193
2329
|
resolvedAction: resolved.resolvedAction,
|
|
2194
|
-
runMode
|
|
2330
|
+
runMode,
|
|
2195
2331
|
mirrorOnly: false,
|
|
2332
|
+
hybridWalletMode: options?.hybridWalletMode,
|
|
2196
2333
|
});
|
|
2197
2334
|
const preflight = await apiPost(ctx, '/api/v1/agent/execute-intent', preflightPayload);
|
|
2198
2335
|
const preflightNormalized = normalizeIntentResponse(preflight);
|
|
@@ -2208,8 +2345,9 @@ async function executeThirdfyRun(ctx, flags, resolved, options) {
|
|
|
2208
2345
|
validationOnly: false,
|
|
2209
2346
|
forceIdempotency: true,
|
|
2210
2347
|
resolvedAction: resolved.resolvedAction,
|
|
2211
|
-
runMode
|
|
2348
|
+
runMode,
|
|
2212
2349
|
mirrorOnly: false,
|
|
2350
|
+
hybridWalletMode: options?.hybridWalletMode,
|
|
2213
2351
|
});
|
|
2214
2352
|
const response = await apiPost(ctx, '/api/v1/agent/execute-intent', payload);
|
|
2215
2353
|
const normalized = normalizeIntentResponse(response);
|
|
@@ -2261,6 +2399,36 @@ async function executeSelfRun(ctx, flags, resolved, options) {
|
|
|
2261
2399
|
}
|
|
2262
2400
|
|
|
2263
2401
|
async function executeHybridRun(ctx, flags, resolved, options) {
|
|
2402
|
+
const hybridWalletMode = normalizeHybridWalletMode(options?.hybridWalletMode || flags.hybridWalletMode || 'self');
|
|
2403
|
+
if (hybridWalletMode === 'agent_wallet') {
|
|
2404
|
+
const managedResult = await executeThirdfyRun(ctx, flags, resolved, options, 'agent_wallet');
|
|
2405
|
+
if (!managedResult.success) {
|
|
2406
|
+
return normalizeIntentResponse({
|
|
2407
|
+
...managedResult,
|
|
2408
|
+
mode: 'hybrid',
|
|
2409
|
+
hybridWalletMode,
|
|
2410
|
+
});
|
|
2411
|
+
}
|
|
2412
|
+
const mirrorPayload = buildIntentPayload(flags, {
|
|
2413
|
+
validationOnly: true,
|
|
2414
|
+
forceIdempotency: true,
|
|
2415
|
+
resolvedAction: resolved.resolvedAction,
|
|
2416
|
+
runMode: 'hybrid',
|
|
2417
|
+
mirrorOnly: true,
|
|
2418
|
+
hybridWalletMode,
|
|
2419
|
+
});
|
|
2420
|
+
const mirrorResponse = await apiPost(ctx, '/api/v1/agent/execute-intent', mirrorPayload);
|
|
2421
|
+
const mirrorNormalized = normalizeIntentResponse(mirrorResponse);
|
|
2422
|
+
return normalizeIntentResponse({
|
|
2423
|
+
success: Boolean(managedResult.success && mirrorNormalized.success),
|
|
2424
|
+
status: managedResult.status || mirrorNormalized.status || 'queued',
|
|
2425
|
+
mode: 'hybrid',
|
|
2426
|
+
hybridWalletMode,
|
|
2427
|
+
idempotencyKey: managedResult.idempotencyKey || mirrorPayload.idempotencyKey,
|
|
2428
|
+
managed: managedResult,
|
|
2429
|
+
mirror: mirrorNormalized,
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2264
2432
|
const selfResult = await executeSelfRun(ctx, flags, resolved, {
|
|
2265
2433
|
...(options || {}),
|
|
2266
2434
|
runMode: 'hybrid',
|
|
@@ -2281,6 +2449,7 @@ async function executeHybridRun(ctx, flags, resolved, options) {
|
|
|
2281
2449
|
success: Boolean(selfResult.success && mirrorNormalized.success),
|
|
2282
2450
|
status: mirrorNormalized.status || 'ready',
|
|
2283
2451
|
mode: 'hybrid',
|
|
2452
|
+
hybridWalletMode,
|
|
2284
2453
|
idempotencyKey: mirrorPayload.idempotencyKey,
|
|
2285
2454
|
self: selfResult,
|
|
2286
2455
|
mirror: mirrorNormalized,
|
|
@@ -2315,6 +2484,7 @@ function buildIntentPayload(flags, options) {
|
|
|
2315
2484
|
payload.executionStrategy = {
|
|
2316
2485
|
runMode: String(options.runMode),
|
|
2317
2486
|
mirrorOnly: Boolean(options.mirrorOnly),
|
|
2487
|
+
...(options.hybridWalletMode ? { hybridWalletMode: String(options.hybridWalletMode) } : {}),
|
|
2318
2488
|
};
|
|
2319
2489
|
}
|
|
2320
2490
|
if (options.forceIdempotency) {
|
|
@@ -2473,17 +2643,26 @@ function getAuthToken(flags, missingMessage) {
|
|
|
2473
2643
|
|
|
2474
2644
|
function buildOwnerAuthHeaders(flags, missingMessage) {
|
|
2475
2645
|
const config = loadProfileConfig();
|
|
2476
|
-
const
|
|
2477
|
-
const
|
|
2478
|
-
|
|
2479
|
-
).trim();
|
|
2480
|
-
|
|
2481
|
-
|
|
2646
|
+
const explicitOwnerSessionToken = String(flags.ownerSessionToken || '').trim();
|
|
2647
|
+
const explicitAuthToken = String(flags.authToken || '').trim();
|
|
2648
|
+
const ownerSessionToken = String(process.env.THIRDFY_OWNER_SESSION_TOKEN || config?.auth?.ownerSessionToken || '').trim();
|
|
2649
|
+
const authToken = String(process.env.THIRDFY_AUTH_TOKEN || config?.auth?.authToken || '').trim();
|
|
2650
|
+
|
|
2651
|
+
// Prefer explicit flags first; then prefer owner-session over bearer from env/config
|
|
2652
|
+
// to keep owner operations on least-privilege credentials by default.
|
|
2653
|
+
if (explicitOwnerSessionToken) {
|
|
2654
|
+
return { 'x-owner-session-token': explicitOwnerSessionToken };
|
|
2655
|
+
}
|
|
2656
|
+
if (explicitAuthToken) {
|
|
2657
|
+
return { Authorization: `Bearer ${explicitAuthToken}` };
|
|
2658
|
+
}
|
|
2659
|
+
if (ownerSessionToken) {
|
|
2660
|
+
return { 'x-owner-session-token': ownerSessionToken };
|
|
2482
2661
|
}
|
|
2483
2662
|
if (authToken) {
|
|
2484
2663
|
return { Authorization: `Bearer ${authToken}` };
|
|
2485
2664
|
}
|
|
2486
|
-
|
|
2665
|
+
throw new Error(missingMessage);
|
|
2487
2666
|
}
|
|
2488
2667
|
|
|
2489
2668
|
function extractActions(response) {
|
|
@@ -2969,8 +3148,8 @@ function printHelp() {
|
|
|
2969
3148
|
thirdfy-agent ${CLI_VERSION}
|
|
2970
3149
|
|
|
2971
3150
|
Core commands:
|
|
2972
|
-
thirdfy-agent profile init [--profile personal|builder|network] [--run-mode thirdfy|self|hybrid] [--custody-mode managed|external] [--json]
|
|
2973
|
-
thirdfy-agent profile use --profile <name> [--run-mode thirdfy|self|hybrid] [--custody-mode managed|external] [--json]
|
|
3151
|
+
thirdfy-agent profile init [--profile personal|builder|network] [--run-mode thirdfy|self|hybrid|agent_wallet] [--custody-mode managed|external] [--json]
|
|
3152
|
+
thirdfy-agent profile use --profile <name> [--run-mode thirdfy|self|hybrid|agent_wallet] [--custody-mode managed|external] [--json]
|
|
2974
3153
|
thirdfy-agent profile show [--json]
|
|
2975
3154
|
thirdfy-agent config set --key <path> --value <value> [--json]
|
|
2976
3155
|
thirdfy-agent login [--auth-token <token> | --owner-session-token <token>] [--agent-api-key <key>] [--agent-key <key>] [--run-mode <mode>] [--custody-mode managed|external] [--wallet-address <addr>] [--json]
|
|
@@ -2978,19 +3157,20 @@ Core commands:
|
|
|
2978
3157
|
thirdfy-agent whoami [--json]
|
|
2979
3158
|
thirdfy-agent catalogs list [--json]
|
|
2980
3159
|
thirdfy-agent actions [--catalog <id>] [--provider <id>] [--agent-api-key <key>] [--chain-id <id>] [--run-mode <mode>] [--json]
|
|
2981
|
-
thirdfy-agent delegation create --auth-token <token> --agent-key <key> --owner-address <addr> --token-address <addr> --max-usd-per-day <usd> [--session-account-address <addr>] [--json]
|
|
3160
|
+
thirdfy-agent delegation create [--auth-token <token> | --owner-session-token <token>] --agent-key <key> --owner-address <addr> --token-address <addr> --max-usd-per-day <usd> [--session-account-address <addr>] [--json]
|
|
2982
3161
|
thirdfy-agent delegation init-custodial [--auth-token <token> | --owner-session-token <token>] [--chain-id <id>] [--json]
|
|
2983
3162
|
thirdfy-agent delegation custodial-grant [--auth-token <token> | --owner-session-token <token>] --agent-key <key> --token-address <addr> --max-usd-per-day <usd> [--chain-id <id>] [--period-duration <sec>] [--expiry-seconds <sec>] [--json]
|
|
2984
|
-
thirdfy-agent delegation activate --auth-token <token> --agent-key <key> --wallet-address <addr> --session-account-address <addr> --delegation-manager <addr> --delegation <json> --signature <hex> [--json]
|
|
2985
|
-
thirdfy-agent delegation status --auth-token <token> --agent-key <key> [--verify] [--json]
|
|
2986
|
-
thirdfy-agent delegation show --auth-token <token> --agent-key <key> [--verify] [--json]
|
|
2987
|
-
thirdfy-agent delegation inspect --auth-token <token> --agent-key <key> [--verify] [--json]
|
|
2988
|
-
thirdfy-agent delegation balance --auth-token <token> --agent-key <key> [--chain-id <id>] [--json]
|
|
2989
|
-
thirdfy-agent delegation revoke --auth-token <token> --agent-key <key> [--reason <text>] [--chain-id <id>] [--json]
|
|
2990
|
-
thirdfy-agent delegation redeem --agent-api-key <key> --action <id> --params <json> [--run-mode thirdfy|hybrid] [--estimated-amount-usd <usd>] [--json]
|
|
3163
|
+
thirdfy-agent delegation activate [--auth-token <token> | --owner-session-token <token>] --agent-key <key> --wallet-address <addr> --session-account-address <addr> --delegation-manager <addr> --delegation <json> --signature <hex> [--json]
|
|
3164
|
+
thirdfy-agent delegation status [--auth-token <token> | --owner-session-token <token>] --agent-key <key> [--verify] [--json]
|
|
3165
|
+
thirdfy-agent delegation show [--auth-token <token> | --owner-session-token <token>] --agent-key <key> [--verify] [--json]
|
|
3166
|
+
thirdfy-agent delegation inspect [--auth-token <token> | --owner-session-token <token>] --agent-key <key> [--verify] [--json]
|
|
3167
|
+
thirdfy-agent delegation balance [--auth-token <token> | --owner-session-token <token>] --agent-key <key> [--chain-id <id>] [--json]
|
|
3168
|
+
thirdfy-agent delegation revoke [--auth-token <token> | --owner-session-token <token>] --agent-key <key> [--reason <text>] [--chain-id <id>] [--json]
|
|
3169
|
+
thirdfy-agent delegation redeem --agent-api-key <key> --action <id> --params <json> [--run-mode thirdfy|hybrid|agent_wallet] [--estimated-amount-usd <usd>] [--json]
|
|
2991
3170
|
thirdfy-agent managed wallet init [--auth-token <token> | --owner-session-token <token>] [--chain-id <id>] [--json]
|
|
2992
3171
|
thirdfy-agent managed wallet grant [--auth-token <token> | --owner-session-token <token>] --agent-key <key> --token-address <addr> --max-usd-per-day <usd> [--chain-id <id>] [--period-duration <sec>] [--expiry-seconds <sec>] [--json]
|
|
2993
|
-
thirdfy-agent managed
|
|
3172
|
+
thirdfy-agent managed setup [--auth-token <token> | --owner-session-token <token>] --agent-key <key> [--run-mode thirdfy|hybrid|agent_wallet] [--token-address <addr>] [--max-usd-per-day <usd>] [--chain-id <id>] [--json]
|
|
3173
|
+
thirdfy-agent managed run --agent-api-key <key> --action <id> --params <json> [--run-mode thirdfy|hybrid|agent_wallet] [--idempotency-key <key>] [--json]
|
|
2994
3174
|
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]
|
|
2995
3175
|
thirdfy-agent credentials status --auth-token <token> [--venue <id>] [--json]
|
|
2996
3176
|
thirdfy-agent agent auth challenge --agent-key <key> [--wallet-address <addr>] [--chain-id <id>] [--json]
|
|
@@ -3017,7 +3197,8 @@ Core commands:
|
|
|
3017
3197
|
Global flags:
|
|
3018
3198
|
--api-base <url> default: https://api.thirdfy.com
|
|
3019
3199
|
--mcp-base <url> default: https://mcp.thirdfy.com (bootstrap token issuance)
|
|
3020
|
-
--run-mode <mode> thirdfy | self | hybrid
|
|
3200
|
+
--run-mode <mode> thirdfy | self | hybrid | agent_wallet
|
|
3201
|
+
--hybrid-wallet-mode <mode> self | agent_wallet (only with --run-mode hybrid)
|
|
3021
3202
|
--custody-mode <mode> managed | external
|
|
3022
3203
|
--profile <name> personal | builder | network
|
|
3023
3204
|
--env <file> load env vars from file
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thirdfy/agent-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"thirdfy-agent": "
|
|
7
|
+
"thirdfy-agent": "bin/thirdfy-agent.mjs"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
@@ -57,6 +57,8 @@ function normalizeIntentResponse(response) {
|
|
|
57
57
|
idempotencyKey: response?.idempotencyKey || null,
|
|
58
58
|
mirror: response?.mirror || null,
|
|
59
59
|
self: response?.self || null,
|
|
60
|
+
managed: response?.managed || null,
|
|
61
|
+
hybridWalletMode: response?.hybridWalletMode || null,
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
64
|
|