@thirdfy/agent-cli 0.2.31 → 0.2.33

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.33] - 2026-07-24
8
+
9
+ ### Fixed
10
+
11
+ - `login email` always prints `executionWallets` / `executionWalletsError` and fails closed with `LOGIN_EMAIL_INCOMPLETE` when `runMode=agent_wallet` and execution wallets are missing or errored. Fund the managed execution wallet, not the owner embed.
12
+
13
+ ## [0.2.32] - 2026-07-23
14
+
15
+ ### Fixed
16
+
17
+ - `doctor certify execution` now keys live catalog and fixture rows by normalized action id (kebab/snake). Catalog `place-hyperliquid-perps-order` (and siblings) overwrite fixtures so required write actions resolve `source=catalog` instead of failing with `CATALOG_SCHEMA_UNAVAILABLE` / write-policy mismatch.
18
+
7
19
  ## [0.2.31] - 2026-07-23
8
20
 
9
21
  ### Added
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.31
43
+ ## What's new in v0.2.33
44
44
 
45
- - Validates action `--params` against catalog `paramsSchema` locally on `preflight`, `run`, and `wallet execute` before HTTP writes. Failures return `PARAMS_SCHEMA_INVALID` with field-level errors.
46
- - Adds `doctor certify execution` for a no-write certification report (CLI version, command availability, venue schema matrix, authority inputs). EarnClaw passes `--no-write`, `--run-mode`, `--schema-digest`, and `--required-actions` for fail-closed pack certification.
45
+ - `login email` always surfaces `executionWallets` (and `executionWalletsError` when present) and fails closed for `agent_wallet` when execution wallets are missing. Fund the printed execution address, not the owner login wallet.
47
46
 
48
47
  Older versions: see [CHANGELOG.md](./CHANGELOG.md) and [GitHub Releases](https://github.com/thirdfy/agent-cli/releases).
49
48
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdfy/agent-cli",
3
- "version": "0.2.31",
3
+ "version": "0.2.33",
4
4
  "description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -450,13 +450,18 @@ async function commandLoginEmail(ctx, flags) {
450
450
  const agentKey = extractAgentKey(data);
451
451
  const wallets = data.wallets && typeof data.wallets === 'object' ? data.wallets : {};
452
452
  const executionWallets = data.executionWallets && typeof data.executionWallets === 'object' ? data.executionWallets : {};
453
+ const executionWalletsError = data.executionWalletsError
454
+ ? String(data.executionWalletsError)
455
+ : data.execution_wallets_error
456
+ ? String(data.execution_wallets_error)
457
+ : null;
453
458
  const userDid = String(data.owner?.creatorDid || wallets.userDid || '').trim();
454
459
  const primaryEvmWallet = String(wallets.primaryEvmWallet || data.owner?.creatorWallet || '').trim();
455
460
  const persistedWallets = {
456
461
  ...wallets,
457
462
  ...(userDid ? { userDid } : {}),
458
463
  ...(primaryEvmWallet ? { primaryEvmWallet } : {}),
459
- ...(Object.keys(executionWallets).length ? { executionWallets } : {}),
464
+ executionWallets,
460
465
  };
461
466
  const runMode = normalizeRunMode(flags.runMode || current.runMode || process.env.THIRDFY_RUN_MODE || 'agent_wallet');
462
467
  const custodyMode = normalizeCustodyMode(flags.custodyMode || current.custodyMode || process.env.THIRDFY_CUSTODY_MODE, runMode);
@@ -491,7 +496,27 @@ async function commandLoginEmail(ctx, flags) {
491
496
  ...(agentKey ? { agentKey } : {}),
492
497
  };
493
498
  }
499
+ // Persist first so operators keep session tokens even when wallet provisioning failed.
494
500
  persistProfileConfig(next);
