@thirdfy/agent-cli 0.2.70 → 0.2.72
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 +12 -0
- package/README.md +2 -3
- package/package.json +1 -1
- package/src/core/actionTimeouts.mjs +3 -1
- package/src/runtime/certification/venueActionSchemas.mjs +36 -1
- package/src/runtime/execution/lanes.mjs +2 -0
- package/src/runtime/execution/managedReadExecuteRail.mjs +14 -0
- package/src/runtime/execution/runners.mjs +6 -1
- package/src/runtime/providerHints.mjs +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.2.72] - 2026-09-17
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Hyperliquid `place_hyperliquid_tp_sl` certification fixture, write lane, and provider hint. Lighter `place_lighter_perps_order` `orderType` enum includes `stop_loss`, `stop_loss_limit`, `take_profit`, `take_profit_limit`. Pin: `test/read-only-action-classifier.test.cjs`. Pairs with `thirdfy-mcp` **0.0.122** and Thirdfy API **3.14.50**.
|
|
12
|
+
|
|
13
|
+
## [0.2.71] - 2026-09-16
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `get_avantis_setup_status` / `get_avantis_onboarding_plan` use the 180s compose timeout. Empty queued `/execute-intent` fallbacks fail as `MANAGED_READ_EMPTY_EXECUTE_INTENT` instead of a successful empty setup payload. Pin: `test/actionTimeouts.test.cjs`, `test/agent-wallet-readonly-fallback.test.cjs`. Matches `thirdfy-mcp` **0.0.121**.
|
|
18
|
+
|
|
7
19
|
## [0.2.70] - 2026-09-16
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/README.md
CHANGED
|
@@ -40,10 +40,9 @@ 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.72
|
|
44
44
|
|
|
45
|
-
-
|
|
46
|
-
- Hosted runtime write keys stay scoped by runtime UUID so Thirdfy action events can join.
|
|
45
|
+
- Hyperliquid `place_hyperliquid_tp_sl` is a write. Lighter `place_lighter_perps_order` accepts stop-loss and take-profit `orderType` values with `triggerPrice`.
|
|
47
46
|
- See [CHANGELOG.md](./CHANGELOG.md) and GitHub Releases for older versions.
|
|
48
47
|
|
|
49
48
|
## Quick start
|
package/package.json
CHANGED
|
@@ -2,6 +2,8 @@ import { DEFAULT_TIMEOUT_MS } from './constants.mjs';
|
|
|
2
2
|
|
|
3
3
|
const LONG_RUNNING_ACTIONS = new Set([
|
|
4
4
|
'complete_avantis_onboarding',
|
|
5
|
+
'get_avantis_setup_status',
|
|
6
|
+
'get_avantis_onboarding_plan',
|
|
5
7
|
'complete_lighter_onboarding',
|
|
6
8
|
'setup_lighter_api_key',
|
|
7
9
|
'register_lighter_api_key',
|
|
@@ -23,7 +25,7 @@ function normalizeActionId(action) {
|
|
|
23
25
|
.toLowerCase();
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
/** Longer HTTP timeout for Lighter onboarding and Morpho/Aave earn writes
|
|
28
|
+
/** Longer HTTP timeout for Avantis/Lighter onboarding, Avantis setup-status compose, and Morpho/Aave earn writes. */
|
|
27
29
|
export function resolveActionTimeoutMs(action, baseTimeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
28
30
|
const base = Number.isFinite(baseTimeoutMs) && baseTimeoutMs > 0 ? baseTimeoutMs : DEFAULT_TIMEOUT_MS;
|
|
29
31
|
if (!LONG_RUNNING_ACTIONS.has(normalizeActionId(action))) return base;
|
|
@@ -47,6 +47,37 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
|
|
|
47
47
|
},
|
|
48
48
|
validSample: { coin: 'ETH', orderId: 1 },
|
|
49
49
|
},
|
|
50
|
+
{
|
|
51
|
+
action: 'place_hyperliquid_tp_sl',
|
|
52
|
+
aliases: ['place-hyperliquid-tp-sl'],
|
|
53
|
+
provider: 'hyperliquid',
|
|
54
|
+
authorityInputs: ['agentApiKey', 'estimatedAmountUsd'],
|
|
55
|
+
paramsSchema: {
|
|
56
|
+
type: 'object',
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
required: ['coin', 'tpsl', 'triggerPx', 'size', 'isBuy'],
|
|
59
|
+
properties: {
|
|
60
|
+
coin: { type: 'string', minLength: 1 },
|
|
61
|
+
tpsl: { type: 'string', enum: ['tp', 'sl'] },
|
|
62
|
+
triggerPx: { type: 'number', minimum: 0 },
|
|
63
|
+
isMarket: { type: 'boolean' },
|
|
64
|
+
limitPx: { type: 'number', minimum: 0 },
|
|
65
|
+
size: { type: 'number', minimum: 0 },
|
|
66
|
+
isBuy: { type: 'boolean' },
|
|
67
|
+
reduceOnly: { type: 'boolean' },
|
|
68
|
+
grouping: { type: 'string', enum: ['na', 'normalTpsl', 'positionTpsl'] },
|
|
69
|
+
userAddress: { type: 'string' },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
validSample: {
|
|
73
|
+
coin: 'ETH',
|
|
74
|
+
tpsl: 'sl',
|
|
75
|
+
triggerPx: 3000,
|
|
76
|
+
size: 0.01,
|
|
77
|
+
isBuy: false,
|
|
78
|
+
reduceOnly: true,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
50
81
|
{
|
|
51
82
|
action: 'update_hyperliquid_leverage',
|
|
52
83
|
aliases: ['update-hyperliquid-leverage'],
|
|
@@ -82,7 +113,11 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
|
|
|
82
113
|
baseAmount: { type: 'integer', minimum: 1 },
|
|
83
114
|
price: { type: 'integer', minimum: 1 },
|
|
84
115
|
isAsk: { type: 'boolean' },
|
|
85
|
-
orderType: {
|
|
116
|
+
orderType: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
enum: ['limit', 'market', 'stop_loss', 'stop_loss_limit', 'take_profit', 'take_profit_limit'],
|
|
119
|
+
},
|
|
120
|
+
triggerPrice: { type: 'integer', minimum: 1 },
|
|
86
121
|
reduceOnly: { type: 'boolean' },
|
|
87
122
|
},
|
|
88
123
|
},
|
|
@@ -3,6 +3,8 @@ export function createExecutionLanes() {
|
|
|
3
3
|
const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
|
|
4
4
|
'place_hyperliquid_perps_order',
|
|
5
5
|
'place-hyperliquid-perps-order',
|
|
6
|
+
'place_hyperliquid_tp_sl',
|
|
7
|
+
'place-hyperliquid-tp-sl',
|
|
6
8
|
'cancel_hyperliquid_perps_order',
|
|
7
9
|
'cancel-hyperliquid-perps-order',
|
|
8
10
|
'update_hyperliquid_leverage',
|
|
@@ -12,6 +12,20 @@ export function managedReadExecuteErrorText(data) {
|
|
|
12
12
|
return String(data?.error || data?.message || data?.code || data?.blockedReason || '').toLowerCase();
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
export function isEmptyQueuedExecuteIntent(data) {
|
|
16
|
+
const rec = data && typeof data === 'object' && !Array.isArray(data) ? data : null;
|
|
17
|
+
if (!rec) return false;
|
|
18
|
+
// Read payloads live in `data`. Do not mix those fields with outer batch defaults —
|
|
19
|
+
// `normalizeIntentResponse` materializes status=queued, executed=0, results=[].
|
|
20
|
+
const nested = rec.data && typeof rec.data === 'object' && !Array.isArray(rec.data) ? rec.data : rec;
|
|
21
|
+
const status = String(nested.status ?? '').toLowerCase();
|
|
22
|
+
const executed = Number(nested.executed);
|
|
23
|
+
const results = nested.results;
|
|
24
|
+
const emptyResults = Array.isArray(results) && results.length === 0;
|
|
25
|
+
const queued = status === 'queued' || (Number.isFinite(executed) && executed === 0);
|
|
26
|
+
return queued && emptyResults;
|
|
27
|
+
}
|
|
28
|
+
|
|
15
29
|
export function shouldFallbackManagedReadToExecuteIntent({ isReadOnly, errorText } = {}) {
|
|
16
30
|
if (!isReadOnly) return false;
|
|
17
31
|
const err = String(errorText || '').toLowerCase();
|
|
@@ -12,7 +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 { shouldFallbackManagedReadToExecuteIntent } from './managedReadExecuteRail.mjs';
|
|
15
|
+
import { isEmptyQueuedExecuteIntent, shouldFallbackManagedReadToExecuteIntent } from './managedReadExecuteRail.mjs';
|
|
16
16
|
import { createRequire } from 'module';
|
|
17
17
|
|
|
18
18
|
const require = createRequire(import.meta.url);
|
|
@@ -506,6 +506,11 @@ async function executeManagedWalletRun(ctx, flags, resolved, options) {
|
|
|
506
506
|
const response = await apiPost(actionCtx, '/api/v1/agent/execute', payload);
|
|
507
507
|
if (shouldFallbackAgentWalletToExecuteIntent(resolved, response)) {
|
|
508
508
|
const intentResult = await runReadOnlyActionViaIntentRail(ctx, flags, resolved, options, preflight);
|
|
509
|
+
if (isEmptyQueuedExecuteIntent(intentResult.raw || intentResult)) {
|
|
510
|
+
intentResult.success = false;
|
|
511
|
+
intentResult.status = 'failed';
|
|
512
|
+
intentResult.error = intentResult.error || 'MANAGED_READ_EMPTY_EXECUTE_INTENT';
|
|
513
|
+
}
|
|
509
514
|
intentResult.executionWalletAddress =
|
|
510
515
|
preflight?.executionWalletAddress ||
|
|
511
516
|
response?.executionWalletAddress ||
|
|
@@ -37,6 +37,7 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
|
|
|
37
37
|
],
|
|
38
38
|
orderManagementActions: [
|
|
39
39
|
'place_hyperliquid_perps_order',
|
|
40
|
+
'place_hyperliquid_tp_sl',
|
|
40
41
|
'cancel_hyperliquid_perps_order',
|
|
41
42
|
'update_hyperliquid_leverage',
|
|
42
43
|
'get_hyperliquid_open_orders',
|