@thirdfy/agent-cli 0.3.0 → 0.4.0
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 +110 -0
- package/README.md +60 -18
- package/package.json +2 -2
- package/scripts/validate-cli-contract-schemas.mjs +5 -0
- package/src/cli/errors.mjs +58 -8
- package/src/cli/manifest.mjs +54 -5
- package/src/cli/options/bootstrap.mjs +15 -3
- package/src/cli/options/chains.mjs +5 -0
- package/src/cli/options/credits.mjs +9 -0
- package/src/cli/options/data.mjs +13 -1
- package/src/cli/options/index.mjs +10 -4
- package/src/cli/options/walletControl.mjs +8 -0
- package/src/commands/bootstrap.mjs +44 -5
- package/src/commands/chat.mjs +3 -0
- package/src/commands/credits.mjs +39 -4
- package/src/commands/delegation.mjs +3 -1
- package/src/commands/discovery.mjs +156 -13
- package/src/commands/doctor.mjs +5 -2
- package/src/commands/execute.mjs +96 -3
- package/src/commands/prompt.mjs +5 -3
- package/src/commands/wallet.mjs +119 -3
- package/src/commands/walletControl.mjs +66 -0
- package/src/contracts/cli/schemas/actions.response.schema.json +45 -0
- package/src/contracts/cli/schemas/chains-non-evm.response.schema.json +76 -0
- package/src/contracts/cli/schemas/chains.response.schema.json +151 -0
- package/src/contracts/cli/schemas/error.response.schema.json +61 -0
- package/src/contracts/cli/schemas/preview.response.schema.json +179 -0
- package/src/contracts/cli/schemas/readiness.response.schema.json +94 -0
- package/src/core/args.mjs +10 -2
- package/src/core/chainRegistry.mjs +223 -1
- package/src/core/context.mjs +37 -14
- package/src/core/errorEnvelope.mjs +209 -0
- package/src/core/fundingTokenIn.mjs +24 -1
- package/src/core/http.mjs +6 -1
- package/src/core/readOnlyActions.mjs +10 -0
- package/src/core/walletExecuteChainId.mjs +25 -0
- package/src/lib/creditsUsdSummary.mjs +48 -4
- package/src/runtime/actions/actionList.mjs +87 -0
- package/src/runtime/actions/selection.mjs +17 -22
- package/src/runtime/execution/actionPreflight.mjs +50 -14
- package/src/runtime/execution/payloads.mjs +36 -28
- package/src/runtime/execution/runners.mjs +75 -21
- package/src/runtime/execution/svmLane.mjs +106 -0
- package/src/runtime/execution/walletControl.mjs +104 -0
- package/src/runtime/handlers.mjs +16 -2
- package/src/runtime/index.mjs +1 -1
- package/src/runtime/main.mjs +21 -5
- package/src/runtime/providerHints/apps.mjs +36 -0
- package/src/runtime/providerHints/catalogHint.mjs +55 -0
- package/src/runtime/providerHints/cex.mjs +130 -0
- package/src/runtime/providerHints/data.mjs +144 -0
- package/src/runtime/providerHints/dispatch.mjs +177 -0
- package/src/runtime/providerHints/dogeos.mjs +38 -0
- package/src/runtime/providerHints/earn.mjs +201 -0
- package/src/runtime/providerHints/perps.mjs +346 -0
- package/src/runtime/providerHints/prediction.mjs +128 -0
- package/src/runtime/providerHints/solana.mjs +85 -0
- package/src/runtime/providerHints/trading.mjs +78 -0
- package/src/runtime/providerHints/walletControl.mjs +39 -0
- package/src/runtime/risk/notional.mjs +59 -0
- package/src/runtime/risk/orderPreview.mjs +154 -0
- package/src/runtime/risk/riskRefusal.mjs +74 -0
- package/src/runtime/validation/actionParamsGate.mjs +2 -0
- package/src/runtime/providerHints.mjs +0 -1017
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,116 @@ All notable changes to `@thirdfy/agent-cli` are documented here. The format is b
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## [0.4.0] - 2026-10-14
|
|
8
|
+
|
|
9
|
+
Data parity with thirdfy-mcp `develop` (`6143bf6`): the CLI answers with the same data the MCP gives an agent. Minor version: new commands, and `--action` no longer resolves to an action whose name merely contains it.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Parity guards (Phase 4 of the MCP parity plan):**
|
|
14
|
+
- Response schemas for `preview`, `chains`, `chains --non-evm`, `readiness` and the error fields (`src/contracts/cli/schemas/`), checked by `validate:cli-contract-schemas`. `test/contract-schemas.test.cjs` runs the commands against a stub API and checks their output against the schemas, failures included.
|
|
15
|
+
- `src/contracts/cli/mcp-tool-map.json`: each of the 67 thirdfy-mcp tools, by its real name (`brain_sections`, ...), and its CLI command, or `null` and the reason (`health`, `listTools`, and the `getWalletProfile` gap). `validate:provider-parity` now also fails when an MCP tool is missing from the map or the map lists a tool the MCP dropped; `test/mcp-tool-map.test.cjs` checks every mapped command exists.
|
|
16
|
+
- `.cursor/rules/cli-mcp-parity.mdc` covers response fields, discovery hints and user-facing copy, not only execution routing.
|
|
17
|
+
- Synced with thirdfy-mcp `develop` `6143bf6`: the trading `tokenLookup` hint names every chain `search_tokens` covers and asks for confirmation on `high`, `medium` or `unknown` risk (MCP #248); `preview`'s `order` uses the venue preview's `notionalUsd` when the API sent no `amountUsd` (MCP #249); the credits refusal hint names `credits buy --usd`.
|
|
18
|
+
|
|
19
|
+
- **`readiness`** (thirdfy-mcp `getSelfReadiness` parity): on `agent_wallet`, the agent's execution wallet on the chain with its own key (`ready`, `walletControl`, `nextAction` and `userMessage` at the top, `fundingReady`, the chain's gas note; `READY` / `NOT_READY`); on `self` / `hybrid` / `thirdfy`, the delegation check with owner auth. Base is assumed without `--chain-id`, and the answer says so; a bad `--chain-id` is `INVALID_ARGUMENTS`. `READY` means the wallet can sign; `fundingReady` says whether it holds the funding token. A Solana chain the API does not run is refused, as `run` refuses it. Checked live against thirdfy-mcp `getSelfReadiness` on Base and DogeOS: the same verdict, wallet, wallet control and gas.
|
|
20
|
+
- **`credits buy --usd <n>`**: buys the agent's Thirdfy credits with USDC on Base through `wallet execute --action buy-agent-credits` (thirdfy-mcp #236). The notional check, risk and credits refusal fields of `wallet execute` apply. `--tx-hash` resumes a purchase whose payment was already sent; `--chain-id` picks Base Sepolia on test APIs. It takes only its own options (no `--action` or `--params`).
|
|
21
|
+
- **`help onboarding --path <id>`** returns one onboarding path with `selectedPath` (thirdfy-mcp `describeOnboardingPath`); the `wallet_sign` path lists the whole flow for an agent with its own key: challenge, sign, `agent auth verify`, `agent register --name`.
|
|
22
|
+
- Tests: `test/readiness-credits-onboarding.test.cjs`.
|
|
23
|
+
|
|
24
|
+
- **`actions` paging** (thirdfy-mcp `getAgentActions` / `actionListFilter` / `actionListView` parity): `--search`, `--limit` (page size up to 100 concise or 25 detailed), `--offset` and `--view concise|detailed` return one page with `pagination { total, matched, offset, limit, returned, hasMore }`, `view` and a `hint` for the next `--offset`. Concise entries keep only the fields an agent needs to pick an action. Without these flags `actions` prints the whole list as before. A bad value is `INVALID_ARGUMENTS`, before any API call. Tests: `test/actions-paging.test.cjs`.
|
|
25
|
+
|
|
26
|
+
- **`chains`: chains from the API** (thirdfy-mcp `getChainCapabilities` / `getNonEvmCapabilities` parity). The CLI reads the chain list in the API's capabilities (`chainFamily`, `caip2`, `supportedExecutionLanes`) when it negotiates them, as thirdfy-mcp `parseApiChains` does; the built-in table in `src/core/chainRegistry.mjs` is the fallback (`chainRegistrySource: api | default`). `chains` reports EVM, Solana and catalog chain ids, CAIP-2 ids, execution lanes, each chain's lanes and the Solana contract; `chains --non-evm` reports the Solana contract and whether execution is enabled.
|
|
27
|
+
- On an API that lists no Solana chain, a Solana action fails with `SOLANA_NOT_ENABLED` (`nextAction: use_evm_chain`) before any wallet call, and the Solana provider hints carry an `availability` note.
|
|
28
|
+
- Checked live against thirdfy-mcp `getChainCapabilities`: the chain ids, catalog chains, Solana ids, CAIP-2 map and source match. The CLI's execution lanes are the run modes the API lists, and its default lane is the profile's.
|
|
29
|
+
- Tests: `test/chain-registry-api.test.cjs`.
|
|
30
|
+
|
|
31
|
+
- **Provider hints at thirdfy-mcp parity** (`actions --provider <id>` → `data.providerHint`, the guidance of thirdfy-mcp `getProviderActions`, re-read at thirdfy-mcp `develop` `ebad427`):
|
|
32
|
+
- New hints: DogeOS (`dogeos`, `dogeos-barkswap`, `dogeos-laika`, `dogeos-ecosystem`, `barkswap`, `laika`: testnet, gas in DOGE, native-DOGE swaps and two-hop routes, wrap/unwrap, `LAIKA_TOKEN_MIGRATED`, launch with name and symbol only), the seven Solana families, and the `openmarket` / `openmarket-metrics` provider apps.
|
|
33
|
+
- `tradeRecipes` for Hyperliquid (one-call open with `leverage`, `takeProfitPx`, `stopLossPx`; `HL_TP_SL_LEG_REJECTED`; `update_hyperliquid_isolated_margin`) and Veranta (`update_veranta_margin`), `tokenLookup` for `trading` (`search_tokens`, `get_token_metadata`), and `walletControlNote` where wallet control changes what a provider can do.
|
|
34
|
+
- A provider with no built-in hint (provider apps, CEX venues, any catalog id: `managed-wallet`, `allora`, `governance-v2`, `openclaw`, `wasabi`, `binance`, `kraken`, `okx`, `coinbase`, `thirdfy`, `lifi`) gets a hint built from its catalog rows (`source: actions_catalog_manifest`), as the MCP builds it. Any provider id works with `--provider`.
|
|
35
|
+
- The existing 30 hints take the MCP's current copy: Hyperliquid funding from Base, Arbitrum, Ethereum or Polygon with sponsored gas, withdraw via `withdraw_hyperliquid_cctp_to_base`, Polymarket readiness fields and EU geoblock blockers, Bitfinex spot actions and credential profiles, Hummingbot and Condor blockers. MCP tool names are written as CLI commands (`thirdfy-agent run`, `thirdfy-agent preview`); the CLI keeps its shell-quoted examples.
|
|
36
|
+
- `validate:provider-parity` checks every MCP static hint (45) and `KNOWN_GAPS` is empty. Tests: `test/provider-hints-dispatch.test.cjs`, `test/output-copy.test.cjs`.
|
|
37
|
+
- **Copy test** (`test/output-copy.test.cjs`, the rules of thirdfy-mcp `tests/mcp/modelFacingCopy.test.ts`): no buy-gas advice, Solana never called user-pays, no "Console" in agent-facing hints, and provider hints name CLI commands, not MCP tools.
|
|
38
|
+
|
|
39
|
+
- **`preview`: would Thirdfy sign this write?** The same verdict as thirdfy-mcp `previewOrder` (`src/tools/risk/previewOrder.ts`), from the action-aware execution-wallet preflight, on `agent_wallet`. Nothing is signed, billed or reserved. `data` carries `wouldSign`, `blockedReason` / `blockedStage` / `reason`, `riskRefusal` with a one-line `message`, `amountUsd`, `orderPreview` (`previewKind` `risk` or `spend`), non-blocking `warnings` (`HL_STOP_PAST_LIQUIDATION`), a `note`, `gas`, `venueSetup` (when `runsWithOrder`) and, for an order Thirdfy would sign, `order`: the `run` arguments, one idempotency key and the `command` argv. A blocked order is the answer (exit 0, `code: PREVIEW_BLOCKED`).
|
|
40
|
+
- On a chain whose wallet pays gas (DogeOS 6281971), a wallet that cannot pay it gives `wouldSign: false`, `INSUFFICIENT_NATIVE_GAS`, `blockedStage: gas`, `nextAction: fund_native_gas` and the faucet message. Solana and sponsored EVM chains are never blocked on gas.
|
|
41
|
+
- A value write Thirdfy cannot size (no `--estimated-amount-usd`, no amount from the API) fails with `ESTIMATED_AMOUNT_USD_REQUIRED` / `fix_request`; a read fails with `PREVIEW_ORDER_WRITE_ONLY`.
|
|
42
|
+
- Code: `src/runtime/risk/` (`orderPreview.mjs`, `riskRefusal.mjs`, `notional.mjs`), `chainGasNote` in `src/core/chainRegistry.mjs`. Tests: `test/order-preview.test.cjs`.
|
|
43
|
+
- **Risk-policy refusals are lifted to the top level** on `run`, `wallet execute` and `agent run` (thirdfy-mcp `liftRiskRefusalFields`): `riskRefusal`, `blockedReason`, `blockedStage: risk_policy`, `retryable` (true only for `POLICY_STATE_UNREAD`) and `message`, e.g. `refused by risk policy: maxLeverage 3, order 5`.
|
|
44
|
+
- **`preflight` keeps `orderPreview`, `riskRefusal` and `venueSetup`** from the action preflight. A `risk_policy` or `venue_rules` refusal is `status: blocked`, and an action preflight the API answers 2xx with `success: false` is a block too (it read as ready).
|
|
45
|
+
- **Notional warning** (thirdfy-mcp `NOTIONAL_WARNING`): a write sent by `run` (`agent_wallet`, `thirdfy`), `wallet execute` or `agent run` without `--estimated-amount-usd`, whose catalog row does not say `behavior.requiresEstimatedAmountUsd: false`, gets `meta.warnings`. `THIRDFY_NOTIONAL_ESTIMATE_MODE=refuse` stops it before anything is sent with `ESTIMATED_AMOUNT_USD_REQUIRED`.
|
|
46
|
+
|
|
47
|
+
- **Error fields, as thirdfy-mcp sends them** (`src/core/errorEnvelope.mjs`, ported from thirdfy-mcp `src/mcp/errors/toolErrorEnvelope.ts` and `src/wallet/creditsRefusal.ts`). Every failed command's JSON `data` now also carries `errorCode`, `retryable`, `retryAfterSeconds` (from `Retry-After` or the body, only when retryable), `nextAction` and `hint`. Existing fields (`statusCode`, `blockedReason`, `blockedStage`, `payload`, …) are unchanged, and wallet-control fields still win.
|
|
48
|
+
- `errorCode` is the API's code (`blockedReason` first, then `errorCode`, `error`, `code`), else one for the status (`UNAUTHORIZED`, `RATE_LIMITED`, `UPSTREAM_ERROR`, …), `UPSTREAM_TIMEOUT` / `UPSTREAM_UNREACHABLE` when the API never answered, or the CLI's own code.
|
|
49
|
+
- A write that got a 5xx with no API code is `retryable: false`, `nextAction: reread_state_before_retry`: it may have executed.
|
|
50
|
+
- **Credits refusal:** HTTP 402 `DAILY_FREE_LIMIT_EXCEEDED` / `INSUFFICIENT_CREDITS` / `CREDITS_EXHAUSTED` is `nextAction: add_credits`, not retryable, with a hint naming `credits balance`. `WRITE_OUTCOME_UNKNOWN: CREDITS_CLAIM_PENDING` says the payment may have been sent and not to buy again.
|
|
51
|
+
- **Empty managed read** (`MANAGED_READ_EMPTY_EXECUTE_INTENT`, a read whose execute call timed out): `retryable: true`, `retryAfterSeconds: 3`, `nextAction: retry_later`, as in thirdfy-mcp.
|
|
52
|
+
- Blocked preflights (`INSUFFICIENT_NATIVE_GAS` → `fund_native_gas`, `INSUFFICIENT_FUNDS`, `EXECUTION_WALLET_MISMATCH` → `fix_request`) carry the same fields.
|
|
53
|
+
- A missing or malformed flag (`requireFlag`, `parseJsonFlag`) is `INVALID_ARGUMENTS` / `fix_request`; an API-blocked `preflight` (409 venue, 403 policy, 429 limits) carries the same fields.
|
|
54
|
+
- `docs/command-reference.md` "Error fields" lists every `nextAction`; a test fails when the CLI sends one it does not list. Tests: `test/error-envelope.test.cjs`.
|
|
55
|
+
|
|
56
|
+
- **`wallet-control state|request|submit`**: the owner's actions on a user-owned agent wallet (Thirdfy API 4.0 `/api/v1/agent/:agentKey/wallet-control/*`), with owner auth only (`--owner-session-token` or a Privy `--auth-token`). `request --action update_policy` returns the unsigned Privy request(s); the owner signs `signatureInput` with their own key; `submit --action update_policy --signature-input <json> --signature <sig>` relays it. This is how an owner answers `AGENT_POLICY_UPDATE_REQUIRED` from the CLI (also `stop_agent`, `resume_agent`, `change_payout --payout-address`, `export --recipient-public-key`). The CLI never signs these. `--agent-key` defaults to the configured agent.
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- **Credits are shown in credits only, never as a dollar price per credit or per request** (thirdfy-mcp #252). `credits balance` and `pricing quote` drop every USD field the API adds (`availableUsd`, `retailUsdPerCredit`, `chargedUsd`, `estimatedTotalUsd`, `displayCurrency`, `pricingCurrency`, at any depth), and the balance summary reads `N credits`. Buying credits keeps its USD amount (`credits buy --usd`): that is a purchase, not a request price. `models list` / `models estimate` (chat model pricing) are unchanged, as in the MCP.
|
|
61
|
+
- **Free daily requests and the owner-credits fallback** (thirdfy-api-v2#884, thirdfy-mcp #253). Billing order for the agent's own runs: its credits, then its free requests (20 per UTC day), then the owner's credits, then a refusal.
|
|
62
|
+
- `credits balance` reads the agent's own ledger (`/agent/credits/balance`) with `--agent-api-key`, or with only an agent key in env/config: `freeDailyLimit`, `freeRequestsUsedToday`, `freeRequestsRemainingToday`, `ownerCreditsFallback`, and a `note` saying who pays next. Owner auth still reads the owner's balance.
|
|
63
|
+
- `pricing quote` says `wouldBePaidBy` when the API sends it.
|
|
64
|
+
- The credits refusal hint (`add_credits`) says the agent's credits, today's free requests and the owner's credits are all used, and names `credits buy --usd`.
|
|
65
|
+
- **Solana gas copy follows the API's sponsored fees.** Solana is no longer a user-pays chain in `src/core/chainRegistry.mjs` (no "about 0.003 SOL"); the `INSUFFICIENT_NATIVE_GAS` text, `doctor` gas notes and the `login email` Solana reminder say Thirdfy sponsors network fees, the wallet pays only rent for new token accounts and SOL the action spends, and to give the user the base58 address. The README and command reference no longer say to fund SOL for network fees.
|
|
66
|
+
- **No "Console" in agent-facing hints:** `AGENT_SIGNER_REMOVED_BY_OWNER` and `AGENT_POLICY_UPDATE_REQUIRED` say "their Thirdfy account", as thirdfy-mcp does.
|
|
67
|
+
- `venue setup` / `venue fund` descriptions: setup also runs by itself with the first order; funding steps are writes sent with `run`. `prompt` suggests `actions` → `preview` → `run` on `agent_wallet`.
|
|
68
|
+
- Provider hint `category` follows the MCP for `bitfinex` (`cex_funding_and_spot`), `hummingbot` and `condor` (`cex_executor`); Bitfinex `credentialType` is `api_key`.
|
|
69
|
+
|
|
70
|
+
- **Provider hints moved into `src/runtime/providerHints/`** (move only, no change in output). The 1,017-line `src/runtime/providerHints.mjs` is now one file per provider family plus `dispatch.mjs`. `createProviderHints` is still exported from `src/runtime/index.mjs`. A deep import of `src/runtime/providerHints.mjs` must change to `src/runtime/providerHints/dispatch.mjs`; the package has no `exports` map, so that path could be imported. `validate:provider-parity` reads the provider ids from `STATIC_HINT_PROVIDER_IDS`. Tests: `test/provider-hints-dispatch.test.cjs`.
|
|
71
|
+
- **`--confirm-writes` follows the action's schema** (thirdfy-mcp 0.1.43 `injectSchemaConfirmationIntoParams`). It is copied into each confirmation key the catalog `paramsSchema` lists, and only those: `params.confirmWrites` on Veranta writes, `params.confirm` on the Hyperliquid and Polymarket bridge and capital hops (the CLI never sent `confirm`). A schema that lists neither, such as one that refuses unknown keys, gets nothing (the CLI always added `confirmWrites`); a value already in `--params` wins, and `params.confirm` no longer turns into `confirmWrites`.
|
|
72
|
+
- **`dogeos_prepare_trade` and `dogeos_prepare_token_launch` are reads** (planning steps; thirdfy-api-v2#867, thirdfy-mcp #239), as in the API and the MCP.
|
|
73
|
+
- **The API gets the action name you asked for**, as thirdfy-mcp sends it. `run`, `preflight`, `wallet execute` and `agent run` sent the catalog row's key (`place-hyperliquid-perps-order` for `--action place_hyperliquid_perps_order`, or the row an alias pointed to); they now send `--action` as written, and the CLI's own rules (schema, amounts, chain) still use the catalog row. A Veranta name that matched only an Avantis row is still sent under the Avantis key. The API accepts every spelling, so results don't change; its logs show the name you used. Tests: `test/api-action-name.test.cjs`.
|
|
74
|
+
- **The catalog's `behavior.readOnly` decides read vs write** for a resolved action when the row has it, either way; the name rules and `supportsExecute` metadata are the fallback (`isReadOnlyResolvedAction` in `src/core/readOnlyActions.mjs`, used by `run`, `preflight`, the params gate and action timeouts). Provider app actions are named freely, so only the catalog knows `partner_vault_apy` is a read. Same rule as thirdfy-mcp `isReadOnlyCatalogAction`. Tests: `test/read-only-behavior-precedence.test.cjs`.
|
|
75
|
+
- **Provider app writes (Thirdfy API manifest writes)**, matching thirdfy-mcp (thirdfy/thirdfy-mcp#199):
|
|
76
|
+
- `run` uses the catalog row's `defaultChainId` when no `--chain-id` or `params.chainId` is given (the API sends it for single-chain provider app writes), so a DogeOS-only write is preflighted on DogeOS. Rows without it keep the Base default (and Solana-only rows 101). `inferChainIdFromCatalogRow` in `src/core/walletExecuteChainId.mjs`.
|
|
77
|
+
- The funding preflight checks the token named by the row's `behavior.spend.tokenParam` before the name fallbacks.
|
|
78
|
+
- `AGENT_POLICY_UPDATE_REQUIRED` is a wallet-control refusal: not retryable, with a hint, and `ownerAction` (the owner's `update_policy` request path) in the error envelope `data`.
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- **A risk refusal on `run` reported `errorCode: FORBIDDEN`** (the HTTP status) when the API nested the refusal under `data`; `errorCode` is now the refusal (`RISK_MAX_LEVERAGE`), as `blockedReason` already was. Found by the new contract test.
|
|
83
|
+
- **`prompt` crashed** with `getEffectiveRunMode is not defined` (a missing import).
|
|
84
|
+
- **An action the agent may not run reached the API when its key came from env or config.** The params gate resolved `--action` against the agent's own policy list (`GET /api/v1/agent/actions`) only with `--agent-api-key`; with `THIRDFY_AGENT_API_KEY` or config `agent.apiKey` it used the public catalog, so `swap` passed and the API refused it with `ACTION_NOT_ALLOWED`. It now uses the same key execution uses, and `run`, `preflight` and `wallet execute` stop such an action as `PARAMS_SCHEMA_INVALID` / `blockedReason: UNKNOWN_ACTION` with `suggestions` before any API write, as thirdfy-mcp does.
|
|
85
|
+
- **`actions` listed the public catalog when the agent key came from env or config.** It used the agent's own list only with `--agent-api-key`, so it showed actions (`swap`, `buy-agent-credits`) that `run` and `preflight` then refused as `UNKNOWN_ACTION`. The lookup now lives in the catalog loader (`getCachedActionsCatalogSnapshot` in `src/core/context.mjs`: flag, `THIRDFY_AGENT_API_KEY`, config), so `actions`, the params gate and `doctor certify` all read the list execution is checked against, as thirdfy-mcp `getAgentActions` does. `meta.policyAware` says a key was sent; the new `meta.policyScoped` says the API answered with the agent policy's allowlist (an unrestricted key gets the whole catalog). A stale key in config now fails `actions` with the API's 401, as `run` does.
|
|
86
|
+
- **`--help` printed the help twice**, and a subcommand's help was followed by the whole root help. A command group run without its subcommand (`thirdfy-agent doctor`) now exits 1, and in `--json` mode answers `INVALID_ARGUMENTS` on stdout, with `command` and `subcommands`, instead of printing nothing. The bare `thirdfy-agent` and `thirdfy-agent help` are help requests and still exit 0.
|
|
87
|
+
- **`--ni` was refused** (`unknown option '--ni'`) although the README and the onboarding help use it. It now means `--not-interactive`, like `-ni`.
|
|
88
|
+
- **Command-line errors carry the error fields.** `UNKNOWN_COMMAND` and `CLI_ARG_ERROR` envelopes now have `errorCode`, `retryable: false` and `nextAction: fix_request`, and `meta.apiBase` is the API base in use (`--api-base`, `--api-base=`, or `THIRDFY_API_BASE`; it was always production). A missing flag (`run` without `--action`) is `code: INVALID_ARGUMENTS` at the top of the envelope, not `CLI_UNHANDLED_ERROR`.
|
|
89
|
+
- **`actions --help` listed about 30 `run` flags** (`--broadcast`, `--skip-preflight`, `--confirm-writes`, …). `actions` now takes only its filters: `--provider`, `--chain-id`, `--catalog`, `--data-category`, plus the auth options.
|
|
90
|
+
- **The README named an env var the CLI never reads** (`AGENT_API_KEY`). It now uses `THIRDFY_AGENT_API_KEY`, which the CLI reads after `--agent-api-key` and before config `agent.apiKey`.
|
|
91
|
+
- **`--action` no longer runs a different action whose name contains it.** A name that is not an action, alias or Veranta/Avantis twin used to resolve to the one catalog action containing it (`swap` → `execute_optimal_swap`), or fail as "Ambiguous" when several did. It is now `UNKNOWN_ACTION` with those actions in `suggestions`, as in thirdfy-mcp.
|
|
92
|
+
- **A write that got a 502, 503 or 504 was sent a second time.** `requestJson` resent any request once on those statuses, writes included, although the first one may have executed. It now resends only requests that are safe to resend (GETs, `validation_only`, calls marked `writeAction: false`), as for timeouts; a write's 5xx comes back with `nextAction: reread_state_before_retry`. thirdfy-mcp never resends a write either.
|
|
93
|
+
- **`validate:provider-parity` checked nothing against thirdfy-mcp.** It read `../thirdfy-mcp/src/tools/getTradingProviderActions.ts`, which moved to `src/tools/discovery/`, and a missing file only printed a warning, so the release preflight passed while the CLI lacked hints for 20 MCP providers. It now finds thirdfy-mcp at `THIRDFY_MCP_DIR`, next to this repo, or next to the main checkout when run from a git worktree, and fails when it is not found (`SKIP_MCP_PARITY=1` skips on purpose). It compares against the MCP's whole `apiProviderIds` list instead of 15 fixed ids. The 20 providers without a CLI hint are listed in `KNOWN_GAPS`; the check fails when a new MCP provider has no hint, or when a listed gap gets one (so the list only shrinks).
|
|
94
|
+
|
|
95
|
+
## [0.3.2] - 2026-10-10
|
|
96
|
+
|
|
97
|
+
### Added
|
|
98
|
+
|
|
99
|
+
- **Thirdfy API 4.0 wallet control** (Privy owner/signer split; `PRIVY_WALLET_CONTROL_MODE` off / enforced / user_owned / per_template), shown the way thirdfy-mcp 0.1.19 shows it. Shared code: `src/runtime/execution/walletControl.mjs`.
|
|
100
|
+
- **Refusals.** When the API refuses a write before signing (`AGENT_SIGNER_REMOVED_BY_OWNER`, `AGENT_WALLET_CONTROL_CONTRADICTORY`, `AGENT_WALLET_CONTROL_UNAVAILABLE`, `WITHDRAW_DESTINATION_NOT_OWNER`), the error envelope's `data` carries `walletControlCode`, `retryable`, `hint`, and the API's `walletControl`, `nextAction` and `userMessage` lifted from the response body. `AGENT_SIGNER_REMOVED_BY_OWNER` is not retryable: the owner must re-enable the agent. The API body stays under `data.payload`, and other errors are unchanged.
|
|
101
|
+
- **Preflight.** The managed `agent_wallet` preflight (`preflight`, `run`) reports the API's `walletControl`. When `signerActive` is false it adds `walletControlBlocked`, `walletControlBlocker` (from `walletControl.blockers`) and `nextAction` (`owner_reenable_agent_signer`, `retry_later` or `contact_support`). Solana checks are exempt, as in the API's gate. The API still refuses the write itself.
|
|
102
|
+
- **`login` bootstrap.** New `--owner-payout-address <addr>` (where exits from a controlled wallet may pay) and `--allowed-actions <a,b,...>` (the agent's action template from catalog names). Both go to `bootstrap-self`; the output shows the API's `ownerPayout` and `actionTemplate`, including any `ownerAction` the owner must sign.
|
|
103
|
+
- Parity with thirdfy-mcp 0.1.19 (thirdfy/thirdfy-mcp#179). Tests: `test/wallet-control-parity.test.cjs`.
|
|
104
|
+
|
|
105
|
+
## [0.3.1] - 2026-10-10
|
|
106
|
+
|
|
107
|
+
### Added
|
|
108
|
+
|
|
109
|
+
- **Solana on the `agent_wallet` lane.** Chains 101 (mainnet, `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`) and 103 (devnet) are in `src/core/chainRegistry.mjs` as family `svm`. A catalog row that runs only on Solana (`supportedChains: [101]`) defaults to chain 101 when no `--chain-id` or `params.chainId` is given; other rows keep the Base 8453 default. On a Solana chain a run mode from the profile, env or config becomes `agent_wallet`; an explicit `--run-mode self|hybrid|thirdfy` (and `wallet sign`, `self-exec`, `wallet submit --chain-id 101`) fails with `SVM_REQUIRES_AGENT_WALLET` before any wallet lookup or execution, so a Solana run never reaches EVM signing. Funding preflight sends the spent mint as `tokenIn` (`inputMint` on `jupiter_swap`, `mint` on `solana_transfer`, Solana USDC on `deposit_polymarket_from_solana` and `deposit_hyperliquid_from_solana`). When the run mode or chain was switched for Solana, `run` output `meta` says so (`runModeDefaultedForSvm`, `chainIdInferred`). `chat --network sol-mainnet|sol-devnet` maps to 101/103. Docs: "Solana (agent_wallet)" in README, `docs/command-reference.md`, `docs/action-inventory-and-compatibility.md` and `llms.txt`. Pin: `test/solana-agent-wallet.test.cjs`.
|
|
110
|
+
|
|
111
|
+
### Fixed
|
|
112
|
+
|
|
113
|
+
- **`agent_wallet` lowercased the execution wallet address.** The preflight lowercased `executionWalletAddress`, which corrupts a case-sensitive Solana base58 address. The CLI now keeps the address exactly as the API returns it. Expected-wallet checks (`--wallet-address`, read fast path) compare 0x addresses case-insensitively and base58 addresses exactly. EVM addresses now print in the API's case (checksummed) instead of lowercase.
|
|
114
|
+
- **`login email` failed when the agent had only a Solana execution wallet.** It accepted only 0x `executionWallets`; it now accepts base58 Solana addresses too and prints a `solanaFundingReminder` with the address to fund with SOL and SPL tokens.
|
|
115
|
+
- **Native gas fallback text is chain-aware.** When the API reports `gasReady: false` without a `nativeGasWarning.message`, a Solana write now says the wallet needs SOL for network fees (about 0.003 SOL) instead of "0 native gas". The API message still wins when present.
|
|
116
|
+
|
|
7
117
|
## [0.3.0] - 2026-10-10
|
|
8
118
|
|
|
9
119
|
### Removed
|
package/README.md
CHANGED
|
@@ -40,35 +40,48 @@ Run without global install:
|
|
|
40
40
|
npx @thirdfy/agent-cli --help
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
## What's new in v0.
|
|
43
|
+
## What's new in v0.4.0
|
|
44
44
|
|
|
45
|
-
-
|
|
46
|
-
-
|
|
45
|
+
- **`preview`:** asks whether Thirdfy would sign a write before anything is sent: the verdict, the order and risk preview, the risk-policy refusal and the `run` command to place it.
|
|
46
|
+
- **New commands:** `readiness` (can the agent act on this chain), `chains` (chains, CAIP-2 ids and lanes from the API), `credits buy --usd <n>`, and `actions --search/--limit/--offset/--view` for one page of the catalog.
|
|
47
|
+
- **Error fields on every failure:** `errorCode`, `retryable`, `retryAfterSeconds`, `nextAction` and `hint`, the same as thirdfy-mcp. A write that got a 5xx is no longer sent twice.
|
|
48
|
+
- **Credits in credits only, and who pays next:** `credits balance` and `pricing quote` show no USD price per credit or request. With an agent key, `credits balance` reads the agent's own ledger: its free requests left today (20 per UTC day), the owner-credits fallback, and a note on who pays next.
|
|
49
|
+
- **Provider hints at MCP parity:** DogeOS, Solana and provider-app hints, perps trade recipes, token lookup and wallet-control notes. Solana fees are sponsored.
|
|
50
|
+
- **Behavior changes:** `--action` matches only an action, alias or Veranta/Avantis twin (no partial names), and is checked against the agent's own allowed actions; `--confirm-writes` goes only into the confirmation keys an action's schema lists.
|
|
51
|
+
- Older versions: see [CHANGELOG.md](./CHANGELOG.md).
|
|
47
52
|
|
|
48
53
|
## Quick start
|
|
49
54
|
|
|
50
55
|
Hosted solo agents use profile `personal` and `--run-mode agent_wallet` (Path A). Fund the managed **execution wallet**, complete venue trading setup from the catalog when needed, then preflight and run. MetaMask Gator / `delegation *` is not required on this path.
|
|
51
56
|
|
|
52
57
|
```bash
|
|
53
|
-
export
|
|
58
|
+
export THIRDFY_AGENT_API_KEY="..."
|
|
54
59
|
export THIRDFY_AUTH_TOKEN="..."
|
|
55
60
|
export THIRDFY_OWNER_SESSION_TOKEN="..."
|
|
56
61
|
|
|
57
62
|
thirdfy-agent profile init --profile personal --json
|
|
58
|
-
thirdfy-agent actions --agent-api-key "$
|
|
63
|
+
thirdfy-agent actions --agent-api-key "$THIRDFY_AGENT_API_KEY" --json
|
|
59
64
|
thirdfy-agent wallet list --json
|
|
60
65
|
# Fund the printed execution wallet address for your target chain (not the owner login wallet).
|
|
61
66
|
|
|
62
67
|
thirdfy-agent preflight \
|
|
63
|
-
--agent-api-key "$
|
|
68
|
+
--agent-api-key "$THIRDFY_AGENT_API_KEY" \
|
|
64
69
|
--run-mode agent_wallet \
|
|
65
70
|
--action swap \
|
|
66
71
|
--params '{"tokenIn":"0x...","tokenOut":"0x...","amountInRaw":"1000000","tokenInDecimals":6}' \
|
|
67
72
|
--estimated-amount-usd 25 \
|
|
68
73
|
--json
|
|
69
74
|
|
|
70
|
-
|
|
75
|
+
# Would Thirdfy sign it? Verdict, order and risk preview, and the run command. Nothing is sent.
|
|
76
|
+
thirdfy-agent preview \
|
|
71
77
|
--agent-api-key "$AGENT_API_KEY" \
|
|
78
|
+
--action swap \
|
|
79
|
+
--params '{"tokenIn":"0x...","tokenOut":"0x...","amountInRaw":"1000000","tokenInDecimals":6}' \
|
|
80
|
+
--estimated-amount-usd 25 \
|
|
81
|
+
--json
|
|
82
|
+
|
|
83
|
+
thirdfy-agent run \
|
|
84
|
+
--agent-api-key "$THIRDFY_AGENT_API_KEY" \
|
|
72
85
|
--run-mode agent_wallet \
|
|
73
86
|
--action swap \
|
|
74
87
|
--params '{"tokenIn":"0x...","tokenOut":"0x...","amountInRaw":"1000000","tokenInDecimals":6}' \
|
|
@@ -91,7 +104,7 @@ npm run e2e:local-api:smoke
|
|
|
91
104
|
|
|
92
105
|
## Auth model
|
|
93
106
|
|
|
94
|
-
- `
|
|
107
|
+
- `THIRDFY_AGENT_API_KEY` (or `--agent-api-key`, or `agent.apiKey` in config): execution identity for `preflight`, `run`, `self-exec`, and policy-aware discovery (`actions` lists the agent's own actions when a key is set).
|
|
95
108
|
- `THIRDFY_AUTH_TOKEN`: owner/account auth for onboarding and delegation commands.
|
|
96
109
|
- `THIRDFY_OWNER_SESSION_TOKEN`: wallet-sign owner session for onboarding and owner operations.
|
|
97
110
|
|
|
@@ -101,7 +114,7 @@ CLI precedence now supports login/config defaults:
|
|
|
101
114
|
2. environment variable
|
|
102
115
|
3. `~/.thirdfy/config.json` (from `thirdfy-agent login` / `config set`)
|
|
103
116
|
|
|
104
|
-
Execution-only workflows can run with only `
|
|
117
|
+
Execution-only workflows can run with only `THIRDFY_AGENT_API_KEY` after onboarding is complete.
|
|
105
118
|
|
|
106
119
|
### Login / logout / config UX
|
|
107
120
|
|
|
@@ -114,7 +127,7 @@ thirdfy-agent wallet list --json
|
|
|
114
127
|
thirdfy-agent doctor auth --json
|
|
115
128
|
|
|
116
129
|
# Store auth defaults once
|
|
117
|
-
thirdfy-agent login --auth-token "$THIRDFY_AUTH_TOKEN" --agent-api-key "$
|
|
130
|
+
thirdfy-agent login --auth-token "$THIRDFY_AUTH_TOKEN" --agent-api-key "$THIRDFY_AGENT_API_KEY" --json
|
|
118
131
|
|
|
119
132
|
# Bootstrap scoped self identity + custody mode in one step
|
|
120
133
|
thirdfy-agent login --auth-token "$THIRDFY_AUTH_TOKEN" --agent-key "0xYOUR_AGENT_KEY" --run-mode self --custody-mode external --wallet-address "0xYOUR_AGENT_KEY" --json
|
|
@@ -220,6 +233,33 @@ thirdfy-agent whoami --json
|
|
|
220
233
|
|
|
221
234
|
`profile use --profile <name>` resets run mode to that profile default unless `--run-mode` is explicitly provided.
|
|
222
235
|
|
|
236
|
+
### Solana (agent_wallet)
|
|
237
|
+
|
|
238
|
+
Solana actions run only with `--run-mode agent_wallet`, on the managed Solana agent wallet that Thirdfy holds for the agent. `self` and `hybrid` sign EVM transactions only, and `thirdfy` fan-out does not run on Solana. Chain ids: `101` (mainnet, `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`) and `103` (devnet).
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
# 1. Log in (or bootstrap). executionWallets lists a base58 Solana address next to any EVM one.
|
|
242
|
+
thirdfy-agent login email you@example.com --code <otp> --accept-terms --json
|
|
243
|
+
|
|
244
|
+
# 2. Look up the Solana execution wallet (read-only).
|
|
245
|
+
thirdfy-agent preflight --chain-id 101 --run-mode agent_wallet --action get_solana_balances --params '{}' --json
|
|
246
|
+
|
|
247
|
+
# 3. Fund the printed base58 executionWalletAddress with the token you trade (USDC). Give the user this base58 address, not the EVM one.
|
|
248
|
+
# Thirdfy sponsors Solana network fees; keep a little SOL only for rent on new token accounts (about 0.002 SOL each).
|
|
249
|
+
|
|
250
|
+
# 4. Quote, then swap with Jupiter.
|
|
251
|
+
thirdfy-agent run --action get_jupiter_quote --params '{"inputMint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","outputMint":"So11111111111111111111111111111111111111112","amount":"5"}' --json
|
|
252
|
+
thirdfy-agent run --action jupiter_swap --params '{"inputMint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v","outputMint":"So11111111111111111111111111111111111111112","amount":"5"}' --estimated-amount-usd 5 --json
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
- An action whose catalog row runs only on Solana (`supportedChains: [101]`) defaults to chain `101` when you pass no `--chain-id`, and a run mode that came from your profile or config switches to `agent_wallet`. An explicit `--run-mode self|hybrid|thirdfy` on Solana fails with `SVM_REQUIRES_AGENT_WALLET` before any wallet lookup or execution.
|
|
256
|
+
- Solana addresses are base58 and case-sensitive. The CLI prints them exactly as the API returns them, and `--wallet-address` must match exactly.
|
|
257
|
+
- Network fees are sponsored. A write stops before execution with `INSUFFICIENT_NATIVE_GAS` when the API reports `gasReady: false`: the wallet is short of the SOL it needs for rent on new token accounts or that the action spends. Ask for SOL only then.
|
|
258
|
+
- Funding checks read the spent mint: `inputMint` on `jupiter_swap`, `mint` on an SPL `solana_transfer`.
|
|
259
|
+
- Providers: Jupiter swaps (`get_jupiter_quote`, `jupiter_swap`), Solana wallet (`get_solana_balances`, `solana_transfer`), Jupiter prediction markets (`get_jupiter_prediction_events`, `get_jupiter_prediction_market`, `get_jupiter_prediction_positions`, `place_jupiter_prediction_order`, `close_jupiter_prediction_position`, `claim_jupiter_prediction_payout`), Jupiter Lend (`get_jupiter_lend_vaults`, `get_jupiter_lend_positions`, `jupiter_lend_deposit`, `jupiter_lend_withdraw`), native staking (`get_solana_staking_info`, `get_solana_staking_positions`, `solana_stake`, `solana_unstake`, `solana_withdraw_stake`), Kamino vaults (`get_kamino_vaults`, `get_kamino_vault_positions`, `kamino_vault_deposit`, `kamino_vault_withdraw`), and Meteora liquidity (`get_meteora_pools`, `get_meteora_positions`, `open_meteora_position`, `remove_meteora_liquidity`, `claim_meteora_fees`). Run `thirdfy-agent actions --chain-id 101 --json` for the live list and each action's params.
|
|
260
|
+
- Bridges into Solana keep an EVM source chain: pass `toChain: 101` and a base58 `toAddress` to `execute_bridge`.
|
|
261
|
+
- Portfolio: `thirdfy-agent analytics portfolio --agent-key <key> --chain-type svm --network-id sol-mainnet --json`.
|
|
262
|
+
|
|
223
263
|
## Framework Guidance
|
|
224
264
|
|
|
225
265
|
Use the same execution identities across frameworks, but choose the rail by who signs and who the agent acts for:
|
|
@@ -236,7 +276,7 @@ Use `--broadcast` with `run --run-mode self` to keep one deterministic command f
|
|
|
236
276
|
|
|
237
277
|
```bash
|
|
238
278
|
thirdfy-agent run \
|
|
239
|
-
--agent-api-key "$
|
|
279
|
+
--agent-api-key "$THIRDFY_AGENT_API_KEY" \
|
|
240
280
|
--run-mode self \
|
|
241
281
|
--broadcast \
|
|
242
282
|
--action swap \
|
|
@@ -250,7 +290,7 @@ Hybrid managed-wallet variant (agent_wallet + Thirdfy mirror):
|
|
|
250
290
|
|
|
251
291
|
```bash
|
|
252
292
|
thirdfy-agent run \
|
|
253
|
-
--agent-api-key "$
|
|
293
|
+
--agent-api-key "$THIRDFY_AGENT_API_KEY" \
|
|
254
294
|
--run-mode hybrid \
|
|
255
295
|
--hybrid-wallet-mode agent_wallet \
|
|
256
296
|
--action swap \
|
|
@@ -292,11 +332,11 @@ Examples:
|
|
|
292
332
|
|
|
293
333
|
```bash
|
|
294
334
|
# Raw/base units
|
|
295
|
-
thirdfy-agent run --agent-api-key "$
|
|
335
|
+
thirdfy-agent run --agent-api-key "$THIRDFY_AGENT_API_KEY" --action swap \
|
|
296
336
|
--params '{"tokenIn":"0x...","tokenOut":"0x...","amountInRaw":"1000000","tokenInDecimals":6,"chainId":8453}' --json
|
|
297
337
|
|
|
298
338
|
# Human units + decimals
|
|
299
|
-
thirdfy-agent run --agent-api-key "$
|
|
339
|
+
thirdfy-agent run --agent-api-key "$THIRDFY_AGENT_API_KEY" --action swap \
|
|
300
340
|
--params '{"tokenIn":"0x...","tokenOut":"0x...","amountInHuman":"1.5","tokenInDecimals":6,"chainId":8453}' --json
|
|
301
341
|
```
|
|
302
342
|
|
|
@@ -315,14 +355,16 @@ Provider and venue guides are kept outside `README` so this page stays stable as
|
|
|
315
355
|
## Command groups
|
|
316
356
|
|
|
317
357
|
- Discovery: `catalogs list`, `signals` (data provider apps and their signals: fields with units, freshness, roles, credits, attribution; `--provider`, `--role`), `actions` (optional `--provider`, `--chain-id`, `--data-category`; see [`docs/command-reference.md`](./docs/command-reference.md) for trading vs earn vs prediction providers and case-insensitive matching)
|
|
318
|
-
-
|
|
358
|
+
- Discovery: `actions`, `chains` (chain ids, CAIP-2 and lanes from the API)
|
|
359
|
+
- Readiness and credits: `readiness` (can the agent act on a chain), `credits balance`, `credits buy --usd <n>`
|
|
360
|
+
- Execution: `preflight`, `preview`, `run`, `intent-status`
|
|
319
361
|
- Polymarket: `polymarket status`, `polymarket setup`, `polymarket dry-run`
|
|
320
362
|
- Venue tooling: `venue readiness`, `venue setup`, `venue fund` (`polymarket`, `hyperliquid`, `lighter`, `veranta`)
|
|
321
363
|
- Hummingbot: `hummingbot status`, `connectors`, `executor-schema`, `executor-create`, `executor-stop`, `gateway-networks`, `gateway-connectors`
|
|
322
364
|
- Condor Pattern A: `condor policy-status`, `condor preflight`
|
|
323
365
|
- Managed signer execution: `wallet execute`, `wallet sign`, `wallet submit`, `agent run`
|
|
324
366
|
- Profiles: `profile init`, `profile use`, `profile show`, `whoami`
|
|
325
|
-
- Onboarding: `login email`, `help onboarding`, `doctor auth`, `wallet list`, `bootstrap begin`, `bootstrap complete`, `onboarding begin`, `onboarding complete`, `agent auth challenge`, `agent auth verify`, `agent register`, `agent key rotate`, `agent key revoke`, `developer bootstrap`
|
|
367
|
+
- Onboarding: `login email`, `help onboarding`, `doctor auth`, `wallet list`, `bootstrap begin`, `bootstrap complete`, `onboarding begin`, `onboarding complete`, `agent auth challenge`, `agent auth verify`, `agent register`, `agent key rotate`, `agent key revoke`, `wallet-control state|request|submit` (owner), `developer bootstrap`
|
|
326
368
|
- User wallet authorization (Gator): `delegation create`, `delegation activate`, `delegation status`, `credentials upsert`, `credentials status`
|
|
327
369
|
- Gator operations: `delegation show`, `delegation inspect`, `delegation revoke`
|
|
328
370
|
- Gator execution helpers: `delegation balance`, `delegation redeem` (daily-cap caveats via `--max-usd-per-day`)
|
|
@@ -392,7 +434,7 @@ thirdfy-agent delegation balance \
|
|
|
392
434
|
|
|
393
435
|
thirdfy-agent delegation redeem \
|
|
394
436
|
--run-mode thirdfy \
|
|
395
|
-
--agent-api-key "$
|
|
437
|
+
--agent-api-key "$THIRDFY_AGENT_API_KEY" \
|
|
396
438
|
--action swap \
|
|
397
439
|
--params '{"tokenIn":"0x...","tokenOut":"0x...","amountInRaw":"1000000","tokenInDecimals":6}' \
|
|
398
440
|
--estimated-amount-usd 5 \
|
|
@@ -430,7 +472,7 @@ thirdfy-agent managed wallet init --auth-token "$THIRDFY_AUTH_TOKEN" --json
|
|
|
430
472
|
# or: --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN"
|
|
431
473
|
thirdfy-agent managed wallet grant --auth-token "$THIRDFY_AUTH_TOKEN" --agent-key "0xYOUR_AGENT_KEY" --token-address "0x..." --max-usd-per-day 250 --json
|
|
432
474
|
# or: --owner-session-token "$THIRDFY_OWNER_SESSION_TOKEN"
|
|
433
|
-
thirdfy-agent agent run --agent-api-key "$
|
|
475
|
+
thirdfy-agent agent run --agent-api-key "$THIRDFY_AGENT_API_KEY" --signer-method fanout_intent --action swap --params '{"tokenIn":"0x...","tokenOut":"0x...","amountInRaw":"1000000","tokenInDecimals":6}' --json
|
|
434
476
|
```
|
|
435
477
|
|
|
436
478
|
### Signer command model (managed + BYOW)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thirdfy/agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
],
|
|
24
24
|
"scripts": {
|
|
25
25
|
"test": "node --test test/*.test.cjs",
|
|
26
|
-
"lint": "node --check ./bin/thirdfy-agent.mjs && node --check ./src/core/*.mjs && node --check ./src/commands/*.mjs && node --check ./src/commands/signals/*.mjs && node --check ./src/cli/*.mjs && node --check ./src/cli/options/*.mjs && node --check ./src/runtime/*.mjs && node --check ./src/runtime/execution/*.mjs && node --check ./src/runtime/actions/*.mjs && node --check ./src/runtime/validation/*.mjs && node --check ./src/runtime/certification/*.mjs",
|
|
26
|
+
"lint": "node --check ./bin/thirdfy-agent.mjs && node --check ./src/core/*.mjs && node --check ./src/commands/*.mjs && node --check ./src/commands/signals/*.mjs && node --check ./src/cli/*.mjs && node --check ./src/cli/options/*.mjs && node --check ./src/runtime/*.mjs && node --check ./src/runtime/execution/*.mjs && node --check ./src/runtime/actions/*.mjs && node --check ./src/runtime/validation/*.mjs && node --check ./src/runtime/certification/*.mjs && node --check ./src/runtime/providerHints/*.mjs && node --check ./src/runtime/risk/*.mjs",
|
|
27
27
|
"validate:cli-manifest-handlers": "node ./scripts/validate-cli-manifest-handlers.mjs",
|
|
28
28
|
"validate:runtime-boundaries": "node ./scripts/validate-runtime-boundaries.mjs",
|
|
29
29
|
"validate:npm-pack": "node ./scripts/validate-npm-pack.mjs",
|
|
@@ -10,6 +10,11 @@ const REQUIRED_FILES = [
|
|
|
10
10
|
'execute-intent.response.schema.json',
|
|
11
11
|
'intent-status.response.schema.json',
|
|
12
12
|
'signals.response.schema.json',
|
|
13
|
+
'preview.response.schema.json',
|
|
14
|
+
'chains.response.schema.json',
|
|
15
|
+
'chains-non-evm.response.schema.json',
|
|
16
|
+
'readiness.response.schema.json',
|
|
17
|
+
'error.response.schema.json',
|
|
13
18
|
];
|
|
14
19
|
|
|
15
20
|
const REQUIRED_KEYS = ['$schema', 'title', 'type'];
|
package/src/cli/errors.mjs
CHANGED
|
@@ -12,16 +12,64 @@ function resolveJsonMode(program, getJsonMode) {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The command group named on the command line, followed down from the root (`['venue']` for
|
|
17
|
+
* `thirdfy-agent venue --json`), and the subcommands it has.
|
|
18
|
+
*/
|
|
19
|
+
function namedGroup(program, argv) {
|
|
20
|
+
const path = [];
|
|
21
|
+
let current = program;
|
|
22
|
+
for (const arg of argv.slice(2)) {
|
|
23
|
+
const next = current.commands.find((cmd) => cmd.name() === arg || cmd.aliases().includes(arg));
|
|
24
|
+
if (!next) continue;
|
|
25
|
+
path.push(next.name());
|
|
26
|
+
current = next;
|
|
27
|
+
}
|
|
28
|
+
return { path, subcommands: current.commands.map((cmd) => cmd.name()) };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* True when Commander stopped because a command group ran without its subcommand (`thirdfy-agent
|
|
33
|
+
* doctor`): an error, exit 1. The bare root and the `help` group (Commander's own help is off so
|
|
34
|
+
* `help onboarding` works) are help requests and exit 0, as before.
|
|
35
|
+
*/
|
|
36
|
+
export function isMissingSubcommand(program, err, argv = process.argv) {
|
|
37
|
+
if (err?.code !== 'commander.help' || Number(err.exitCode ?? 0) !== 1) return false;
|
|
38
|
+
const { path } = namedGroup(program, argv);
|
|
39
|
+
return path.length > 0 && path[0] !== 'help';
|
|
40
|
+
}
|
|
41
|
+
|
|
15
42
|
export function configureCommanderErrors(program, deps) {
|
|
16
|
-
const { getJsonMode, printEnvelope, CLI_VERSION, getApiBase
|
|
43
|
+
const { getJsonMode, printEnvelope, CLI_VERSION, getApiBase } = deps;
|
|
44
|
+
|
|
45
|
+
// The error fields every failed command carries (src/core/errorEnvelope.mjs): a bad command line is
|
|
46
|
+
// fixed by the caller, never retried.
|
|
47
|
+
const argErrorFields = (errorCode) => ({ errorCode, retryable: false, nextAction: 'fix_request' });
|
|
48
|
+
// Commander fails before the context is built, so resolve the API base as the context would:
|
|
49
|
+
// `--api-base`, then THIRDFY_API_BASE, then the default.
|
|
50
|
+
const meta = () => ({
|
|
51
|
+
version: CLI_VERSION,
|
|
52
|
+
apiBase: String(collectVersionFlagsFromArgv(process.argv).apiBase || process.env.THIRDFY_API_BASE || getApiBase()).replace(/\/+$/, ''),
|
|
53
|
+
});
|
|
17
54
|
|
|
18
55
|
program.exitOverride((err) => {
|
|
19
56
|
const jsonMode = resolveJsonMode(program, getJsonMode);
|
|
20
57
|
|
|
58
|
+
// Commander has already printed the help when it raises these; printing it again showed it twice
|
|
59
|
+
// (and the root help after a subcommand's).
|
|
21
60
|
if (err.code === 'commander.help' || err.code === 'commander.helpDisplayed') {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
61
|
+
// A group run without its subcommand (`doctor`) is an error (exit 1), not a help request: in
|
|
62
|
+
// --json mode answer with an envelope on stdout; the help itself went to stderr.
|
|
63
|
+
if (jsonMode && isMissingSubcommand(program, err)) {
|
|
64
|
+
const { path, subcommands } = namedGroup(program, process.argv);
|
|
65
|
+
const command = `thirdfy-agent ${path.join(' ')}`;
|
|
66
|
+
printEnvelope({
|
|
67
|
+
success: false,
|
|
68
|
+
code: 'INVALID_ARGUMENTS',
|
|
69
|
+
message: `\`${command}\` needs a subcommand: ${subcommands.join(', ')}.`,
|
|
70
|
+
data: { ...argErrorFields('INVALID_ARGUMENTS'), command, subcommands },
|
|
71
|
+
meta: meta(),
|
|
72
|
+
});
|
|
25
73
|
}
|
|
26
74
|
throw err;
|
|
27
75
|
}
|
|
@@ -32,8 +80,8 @@ export function configureCommanderErrors(program, deps) {
|
|
|
32
80
|
success: false,
|
|
33
81
|
code: 'UNKNOWN_COMMAND',
|
|
34
82
|
message: err.message || 'Unknown command',
|
|
35
|
-
data: {},
|
|
36
|
-
meta:
|
|
83
|
+
data: { ...argErrorFields('UNKNOWN_COMMAND'), hint: 'Run `thirdfy-agent --help` to list commands.' },
|
|
84
|
+
meta: meta(),
|
|
37
85
|
});
|
|
38
86
|
}
|
|
39
87
|
throw err;
|
|
@@ -52,8 +100,8 @@ export function configureCommanderErrors(program, deps) {
|
|
|
52
100
|
success: false,
|
|
53
101
|
code: 'CLI_ARG_ERROR',
|
|
54
102
|
message: err.message || 'Invalid CLI arguments',
|
|
55
|
-
data: { commanderCode: err.code },
|
|
56
|
-
meta:
|
|
103
|
+
data: { commanderCode: err.code, ...argErrorFields('INVALID_ARGUMENTS') },
|
|
104
|
+
meta: meta(),
|
|
57
105
|
});
|
|
58
106
|
}
|
|
59
107
|
throw err;
|
|
@@ -74,6 +122,8 @@ export function collectVersionFlagsFromArgv(argv = process.argv) {
|
|
|
74
122
|
if (argv.includes('--json')) flags.json = true;
|
|
75
123
|
const apiIdx = argv.indexOf('--api-base');
|
|
76
124
|
if (apiIdx >= 0 && argv[apiIdx + 1]) flags.apiBase = argv[apiIdx + 1];
|
|
125
|
+
const apiInline = argv.find((arg) => arg.startsWith('--api-base='));
|
|
126
|
+
if (!flags.apiBase && apiInline) flags.apiBase = apiInline.slice('--api-base='.length);
|
|
77
127
|
const envIdx = argv.indexOf('--env');
|
|
78
128
|
if (envIdx >= 0 && argv[envIdx + 1]) flags.env = argv[envIdx + 1];
|
|
79
129
|
return flags;
|
package/src/cli/manifest.mjs
CHANGED
|
@@ -71,13 +71,13 @@ export const CLI_MANIFEST = [
|
|
|
71
71
|
{
|
|
72
72
|
path: ['help', 'onboarding'],
|
|
73
73
|
handler: 'commandHelpOnboarding',
|
|
74
|
-
options: ['chains'],
|
|
74
|
+
options: ['chains', 'onboardingHelp'],
|
|
75
75
|
description: 'Structured onboarding help (JSON envelope)',
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
path: ['onboarding', 'help'],
|
|
79
79
|
handler: 'commandHelpOnboarding',
|
|
80
|
-
options: ['chains'],
|
|
80
|
+
options: ['chains', 'onboardingHelp'],
|
|
81
81
|
description: 'Structured onboarding help (JSON envelope)',
|
|
82
82
|
},
|
|
83
83
|
{ path: ['wallet', 'list'], handler: 'commandWalletList', description: 'List configured wallets' },
|
|
@@ -112,7 +112,7 @@ export const CLI_MANIFEST = [
|
|
|
112
112
|
path: ['venue', 'setup'],
|
|
113
113
|
handler: 'commandVenueSetup',
|
|
114
114
|
options: ['auth', 'venue'],
|
|
115
|
-
description: 'Prepare or complete venue onboarding
|
|
115
|
+
description: 'Prepare or complete venue onboarding. Thirdfy also runs the one-time setup by itself with the first order (no funds move)',
|
|
116
116
|
args: { query: '<venue>' },
|
|
117
117
|
tier: 'online',
|
|
118
118
|
},
|
|
@@ -120,7 +120,7 @@ export const CLI_MANIFEST = [
|
|
|
120
120
|
path: ['venue', 'fund'],
|
|
121
121
|
handler: 'commandVenueFund',
|
|
122
122
|
options: ['auth', 'venue'],
|
|
123
|
-
description: 'Return venue funding plan
|
|
123
|
+
description: 'Return the venue funding plan. Nothing moves here: each funding step is a write you send with `run` and confirm',
|
|
124
124
|
args: { query: '<venue>' },
|
|
125
125
|
tier: 'online',
|
|
126
126
|
},
|
|
@@ -207,6 +207,13 @@ export const CLI_MANIFEST = [
|
|
|
207
207
|
options: ['auth'],
|
|
208
208
|
description: 'Credits balance',
|
|
209
209
|
},
|
|
210
|
+
{
|
|
211
|
+
path: ['credits', 'buy'],
|
|
212
|
+
handler: 'commandCreditsBuy',
|
|
213
|
+
tier: 'online',
|
|
214
|
+
options: ['auth', 'creditsBuy'],
|
|
215
|
+
description: "Buy the agent's Thirdfy credits with USDC from its wallet on Base (gas sponsored): --usd <whole dollars>",
|
|
216
|
+
},
|
|
210
217
|
{
|
|
211
218
|
path: ['credits', 'transactions'],
|
|
212
219
|
handler: 'commandCreditsTransactions',
|
|
@@ -519,6 +526,27 @@ export const CLI_MANIFEST = [
|
|
|
519
526
|
options: ['auth'],
|
|
520
527
|
description: 'Rotate agent API key',
|
|
521
528
|
},
|
|
529
|
+
{
|
|
530
|
+
path: ['wallet-control', 'state'],
|
|
531
|
+
handler: 'commandWalletControlState',
|
|
532
|
+
tier: 'online',
|
|
533
|
+
options: ['auth'],
|
|
534
|
+
description: 'Owner: read the user-owned agent wallet state',
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
path: ['wallet-control', 'request'],
|
|
538
|
+
handler: 'commandWalletControlRequest',
|
|
539
|
+
tier: 'online',
|
|
540
|
+
options: ['auth', 'walletControl'],
|
|
541
|
+
description: 'Owner: get the unsigned request for an owner action (update_policy, stop_agent, resume_agent, change_payout, export)',
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
path: ['wallet-control', 'submit'],
|
|
545
|
+
handler: 'commandWalletControlSubmit',
|
|
546
|
+
tier: 'online',
|
|
547
|
+
options: ['auth', 'walletControl'],
|
|
548
|
+
description: 'Owner: relay the signed owner request',
|
|
549
|
+
},
|
|
522
550
|
{
|
|
523
551
|
path: ['agent', 'key', 'revoke'],
|
|
524
552
|
handler: 'commandAgentKeyRevoke',
|
|
@@ -602,7 +630,7 @@ export const CLI_MANIFEST = [
|
|
|
602
630
|
path: ['actions'],
|
|
603
631
|
handler: 'commandActions',
|
|
604
632
|
tier: 'online',
|
|
605
|
-
options: ['auth', '
|
|
633
|
+
options: ['auth', 'actionList', 'dataCategory'],
|
|
606
634
|
description: 'List allowed actions',
|
|
607
635
|
},
|
|
608
636
|
{
|
|
@@ -612,6 +640,20 @@ export const CLI_MANIFEST = [
|
|
|
612
640
|
options: ['signals'],
|
|
613
641
|
description: 'List data provider apps and the signals they publish (fields, units, freshness, roles, credits)',
|
|
614
642
|
},
|
|
643
|
+
{
|
|
644
|
+
path: ['readiness'],
|
|
645
|
+
handler: 'commandReadiness',
|
|
646
|
+
tier: 'online',
|
|
647
|
+
options: ['auth', 'chains'],
|
|
648
|
+
description: 'Can the agent act? agent_wallet: its execution wallet, funding, gas and wallet control; self/hybrid/thirdfy: the delegation check',
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
path: ['chains'],
|
|
652
|
+
handler: 'commandChains',
|
|
653
|
+
tier: 'online',
|
|
654
|
+
options: ['chainList'],
|
|
655
|
+
description: 'Chains Thirdfy acts on (from the API): chain ids, CAIP-2 ids, execution lanes and the Solana contract',
|
|
656
|
+
},
|
|
615
657
|
{
|
|
616
658
|
path: ['preflight'],
|
|
617
659
|
handler: 'commandPreflight',
|
|
@@ -619,6 +661,13 @@ export const CLI_MANIFEST = [
|
|
|
619
661
|
options: ['auth', 'execution'],
|
|
620
662
|
description: 'Governance preflight',
|
|
621
663
|
},
|
|
664
|
+
{
|
|
665
|
+
path: ['preview'],
|
|
666
|
+
handler: 'commandPreview',
|
|
667
|
+
tier: 'online',
|
|
668
|
+
options: ['auth', 'execution'],
|
|
669
|
+
description: 'Preview a write (agent_wallet): would Thirdfy sign it, order and risk preview, and the run command. Nothing is sent',
|
|
670
|
+
},
|
|
622
671
|
{ path: ['run'], handler: 'commandRun', tier: 'online', options: ['auth', 'execution'], description: 'Execute intent' },
|
|
623
672
|
{
|
|
624
673
|
path: ['self-exec'],
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Commander 13+ rejects multi-character short flags; keep the historical `-ni` spelling working, and
|
|
3
|
+
* `--ni`, the spelling the README and onboarding help use.
|
|
4
|
+
*/
|
|
5
|
+
const NOT_INTERACTIVE_ALIASES = new Set(['-ni', '--ni']);
|
|
6
|
+
|
|
2
7
|
export function normalizeLegacyShortFlags(argv) {
|
|
3
8
|
const end = argv.indexOf('--');
|
|
4
|
-
return argv.map((arg, index) => (arg
|
|
9
|
+
return argv.map((arg, index) => (NOT_INTERACTIVE_ALIASES.has(arg) && (end === -1 || index < end) ? '--not-interactive' : arg));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** `help onboarding --path`: one path, as thirdfy-mcp `describeOnboardingPath`. */
|
|
13
|
+
export function attachOnboardingHelpOptions(cmd) {
|
|
14
|
+
return cmd.option('--path <id>', 'one path: email_otp | wallet_sign | existing_credentials | mcp_bootstrap | all');
|
|
5
15
|
}
|
|
6
16
|
|
|
7
17
|
export function attachBootstrapOptions(cmd) {
|
|
@@ -16,9 +26,11 @@ export function attachBootstrapOptions(cmd) {
|
|
|
16
26
|
.option('--otp <otp>', 'email OTP alias')
|
|
17
27
|
.option('--accept-terms', 'accept terms for email login')
|
|
18
28
|
.option('--key-name <name>', 'Privy key name')
|
|
19
|
-
.option('--not-interactive', 'fail fast when required login input is missing (
|
|
29
|
+
.option('--not-interactive', 'fail fast when required login input is missing (aliases: --ni, -ni)')
|
|
20
30
|
.option('--no-bootstrap-agent', 'skip agent bootstrap on email login')
|
|
21
31
|
.option('--rotate-agent-key', 'rotate agent key')
|
|
32
|
+
.option('--owner-payout-address <addr>', 'owner payout address for exits from a wallet under wallet control (API 4.0+)')
|
|
33
|
+
.option('--allowed-actions <list>', "comma-separated catalog action names for the agent's action template (API 4.0+)")
|
|
22
34
|
.option('--login-attempt-id <id>', 'email login attempt id')
|
|
23
35
|
.option('--all', 'logout all sessions')
|
|
24
36
|
.option('--clear-agent-key', 'clear stored agent key on logout');
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export function attachChainOptions(cmd) {
|
|
2
2
|
return cmd.option('--chain-id <id>', 'chain id');
|
|
3
3
|
}
|
|
4
|
+
|
|
5
|
+
/** Options for `chains` (thirdfy-mcp getChainCapabilities / getNonEvmCapabilities). */
|
|
6
|
+
export function attachChainListOptions(cmd) {
|
|
7
|
+
return cmd.option('--non-evm', 'only the non-EVM (Solana) execution contract');
|
|
8
|
+
}
|