@thirdfy/agent-cli 0.5.0 → 0.5.1

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
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to `@thirdfy/agent-cli` are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
4
 
5
+ ## [0.5.1] - 2026-10-16
6
+
7
+ A patch release: `doctor certify execution` works for agents with a narrow allowlist.
8
+
9
+ ### Fixed
10
+
11
+ - **`doctor certify execution` no longer fails on venue writes outside the agent's allowlist** (THI-201). With an agent key, the CLI reads the agent's own action list, so bare certification flagged every venue write the agent may not call (for example `place_hyperliquid_tp_sl` or `update_lighter_leverage`) as `CATALOG_SCHEMA_UNAVAILABLE` and failed. It now checks only the venue writes the agent may call and lists the rest in `skippedVenueActions`. An allowed action with a broken schema, a `--required-actions` entry the agent can't call, and a public catalog missing a venue write all still fail.
12
+
5
13
  ## [0.5.0] - 2026-10-15
6
14
 
7
15
  `thirdfy-agent login` signs in with OAuth in the browser (auth.thirdfy.com, the same sign-in the MCP uses), as one agent with read access, plus trade with `--trade`. A minor version because `login` without a token flag now starts that sign-in instead of failing, and `credits balance` / `pricing quote` drop their credit counts for USD. `login email`, token flags and agent API keys work as before.
package/README.md CHANGED
@@ -39,12 +39,11 @@ Run without global install:
39
39
  npx @thirdfy/agent-cli --help
