@thirdfy/agent-cli 0.2.38 → 0.2.40

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 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.40] - 2026-07-27
8
+
9
+ ### Added
10
+
11
+ - EarnClaw runtime Polymarket write gate: when `EARNCLAW_RUNTIME_ID` or `THIRDFY_EXECUTION_RUNTIME_ID` is set, `place_polymarket_order` / `place_prediction_order` require `idempotencyKey` starting with `earnclaw:<runtimeId>`. Blocks bare CLI/dashboard tool buys on managed Hermes machines. MCP parity in `thirdfy-mcp` **0.0.83**.
12
+
13
+ ## [0.2.39] - 2026-07-27
14
+
15
+ ### Changed
16
+
17
+ - Morpho provider hints: `supportedChains` `[8453, 4663]`; Base MetaMorpho V1 writable, Robinhood Vault V2 read-only until deposit capacity opens. Updated `docs/providers/earn-morpho.md`. Pairs with Thirdfy API develop + `thirdfy-mcp` **0.0.82**.
18
+
7
19
  ## [0.2.38] - 2026-07-25
8
20
 
9
21
  ### Fixed
package/README.md CHANGED
@@ -40,13 +40,11 @@ Run without global install:
40
40
  npx @thirdfy/agent-cli --help
41
41
  ```
42
42
 
43
- ## What's new in v0.2.38
43
+ ## What's new in v0.2.40
44
+ - EarnClaw machines: Polymarket place orders require an `earnclaw:<runtimeId>…` idempotency key (blocks ad-hoc CLI/dashboard buys).
45
+ - Pack execute-gate and operator exits already set this key; bare tool writes fail closed.
46
+ - See [CHANGELOG.md](./CHANGELOG.md) for older versions.
44
47
 
45
- - Earn discovery prefers `get_earning_opportunities` / `get-earning-opportunities` (legacy `get_earn_opportunities` aliases remain).
46
- - Lighter onboarding hints: repeat `complete_lighter_onboarding` on Base (`8453`) until setup is ready.
47
- - Pairs with MCP **0.0.81** and Thirdfy API **v3.13.26**.
48
-
49
- Older versions: see [CHANGELOG.md](./CHANGELOG.md) and [GitHub Releases](https://github.com/thirdfy/agent-cli/releases).
50
48
 
51
49
  ## Quick start
52
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdfy/agent-cli",
3
- "version": "0.2.38",
3
+ "version": "0.2.40",
4
4
  "description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,63 @@
1
+ import { createCliError } from '../../core/envelope.mjs';
2
+
3
+ function readUuid(value) {
4
+ const trimmed = String(value || '').trim();
5
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(trimmed)
6
+ ? trimmed
7
+ : undefined;
8
+ }
9
+
10
+ const POLYMARKET_PLACE_ACTIONS = new Set([
11
+ 'place_polymarket_order',
12
+ 'place-polymarket-order',
13
+ 'place_prediction_order',
14
+ 'place-prediction-order',
15
+ ]);
16
+
17
+ export function resolveEarnclawRuntimeId(env = process.env) {
18
+ const runtimeId = readUuid(env.THIRDFY_EXECUTION_RUNTIME_ID)
19
+ || readUuid(env.EARNCLAW_RUNTIME_ID);
20
+ return runtimeId ? runtimeId.toLowerCase() : '';
21
+ }
22
+
23
+ export function normalizePolymarketPlaceAction(action) {
24
+ return String(action || '')
25
+ .trim()
26
+ .toLowerCase()
27
+ .replace(/_/g, '-');
28
+ }
29
+
30
+ export function isPolymarketPlaceAction(action) {
31
+ const key = String(action || '').trim().toLowerCase();
32
+ if (POLYMARKET_PLACE_ACTIONS.has(key)) return true;
33
+ return normalizePolymarketPlaceAction(key) === 'place-polymarket-order'
34
+ || normalizePolymarketPlaceAction(key) === 'place-prediction-order';
35
+ }
36
+
37
+ /**
38
+ * On EarnClaw-managed Hermes machines, ad-hoc CLI/MCP/dashboard tool calls can
39
+ * place Polymarket orders without pack execute-gate. Require an earnclaw-scoped
40
+ * idempotency key so only adapter/operator keys pass.
41
+ */
42
+ export function assertEarnclawPolymarketWriteIdempotency({
43
+ action,
44
+ idempotencyKey,
45
+ paramsIdempotencyKey,
46
+ env = process.env,
47
+ } = {}) {
48
+ if (!isPolymarketPlaceAction(action)) return null;
49
+ const runtimeId = resolveEarnclawRuntimeId(env);
50
+ if (!runtimeId) return null;
51
+
52
+ const prefix = `earnclaw:${runtimeId}`;
53
+ const keys = [idempotencyKey, paramsIdempotencyKey]
54
+ .map((key) => String(key || '').trim())
55
+ .filter(Boolean);
56
+ if (keys.some((key) => key.toLowerCase().startsWith(prefix))) return null;
57
+
58
+ throw createCliError(
59
+ 'EARNCLAW_PM_IDEMPOTENCY_REQUIRED',
60
+ `EarnClaw runtime ${runtimeId} requires Polymarket place orders to use idempotencyKey starting with "${prefix}". ` +
61
+ 'Pack execute-gate and operator exits set this automatically. Bare CLI/dashboard tool writes are blocked.',
62
+ );
63
+ }
@@ -3,6 +3,7 @@ import { requireFlag, parseJsonFlag } from '../../core/args.mjs';
3
3
  import { loadProfileConfig } from '../../core/context.mjs';
4
4
  import { createCliError } from '../../core/envelope.mjs';
5
5
  import { normalizeRunMode, normalizeManagedRunMode, normalizeHybridWalletMode } from '../../core/runMode.mjs';
6
+ import { assertEarnclawPolymarketWriteIdempotency } from './earnclawRuntimeWriteGate.mjs';
6
7
  export function createExecutionPayloads({ getPreparedParams, rawUnitsToDecimalString }) {
7
8
  function buildBuildTxPayload(flags, resolvedAction) {
8
9
  const runMode = String(flags.runMode || '').trim().toLowerCase() || 'self';
@@ -125,6 +126,11 @@ function buildManagedExecutePayload(flags, options) {
125
126
  } else if (flags.idempotencyKey) {
126
127
  payload.executionIdempotencyKey = String(flags.idempotencyKey);
127
128
  }
129
+ assertEarnclawPolymarketWriteIdempotency({
130
+ action: payload.action,
131
+ idempotencyKey: payload.executionIdempotencyKey,
132
+ paramsIdempotencyKey: managedParams?.idempotencyKey,
133
+ });
128
134
  return payload;
129
135
  }
130
136
 
@@ -171,6 +177,13 @@ function buildIntentPayload(flags, options) {
171
177
  } else if (flags.idempotencyKey) {
172
178
  payload.idempotencyKey = String(flags.idempotencyKey);
173
179
  }
180
+ if (!options.validationOnly) {
181
+ assertEarnclawPolymarketWriteIdempotency({
182
+ action: payload.action,
183
+ idempotencyKey: payload.idempotencyKey,
184
+ paramsIdempotencyKey: payload.params?.idempotencyKey,
185
+ });
186
+ }
174
187
  return payload;
175
188
  }
176
189
 
@@ -392,10 +392,11 @@ export function createProviderHints({ getNegotiatedCapabilitiesCache }) {
392
392
  canonicalWriteAction: 'deposit_earn_position',
393
393
  readActions: ['get_morpho_vaults', 'get_earn_provider', 'get_earn_position'],
394
394
  orderManagementActions: ['deposit_earn_position', 'withdraw_earn_position'],
395
- supportedChains: [8453, 84532],
396
- laneCompatibility: 'Morpho deposits require ERC-20 approvals to the vault/market routes surfaced by the catalog before deposit_earn_position.',
395
+ supportedChains: [8453, 4663],
396
+ laneCompatibility:
397
+ 'Base MetaMorpho V1 vaults are writable (use depositPlan from discovery). Robinhood Vault V2 on 4663 is discoverable read-only until maxDeposit > 0. Deposits require ERC-20 approval before deposit_earn_position.',
397
398
  example:
398
- 'thirdfy-agent actions --provider morpho && thirdfy-agent run --action deposit_earn_position --params \'{"providerId":"morpho","vaultAddress":"0x...","tokenAddress":"0x...","amount":"10","chainId":8453}\'',
399
+ 'thirdfy-agent run --action get_earn_opportunities --params \'{"providerId":"morpho","chainId":8453,"asset":"USDC"}\' && thirdfy-agent run --action deposit_earn_position --params \'{"providerId":"morpho","chainId":8453,"vaultAddress":"0x...","tokenAddress":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","amount":"0.1","tokenDecimals":6}\'',
399
400
  };
400
401
  }
401
402
  if (provider === 'aegis') {