@thirdfy/agent-cli 0.2.33 → 0.2.34
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 +3 -2
- package/package.json +1 -1
- package/src/runtime/execution/runners.mjs +81 -27
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.2.34] - 2026-07-25
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Read-only actions run under `--run-mode agent_wallet` no longer go through the managed-wallet execute rail. `run`, `preflight`, and pack callers that request `get_*`, `list_*`, or `search_*` actions (or any catalog action with `supportsExecute: false`) are routed to the read rail up front, so the provider payload is returned as-is instead of being reshaped into an execution intent envelope (`intentId`, `blocked`, `executed`).
|
|
12
|
+
- Reads on `agent_wallet` are no longer gated on execution-wallet funding. The execution wallet address is still resolved and reported for response parity, but an unfunded wallet does not block a read. Every other preflight failure (`MISSING_USER_DID`, `EXECUTION_WALLET_MISMATCH`, and other routing checks) still fails closed with `PREFLIGHT_BLOCKED`, exactly as it does for a write.
|
|
13
|
+
|
|
14
|
+
Operator impact: `get_hyperliquid_perps_meta` and `get_hyperliquid_all_mids` previously returned an envelope with the market universe buried under `data.raw`, which consumers could not parse. Hyperliquid agents fell back to a stale cached universe and reported no trading edge. Upgrade to 0.2.34 on every runtime that executes with `--run-mode agent_wallet`.
|
|
15
|
+
|
|
7
16
|
## [0.2.33] - 2026-07-24
|
|
8
17
|
|
|
9
18
|
### Fixed
|
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.34
|
|
44
44
|
|
|
45
|
-
- `
|
|
45
|
+
- Read-only actions (`get_*`, `list_*`, `search_*`) requested with `--run-mode agent_wallet` now return the provider payload directly instead of an execution envelope. Market data reads such as `get_hyperliquid_perps_meta` no longer come back wrapped as an intent.
|
|
46
|
+
- Reads are no longer gated on execution-wallet funding. The execution address is still reported, but an unfunded wallet does not block a read. Routing and identity checks still apply.
|
|
46
47
|
|
|
47
48
|
Older versions: see [CHANGELOG.md](./CHANGELOG.md) and [GitHub Releases](https://github.com/thirdfy/agent-cli/releases).
|
|
48
49
|
|
package/package.json
CHANGED
|
@@ -79,15 +79,29 @@ function applyExecutionFallbackHints(normalized, { flags, runMode, resolvedActio
|
|
|
79
79
|
return next;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
function
|
|
82
|
+
function isReadOnlyAction(resolved) {
|
|
83
83
|
const meta = resolved?.resolvedActionMeta || {};
|
|
84
84
|
const action = String(resolved?.resolvedAction || '')
|
|
85
85
|
.trim()
|
|
86
86
|
.toLowerCase()
|
|
87
87
|
.replace(/-/g, '_');
|
|
88
|
-
const readOnlyByName =
|
|
88
|
+
const readOnlyByName =
|
|
89
|
+
action.startsWith('get_') || action.startsWith('search_') || action.startsWith('list_');
|
|
89
90
|
const readOnlyByMeta = meta.supportsExecute === false && meta.supportsExecuteIntent !== false;
|
|
90
|
-
|
|
91
|
+
return readOnlyByName || readOnlyByMeta;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Reads carry no transaction, so wallet balance never blocks them. Every other preflight failure
|
|
95
|
+
// (identity, routing, wallet mismatch) still fails closed, same as a write.
|
|
96
|
+
const FUNDING_ONLY_PREFLIGHT_BLOCKS = new Set(['INSUFFICIENT_FUNDS', 'INVALID_FUNDING_BALANCE']);
|
|
97
|
+
|
|
98
|
+
function isFundingOnlyPreflightBlock(preflight) {
|
|
99
|
+
const reason = String(preflight?.blockedReason || '').trim().toUpperCase();
|
|
100
|
+
return FUNDING_ONLY_PREFLIGHT_BLOCKS.has(reason);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function shouldFallbackAgentWalletToExecuteIntent(resolved, response) {
|
|
104
|
+
if (!isReadOnlyAction(resolved)) return false;
|
|
91
105
|
const err = String(response?.error || response?.message || '').toLowerCase();
|
|
92
106
|
return err.includes('does not support execute rail');
|
|
93
107
|
}
|
|
@@ -97,7 +111,8 @@ async function runPreflightByMode(runMode, ctx, flags, resolved, options = {}) {
|
|
|
97
111
|
if (runMode === 'agent_wallet') {
|
|
98
112
|
const preflight = await resolveManagedExecutionPreflight(ctx, flags, resolved, {
|
|
99
113
|
runMode,
|
|
100
|
-
|
|
114
|
+
// Reads carry no transaction; keep funding optional so preflight matches run.
|
|
115
|
+
requireFundingCheck: !isReadOnlyAction(resolved),
|
|
101
116
|
preflightSeed: options.managedPreflight,
|
|
102
117
|
});
|
|
103
118
|
const normalized = normalizeIntentResponse({
|
|
@@ -304,7 +319,68 @@ async function executeThirdfyRun(ctx, flags, resolved, options, runMode = 'third
|
|
|
304
319
|
return normalized;
|
|
305
320
|
}
|
|
306
321
|
|
|
322
|
+
async function runReadOnlyActionViaIntentRail(ctx, flags, resolved, options, preflight) {
|
|
323
|
+
const userDid = resolveEffectiveUserDid(flags, {
|
|
324
|
+
runMode: 'agent_wallet',
|
|
325
|
+
hybridWalletMode: options?.hybridWalletMode,
|
|
326
|
+
preferDelegationIdentity: options?.preferDelegationIdentity,
|
|
327
|
+
});
|
|
328
|
+
const intentResult = await executeThirdfyRun(
|
|
329
|
+
ctx,
|
|
330
|
+
{
|
|
331
|
+
...flags,
|
|
332
|
+
chainId: Number(flags.chainId || 8453),
|
|
333
|
+
runMode: 'thirdfy',
|
|
334
|
+
userDid: userDid || flags.userDid,
|
|
335
|
+
executionScope: flags.executionScope || 'solo_owner_mirror',
|
|
336
|
+
},
|
|
337
|
+
resolved,
|
|
338
|
+
{ ...options, skipPreflight: true },
|
|
339
|
+
'thirdfy'
|
|
340
|
+
);
|
|
341
|
+
intentResult.mode = 'agent_wallet';
|
|
342
|
+
intentResult.routeFallback = 'execute_intent_read_only';
|
|
343
|
+
if (preflight) {
|
|
344
|
+
intentResult.executionWalletPreflight = preflight;
|
|
345
|
+
intentResult.executionWalletAddress = preflight.executionWalletAddress || null;
|
|
346
|
+
}
|
|
347
|
+
intentResult.signerMethod = 'managed_wallet_server';
|
|
348
|
+
return intentResult;
|
|
349
|
+
}
|
|
350
|
+
|
|
307
351
|
async function executeManagedWalletRun(ctx, flags, resolved, options) {
|
|
352
|
+
// Read-only actions carry no transaction. Routing them through the managed-wallet execute rail
|
|
353
|
+
// returns an execution envelope instead of the queried data, which silently starves callers that
|
|
354
|
+
// asked for market state (see earnclaw-api HL universe probe regression, 2026-07-25).
|
|
355
|
+
// The wallet address is still resolved for response parity, but a read is never gated on funding.
|
|
356
|
+
if (isReadOnlyAction(resolved)) {
|
|
357
|
+
// Do not swallow lookup/routing errors: writes let them propagate, and reads must fail closed
|
|
358
|
+
// the same way (wallet mismatch, missing identity, failed /execution-wallet).
|
|
359
|
+
const readPreflight = await resolveManagedExecutionPreflight(ctx, flags, resolved, {
|
|
360
|
+
runMode: 'agent_wallet',
|
|
361
|
+
requireFundingCheck: false,
|
|
362
|
+
preflightSeed: options?.managedPreflight,
|
|
363
|
+
});
|
|
364
|
+
// Funding is intentionally skipped for reads (`requireFundingCheck: false`), but routing
|
|
365
|
+
// failures (wallet mismatch, missing identity, failed address seed) must still fail closed.
|
|
366
|
+
if (readPreflight && readPreflight.success === false && !isFundingOnlyPreflightBlock(readPreflight)) {
|
|
367
|
+
const normalized = normalizeIntentResponse({
|
|
368
|
+
success: false,
|
|
369
|
+
status: 'failed',
|
|
370
|
+
mode: 'agent_wallet',
|
|
371
|
+
blockedReason: readPreflight.blockedReason || 'PRECHECK_FAILED',
|
|
372
|
+
blockedStage: readPreflight.blockedStage || 'routing',
|
|
373
|
+
error: readPreflight.error || 'Managed wallet preflight failed',
|
|
374
|
+
preflightBlocked: true,
|
|
375
|
+
executionWalletAddress: readPreflight.executionWalletAddress || null,
|
|
376
|
+
executionWalletPreflight: readPreflight,
|
|
377
|
+
});
|
|
378
|
+
normalized.executionWalletAddress = readPreflight.executionWalletAddress || null;
|
|
379
|
+
normalized.executionWalletPreflight = readPreflight;
|
|
380
|
+
return normalized;
|
|
381
|
+
}
|
|
382
|
+
return runReadOnlyActionViaIntentRail(ctx, flags, resolved, options, readPreflight);
|
|
383
|
+
}
|
|
308
384
|
const skipPreflight = Boolean(options?.skipPreflight);
|
|
309
385
|
let preflight = null;
|
|
310
386
|
if (!skipPreflight) {
|
|
@@ -338,34 +414,12 @@ async function executeManagedWalletRun(ctx, flags, resolved, options) {
|
|
|
338
414
|
const actionCtx = withActionTimeout(ctx, resolved.resolvedAction);
|
|
339
415
|
const response = await apiPost(actionCtx, '/api/v1/agent/execute', payload);
|
|
340
416
|
if (shouldFallbackAgentWalletToExecuteIntent(resolved, response)) {
|
|
341
|
-
const
|
|
342
|
-
runMode: 'agent_wallet',
|
|
343
|
-
hybridWalletMode: options.hybridWalletMode,
|
|
344
|
-
preferDelegationIdentity: options.preferDelegationIdentity,
|
|
345
|
-
});
|
|
346
|
-
const chainId = Number(flags.chainId || 8453);
|
|
347
|
-
const intentResult = await executeThirdfyRun(
|
|
348
|
-
ctx,
|
|
349
|
-
{
|
|
350
|
-
...flags,
|
|
351
|
-
chainId,
|
|
352
|
-
runMode: 'thirdfy',
|
|
353
|
-
userDid: userDid || flags.userDid,
|
|
354
|
-
executionScope: flags.executionScope || 'solo_owner_mirror',
|
|
355
|
-
},
|
|
356
|
-
resolved,
|
|
357
|
-
{ ...options, skipPreflight: true },
|
|
358
|
-
'thirdfy'
|
|
359
|
-
);
|
|
360
|
-
intentResult.mode = 'agent_wallet';
|
|
361
|
-
intentResult.routeFallback = 'execute_intent_read_only';
|
|
362
|
-
intentResult.executionWalletPreflight = preflight;
|
|
417
|
+
const intentResult = await runReadOnlyActionViaIntentRail(ctx, flags, resolved, options, preflight);
|
|
363
418
|
intentResult.executionWalletAddress =
|
|
364
419
|
preflight?.executionWalletAddress ||
|
|
365
420
|
response?.executionWalletAddress ||
|
|
366
421
|
response?.data?.executionWalletAddress ||
|
|
367
422
|
null;
|
|
368
|
-
intentResult.signerMethod = 'managed_wallet_server';
|
|
369
423
|
return intentResult;
|
|
370
424
|
}
|
|
371
425
|
const normalized = normalizeIntentResponse({
|