40
40
  ```
41
41
 
42
- ## What's new in v0.5.0
42
+ ## What's new in v0.5.1
43
43
 
44
- - **Sign in with your Thirdfy account:** `thirdfy-agent login` opens your browser at auth.thirdfy.com. You pick one agent and approve read access, and trade access with `thirdfy-agent login --trade`. The CLI then acts as that agent, refreshes the sign-in by itself, and `thirdfy-agent logout` revokes it. Disconnect it any time in Connected apps at auth.thirdfy.com.
45
- - **Same sign-in as the MCP:** the same consent page and the same Connected apps list as Claude, ChatGPT and Cursor.
46
- - **Nothing else changes:** `login email`, `--auth-token`, `--owner-session-token` and agent API keys work as before, and an explicit `--agent-api-key` always wins over the sign-in.
47
- - Older versions: see [CHANGELOG.md](./CHANGELOG.md).
44
+ - **`doctor certify execution` passes for agents with a narrow allowlist.** It now checks only the venue writes your agent may call, and lists the others under `skippedVenueActions` instead of failing. Runtimes that stop on a failed certify (Hermes, OpenClaw) start again.
45
+ - **Still fails closed:** an allowed action with a broken schema, a `--required-actions` entry the agent can't call, or a missing catalog.
46
+ - Browser sign-in (`thirdfy-agent login`, `--trade` for writes) arrived in 0.5.0. Older versions: see [CHANGELOG.md](./CHANGELOG.md).
48
47
 
49
48
  ## Quick start
50
49
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdfy/agent-cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Thirdfy Agent CLI for onboarding, governance preflight, execute-intent, and status polling.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from 'module';
2
2
  import { requireFlag } from '../core/args.mjs';
3
3
  import { apiGet, requestWithRouteFallback } from '../core/http.mjs';
4
- import { loadProfileConfig, getProfileConfigPath, getCachedActionsCatalog } from '../core/context.mjs';
4
+ import { loadProfileConfig, getProfileConfigPath, getCachedActionsCatalogSnapshot } from '../core/context.mjs';
5
5
  import { DEFAULT_API_BASE, PROFILE_DEFAULTS } from '../core/constants.mjs';
6
6
  import { DEFAULT_CHAIN_ID, describeChainGas, parseChainIdFlag, pinnedRpcProvider, resolveChainRpcUrl } from '../core/chainRegistry.mjs';
7
7
  import { exitAfterOutput } from '../core/envelope.mjs';
@@ -333,9 +333,12 @@ async function commandManagedSetup(ctx, flags, capabilities) {
333
333
 
334
334
  async function commandDoctorCertifyExecution(ctx, flags) {
335
335
  let liveActions = null;
336
+ let catalogPolicyScoped = false;
336
337
  let catalogError = null;
337
338
  try {
338
- liveActions = await getCachedActionsCatalog(ctx, flags);
339
+ const snapshot = await getCachedActionsCatalogSnapshot(ctx, flags);
340
+ liveActions = snapshot.actions;
341
+ catalogPolicyScoped = snapshot.policyScoped === true;
339
342
  } catch (error) {
340
343
  catalogError = {
341
344
  message: error?.message || 'Actions catalog fetch failed',
@@ -356,6 +359,7 @@ async function commandDoctorCertifyExecution(ctx, flags) {
356
359
  runMode,
357
360
  expectedSchemaDigest,
358
361
  requiredActions,
362
+ catalogPolicyScoped,
359
363
  });
360
364
 
361
365
  printEnvelope({
@@ -369,6 +373,7 @@ async function commandDoctorCertifyExecution(ctx, flags) {
369
373
  catalog: {
370
374
  fetched: Array.isArray(liveActions),
371
375
  actionCount: Array.isArray(liveActions) ? liveActions.length : 0,
376
+ policyScoped: catalogPolicyScoped,
372
377
  error: catalogError,
373
378
  },
374
379
  },
@@ -227,6 +227,7 @@ export function buildExecutionCertificationReport({
227
227
  runMode = null,
228
228
  expectedSchemaDigest = null,
229
229
  requiredActions = [],
230
+ catalogPolicyScoped = false,
230
231
  } = {}) {
231
232
  const version = String(cliVersion || PACKAGE_VERSION).trim();
232
233
  const normalizedRequiredActions = parseRequiredActions(requiredActions);
@@ -259,10 +260,25 @@ export function buildExecutionCertificationReport({
259
260
  }
260
261
  }
261
262
 
263
+ // A keyed catalog lists only the agent's allowlist. Bare certification then checks the venue
264
+ // writes this agent may call; the rest are reported as skipped, not as missing schemas.
265
+ // An unscoped (public) catalog that lacks a venue action still fails closed.
266
+ const scopeToAllowlist = bareCertification && catalogFetched && catalogPolicyScoped === true;
267
+ const liveActionKeys = new Set();
268
+ for (const row of liveActions || []) {
269
+ for (const name of [row?.action, ...(Array.isArray(row?.aliases) ? row.aliases : [])]) {
270
+ if (name) liveActionKeys.add(normalizeActionKey(String(name)));
271
+ }
272
+ }
273
+ const skippedVenueActions = [];
262
274
  const venueActionNames =
263
275
  normalizedRequiredActions.length > 0
264
276
  ? normalizedRequiredActions.filter((action) => isWriteAction(action))
265
- : VENUE_WRITE_ACTION_FIXTURES.map((fixture) => fixture.action);
277
+ : VENUE_WRITE_ACTION_FIXTURES.map((fixture) => fixture.action).filter((action) => {
278
+ if (!scopeToAllowlist || liveActionKeys.has(normalizeActionKey(action))) return true;
279
+ skippedVenueActions.push({ action, reason: 'NOT_ON_AGENT_ALLOWLIST' });
280
+ return false;
281
+ });
266
282
 
267
283
  const venueActions = runSchemaCompatibilityChecks(catalogByAction, venueActionNames, {
268
284
  requireLiveCatalog: bareCertification,
@@ -380,6 +396,7 @@ export function buildExecutionCertificationReport({
380
396
  commandAvailability,
381
397
  authorityInputs,
382
398
  venueActions,
399
+ skippedVenueActions,
383
400
  checks,
384
401
  };
385
402
  }