@thirdfy/agent-cli 0.2.39 → 0.2.41

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.41] - 2026-07-28
8
+
9
+ ### Fixed
10
+
11
+ - `--provider lifi` soft-matches catalog `providerId: bridge` for `get_bridge_quote` / `execute_bridge` (historical pack taxonomy) while still matching real trade provider `lifi` (Robinhood 4663). Preferred flag remains `--provider bridge`. MCP parity in `thirdfy-mcp` **0.0.84**.
12
+
13
+ ## [0.2.40] - 2026-07-27
14
+
15
+ ### Added
16
+
17
+ - 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**.
18
+
7
19
  ## [0.2.39] - 2026-07-27
8
20
 
9
21
  ### Changed
package/README.md CHANGED
@@ -40,13 +40,10 @@ Run without global install:
40
40
  npx @thirdfy/agent-cli --help
41
41
  ```
42
42
 
43
- ## What's new in v0.2.39
43
+ ## What's new in v0.2.41
44
+ - `--provider lifi` soft-matches catalog `providerId: bridge` for bridge quote/execute (historical pack flag) while still matching trade provider `lifi` on Robinhood 4663. Prefer `--provider bridge` for bridges.
45
+ - See [CHANGELOG.md](./CHANGELOG.md) for older versions.
44
46
 
45
- - Morpho hints: Base MetaMorpho V1 writes on `8453`; Robinhood Vault V2 on `4663` is discoverable read-only until `maxDeposit > 0`. Prefer `depositPlan` from `get_earn_opportunities`.
46
- - Updated [`docs/providers/earn-morpho.md`](./docs/providers/earn-morpho.md) for GraphQL discovery and V1 vs V2 behavior.
47
- - Pairs with `thirdfy-mcp` **0.0.82** and Thirdfy API develop (Morpho GraphQL integration).
48
-
49
- Older versions: see [CHANGELOG.md](./CHANGELOG.md) and [GitHub Releases](https://github.com/thirdfy/agent-cli/releases).
50
47
 
51
48
  ## Quick start
52
49
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdfy/agent-cli",
3
- "version": "0.2.39",
3
+ "version": "0.2.41",
4
4
  "description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -239,6 +239,14 @@ function actionMatchesProvider(action, requestedProvider, allActions) {
239
239
  if (provider === 'hyperliquid' || provider === 'lighter') return false;
240
240
  return isProviderActionKey(action, 'bridge');
241
241
  }
242
+ // Soft-compat: packs historically used --provider lifi for Li.Fi bridge. Catalog providerId is bridge.
243
+ // Still match real trade providerId lifi (Robinhood 4663 spot). Do not remap normalizeProviderId globally.
244
+ if (requestedProvider === 'lifi') {
245
+ if (provider === 'lifi') return true;
246
+ if (provider === 'hyperliquid' || provider === 'lighter') return false;
247
+ if (provider === 'bridge' || isProviderActionKey(action, 'bridge')) return true;
248
+ return false;
249
+ }
242
250
  if (requestedProvider.startsWith('dogeos')) return isProviderActionKey(action, requestedProvider);
243
251
  if (requestedProvider === 'earn') {
244
252
  return provider === 'earn' || provider === 'morpho' || provider === 'aegis' || provider === 'curve' || provider === 'capyfi' || provider === 'vaults-fyi' || provider === 'yield-xyz' || isEarnCatalogAction(action);
@@ -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