501
+
502
+ const executionWalletAddresses = Object.values(executionWallets)
503
+ .map((row) => (row && typeof row === 'object' ? String(row.walletAddress || '').trim() : ''))
504
+ .filter((addr) => /^0x[a-fA-F0-9]{40}$/.test(addr));
505
+ const hasExecutionWallet = executionWalletAddresses.length > 0;
506
+ if (runMode === 'agent_wallet' && (executionWalletsError || !hasExecutionWallet)) {
507
+ const fundHint = executionWalletAddresses[0] || 'the managed execution wallet address for each chain';
508
+ throw createCliError(
509
+ 'LOGIN_EMAIL_INCOMPLETE',
510
+ executionWalletsError
511
+ ? `Email login completed but execution wallets failed: ${executionWalletsError}. Fund the managed execution wallet (${fundHint}), not the owner embedded login wallet.`
512
+ : `Email login completed without execution wallets. Fund the managed execution wallet for agent_wallet trading (${fundHint}), not the owner embedded login wallet. Re-run login email or call /api/v1/agent/onboarding/cli/email/complete and confirm executionWallets is returned.`,
513
+ {
514
+ executionWallets,
515
+ executionWalletsError: executionWalletsError || null,
516
+ }
517
+ );
518
+ }
519
+
495
520
  printEnvelope({
496
521
  success: true,
497
522
  code: 'LOGIN_EMAIL_OK',
@@ -504,13 +529,17 @@ async function commandLoginEmail(ctx, flags) {
504
529
  evmWallets: Array.isArray(persistedWallets.evm) ? persistedWallets.evm : [],
505
530
  solanaWallets: Array.isArray(persistedWallets.solana) ? persistedWallets.solana : [],
506
531
  primaryEvmWallet: persistedWallets.primaryEvmWallet || null,
507
- executionWallets:
508
- persistedWallets.executionWallets && typeof persistedWallets.executionWallets === 'object'
509
- ? persistedWallets.executionWallets
510
- : {},
532
+ executionWallets,
533
+ executionWalletsError: executionWalletsError || null,
511
534
  ownerLinkedWallet: data.ownerLinkedWallet || null,
512
535
  configPath: getProfileConfigPath(),
513
536
  nextSteps: response?.nextSteps || [],
537
+ ...(runMode === 'agent_wallet'
538
+ ? {
539
+ fundingReminder:
540
+ 'Fund executionWallets addresses for agent_wallet. Do not fund the owner embedded wallet for Path A trading.',
541
+ }
542
+ : {}),
514
543
  },
515
544
  meta: { apiBase: ctx.apiBase },
516
545
  });
@@ -76,9 +76,10 @@ export function computeActionSchemaDigest(actions) {
76
76
  }
77
77
 
78
78
  function mergeCatalogWithFixtures(liveActions) {
79
+ // Key by normalized action id so catalog kebab-case overwrites fixture snake_case.
79
80
  const byAction = new Map();
80
81
  for (const fixture of VENUE_WRITE_ACTION_FIXTURES) {
81
- byAction.set(fixture.action, {
82
+ byAction.set(normalizeActionKey(fixture.action), {
82
83
  action: fixture.action,
83
84
  aliases: fixture.aliases,
84
85
  provider: fixture.provider,
@@ -90,10 +91,12 @@ function mergeCatalogWithFixtures(liveActions) {
90
91
  for (const row of liveActions || []) {
91
92
  const action = String(row?.action || '').trim();
92
93
  if (!action) continue;
93
- const existing = byAction.get(action);
94
- byAction.set(action, {
94
+ const key = normalizeActionKey(action);
95
+ const existing = byAction.get(key);
96
+ const aliases = Array.isArray(row.aliases) ? row.aliases : existing?.aliases || [];
97
+ byAction.set(key, {
95
98
  action,
96
- aliases: Array.isArray(row.aliases) ? row.aliases : existing?.aliases || [],
99
+ aliases,
97
100
  provider: row.provider || row.providerId || existing?.provider || null,
98
101
  paramsSchema: row.paramsSchema || existing?.paramsSchema || null,
99
102
  requiredParams: Array.isArray(row.requiredParams) ? row.requiredParams : existing?.requiredParams || [],
@@ -104,9 +107,9 @@ function mergeCatalogWithFixtures(liveActions) {
104
107
  }
105
108
 
106
109
  function resolveCatalogRow(catalogByAction, action) {
107
- const direct = catalogByAction.get(action);
108
- if (direct) return direct;
109
110
  const normalized = normalizeActionKey(action);
111
+ const direct = catalogByAction.get(normalized) || catalogByAction.get(action);
112
+ if (direct) return direct;
110
113
  for (const row of catalogByAction.values()) {
111
114
  if (normalizeActionKey(row.action) === normalized) return row;
112
115
  for (const alias of row.aliases || []) {
@@ -117,9 +120,11 @@ function resolveCatalogRow(catalogByAction, action) {
117
120
  }
118
121
 
119
122
  function runSchemaCompatibilityChecks(catalogByAction, actionsToCheck, { requireLiveCatalog = false } = {}) {
120
- const fixturesByAction = new Map(VENUE_WRITE_ACTION_FIXTURES.map((fixture) => [fixture.action, fixture]));
123
+ const fixturesByAction = new Map(
124
+ VENUE_WRITE_ACTION_FIXTURES.map((fixture) => [normalizeActionKey(fixture.action), fixture]),
125
+ );
121
126
  return actionsToCheck.map((actionName) => {
122
- const fixture = fixturesByAction.get(actionName) || null;
127
+ const fixture = fixturesByAction.get(normalizeActionKey(actionName)) || null;
123
128
  const catalogRow = resolveCatalogRow(catalogByAction, actionName);
124
129
  const liveSchema = catalogRow?.source === 'catalog' ? catalogRow.paramsSchema : null;
125
130
  const schema = requireLiveCatalog ? liveSchema : catalogRow?.paramsSchema || fixture?.paramsSchema || null;