@thirdfy/agent-cli 0.2.77 → 0.2.79

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.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The typed reading of a data read (Thirdfy API typed results, thirdfy-api-v2#681): `typed`, with
3
+ * `parseIssues`, `freshness` and `attribution`, wherever the run result nests it. Human output shows
4
+ * it before the full result, so a reader does not dig through the provider's raw `payload`.
5
+ */
6
+
7
+ const isRecord = (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value));
8
+
9
+ /** Keys never searched: the raw vendor reply can be large and never holds the typed reading. */
10
+ const SKIPPED_KEYS = new Set(['payload', 'telemetry', 'preflight', 'laneMetadata']);
11
+ const MAX_DEPTH = 6;
12
+
13
+ function parseJsonText(value) {
14
+ if (typeof value !== 'string') return value;
15
+ const trimmed = value.trim();
16
+ if (!trimmed.startsWith('{')) return value;
17
+ try {
18
+ return JSON.parse(trimmed);
19
+ } catch {
20
+ return value;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * The first object (breadth-first) that carries `typed`, returned as
26
+ * `{ typed, parseIssues, freshness?, attribution? }`; null when the result has no typed reading.
27
+ */
28
+ export function findTypedReading(result) {
29
+ let level = [result];
30
+ for (let depth = 0; depth <= MAX_DEPTH && level.length; depth += 1) {
31
+ const next = [];
32
+ for (const raw of level) {
33
+ const candidate = parseJsonText(raw);
34
+ if (!isRecord(candidate)) continue;
35
+ if (Object.prototype.hasOwnProperty.call(candidate, 'typed')) {
36
+ return {
37
+ typed: candidate.typed ?? null,
38
+ parseIssues: Array.isArray(candidate.parseIssues) ? candidate.parseIssues : [],
39
+ ...(isRecord(candidate.freshness) ? { freshness: candidate.freshness } : {}),
40
+ ...(typeof candidate.attribution === 'string' && candidate.attribution ? { attribution: candidate.attribution } : {}),
41
+ };
42
+ }
43
+ for (const [key, value] of Object.entries(candidate)) {
44
+ if (!SKIPPED_KEYS.has(key)) next.push(value);
45
+ }
46
+ }
47
+ level = next;
48
+ }
49
+ return null;
50
+ }
51
+
52
+ /** The human-mode block printed before the full result. */
53
+ export function formatTypedReadingLead(reading) {
54
+ return `Typed reading:\n${JSON.stringify(reading, null, 2)}\nFull result:`;
55
+ }
@@ -1,5 +1,6 @@
1
1
  import { requireFlag, parseJsonFlag } from '../core/args.mjs';
2
2
  import { apiGet, apiPost } from '../core/http.mjs';
3
+ import { hasVerantaToken, normalizeVenueProviderId } from '../core/venueBrand.mjs';
3
4
  export function createTelemetryCommands({ printEnvelope, resolveAgentApiKey }) {
4
5
  function applySharedPortfolioListingFiltersToQuery(query, flags) {
5
6
  if (flags.chainId) query.set('chainId', String(flags.chainId));
@@ -110,7 +111,10 @@ async function commandTrackAction(ctx, flags, capabilities) {
110
111
  if (flags.idempotencyKey) payload.idempotencyKey = String(flags.idempotencyKey);
111
112
  if (flags.category) payload.category = String(flags.category);
112
113
  if (flags.source) payload.source = String(flags.source);
113
- if (flags.provider) payload.provider = String(flags.provider);
114
+ if (flags.provider) {
115
+ // `veranta` / `veranta-vault` report under the internal `avantis` / `avantis-vault` ids.
116
+ payload.provider = hasVerantaToken(flags.provider) ? normalizeVenueProviderId(flags.provider) : String(flags.provider);
117
+ }
114
118
  if (flags.protocol) payload.protocol = String(flags.protocol);
115
119
  if (flags.namespace) payload.namespace = String(flags.namespace);
116
120
  if (flags.actionType) payload.actionType = String(flags.actionType);
@@ -1,13 +1,16 @@
1
1
  import { apiGet, apiPost } from '../core/http.mjs';
2
2
  import { loadProfileConfig } from '../core/context.mjs';
3
+ import { normalizeVenueProviderId } from '../core/venueBrand.mjs';
3
4
 
5
+ // `avantis` is the internal venue id the API expects; `veranta` (the venue's new name) maps onto it.
4
6
  const VENUES = new Set(['polymarket', 'hyperliquid', 'lighter', 'avantis']);
5
7
 
6
8
  function resolveVenue(flags) {
7
9
  const fromQuery = Array.isArray(flags.query) ? flags.query[0] : flags.query;
8
- const venue = String(flags.venue || fromQuery || '').trim().toLowerCase();
10
+ const requested = String(flags.venue || fromQuery || '').trim().toLowerCase();
11
+ const venue = normalizeVenueProviderId(requested);
9
12
  if (!VENUES.has(venue)) {
10
- throw new Error(`Unsupported venue "${venue}". Use polymarket, hyperliquid, lighter, or avantis.`);
13
+ throw new Error(`Unsupported venue "${requested}". Use polymarket, hyperliquid, lighter, or veranta.`);
11
14
  }
12
15
  return venue;
13
16
  }
@@ -1,5 +1,5 @@
1
1
  import { requireFlag } from '../core/args.mjs';
2
- import { createCliError } from '../core/envelope.mjs';
2
+ import { createCliError, exitAfterOutput } from '../core/envelope.mjs';
3
3
  import { loadProfileConfig } from '../core/context.mjs';
4
4
 
5
5
  export function createWalletCommands(deps) {
@@ -47,7 +47,7 @@ async function commandWalletExecute(ctx, flags, capabilities) {
47
47
  capabilitiesVersion: capabilities?.contractVersion || null,
48
48
  },
49
49
  });
50
- if (!result.success) process.exit(1);
50
+ if (!result.success) await exitAfterOutput(1);
51
51
  }
52
52
 
53
53
 
@@ -76,7 +76,7 @@ async function commandWalletSign(ctx, flags, capabilities) {
76
76
  capabilitiesVersion: capabilities?.contractVersion || null,
77
77
  },
78
78
  });
79
- if (!selfResult.success) process.exit(1);
79
+ if (!selfResult.success) await exitAfterOutput(1);
80
80
  }
81
81
 
82
82
 
@@ -0,0 +1,78 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CLI Endpoint: signals",
4
+ "type": "object",
5
+ "required": ["apps", "signals"],
6
+ "properties": {
7
+ "apps": {
8
+ "type": "array",
9
+ "items": {
10
+ "type": "object",
11
+ "required": ["appId", "title", "signalIds"],
12
+ "properties": {
13
+ "appId": { "type": "string" },
14
+ "title": { "type": "string" },
15
+ "kind": { "type": ["string", "null"] },
16
+ "category": { "type": ["string", "null"] },
17
+ "summary": { "type": ["string", "null"] },
18
+ "manifestVersion": { "type": ["number", "null"] },
19
+ "attribution": { "type": ["string", "null"] },
20
+ "displayValues": { "type": ["boolean", "null"] },
21
+ "docsUrl": { "type": "string" },
22
+ "signalIds": { "type": "array", "items": { "type": "string" } }
23
+ }
24
+ }
25
+ },
26
+ "signals": {
27
+ "type": "array",
28
+ "items": {
29
+ "type": "object",
30
+ "required": ["id", "title", "fields", "freshness", "roles", "action"],
31
+ "properties": {
32
+ "id": { "type": "string" },
33
+ "title": { "type": "string" },
34
+ "summary": { "type": ["string", "null"] },
35
+ "measures": { "type": ["string", "null"] },
36
+ "scope": { "type": ["string", "null"] },
37
+ "universe": {},
38
+ "fields": {
39
+ "type": "array",
40
+ "items": {
41
+ "type": "object",
42
+ "required": ["name"],
43
+ "properties": {
44
+ "name": { "type": "string" },
45
+ "path": { "type": ["string", "null"] },
46
+ "type": { "type": ["string", "null"] },
47
+ "unit": { "type": ["string", "null"] },
48
+ "range": { "type": "array", "items": { "type": "number" } },
49
+ "values": { "type": "array", "items": { "type": "string" } },
50
+ "description": { "type": "string" }
51
+ }
52
+ }
53
+ },
54
+ "asOfPath": { "type": ["string", "null"] },
55
+ "freshness": {
56
+ "type": "object",
57
+ "properties": {
58
+ "cadenceSec": { "type": ["number", "null"] },
59
+ "freshnessSec": { "type": ["number", "null"] },
60
+ "horizonHours": { "type": ["number", "null"] }
61
+ }
62
+ },
63
+ "roles": { "type": "array", "items": { "type": "string" } },
64
+ "rules": { "type": "array", "items": { "type": "string" } },
65
+ "action": { "type": "string" },
66
+ "dataCategory": { "type": ["string", "null"] },
67
+ "billingTier": { "type": ["string", "null"] },
68
+ "credits": { "type": ["number", "null"] },
69
+ "appId": { "type": ["string", "null"] },
70
+ "manifestVersion": { "type": ["number", "null"] },
71
+ "attribution": { "type": ["string", "null"] },
72
+ "displayValues": { "type": ["boolean", "null"] },
73
+ "docsUrl": { "type": "string" }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
@@ -1,5 +1,6 @@
1
1
  import { DEFAULT_TIMEOUT_MS } from './constants.mjs';
2
2
  import { isReadOnlyActionName, isReadOnlyResolvedAction } from './readOnlyActions.mjs';
3
+ import { toLegacyVenueActionKey } from './venueBrand.mjs';
3
4
 
4
5
  /**
5
6
  * Default HTTP timeout for managed writes. A perps order or TP/SL update signs, broadcasts, and waits
@@ -8,6 +9,7 @@ import { isReadOnlyActionName, isReadOnlyResolvedAction } from './readOnlyAction
8
9
  */
9
10
  const DEFAULT_WRITE_TIMEOUT_MS = 90_000;
10
11
 
12
+ // Keyed by the Avantis spelling; `normalizeActionId` maps Veranta names onto it.
11
13
  const LONG_RUNNING_ACTIONS = new Set([
12
14
  'complete_avantis_onboarding',
13
15
  'get_avantis_setup_status',
@@ -29,17 +31,14 @@ const LONG_RUNNING_ACTIONS = new Set([
29
31
  'swap',
30
32
  'execute_optimal_swap',
31
33
  'deposit_hyperliquid_cctp',
32
- // Avantis market open/close: the API streams the batched order for up to 90s, then polls its
34
+ // Veranta (formerly Avantis) market open/close: the API streams the batched order for up to 90s, then polls its
33
35
  // tracking status for up to 90s more before it answers.
34
36
  'place_avantis_perps_order',
35
37
  'close_avantis_position',
36
38
  ]);
37
39
 
38
40
  function normalizeActionId(action) {
39
- return String(action || '')
40
- .trim()
41
- .replace(/-/g, '_')
42
- .toLowerCase();
41
+ return toLegacyVenueActionKey(action).replace(/-/g, '_');
43
42
  }
44
43
 
45
44
  function positiveEnvMs(...names) {
@@ -1,5 +1,3 @@
1
- import { writeSync } from 'node:fs';
2
-
3
1
  export function createCliError(code, message, payload = {}) {
4
2
  const error = new Error(message);
5
3
  error.payload = {
@@ -11,11 +9,52 @@ export function createCliError(code, message, payload = {}) {
11
9
  return error;
12
10
  }
13
11
 
14
- export function printEnvelope(payload, { jsonMode = false } = {}) {
15
- // writeSync: callers often process.exit() immediately after print. Async
16
- // process.stdout.write can truncate JSON when stdout is a pipe (tests/CI).
12
+ /**
13
+ * `humanLead`: text printed in human mode between the status line and the data JSON (a typed
14
+ * reading, a signal list). `humanData: false` prints the lead instead of the data JSON, with a
15
+ * pointer to --json. JSON mode ignores both: the envelope stays as it was.
16
+ */
17
+ export function printEnvelope(payload, { jsonMode = false, humanLead, humanData = true } = {}) {
18
+ // process.stdout.write queues the whole text. fs.writeSync(1) did not: once Node has opened
19
+ // process.stdout, a pipe is non-blocking and writeSync writes only what fits in the pipe buffer
20
+ // (64 KiB on macOS), so a large --json envelope read by a slow consumer was cut. Exits go
21
+ // through exitAfterOutput, which waits for this queue to drain.
22
+ const lead = typeof humanLead === 'string' && humanLead ? `${humanLead}\n` : '';
23
+ const body = lead && humanData === false ? 'Use --json for the full listing.' : JSON.stringify(payload.data || {}, null, 2);
17
24
  const text = jsonMode
18
25
  ? `${JSON.stringify(payload, null, 2)}\n`
19
- : `[${payload.success ? 'OK' : 'ERROR'}] ${payload.code}: ${payload.message}\n${JSON.stringify(payload.data || {}, null, 2)}\n`;
20
- writeSync(1, text);
26
+ : `[${payload.success ? 'OK' : 'ERROR'}] ${payload.code}: ${payload.message}\n${lead}${body}\n`;
27
+ process.stdout.write(text);
28
+ }
29
+
30
+ /** How long exitAfterOutput waits for output to drain before exiting anyway. */
31
+ export const DEFAULT_EXIT_DRAIN_TIMEOUT_MS = 10_000;
32
+
33
+ function exitDrainTimeoutMs() {
34
+ const override = Number(process.env.THIRDFY_AGENT_EXIT_DRAIN_TIMEOUT_MS);
35
+ return Number.isFinite(override) && override >= 0 ? override : DEFAULT_EXIT_DRAIN_TIMEOUT_MS;
36
+ }
37
+
38
+ /**
39
+ * Exit with `code` after stdout and stderr have written everything queued. process.exit() right
40
+ * after a write drops what a pipe has not taken yet. A reader that stops reading cannot hold the
41
+ * CLI open: after THIRDFY_AGENT_EXIT_DRAIN_TIMEOUT_MS (default 10 s) it exits with `code` anyway.
42
+ * The returned promise never settles, so `await exitAfterOutput(1)` (or `return
43
+ * exitAfterOutput(1)`) stops the caller where process.exit did.
44
+ */
45
+ export function exitAfterOutput(code = 0) {
46
+ process.exitCode = code;
47
+ const streams = [process.stdout, process.stderr].filter((stream) => stream && !stream.destroyed && stream.writable);
48
+ let pending = streams.length;
49
+ if (!pending) process.exit(code);
50
+ setTimeout(() => process.exit(code), exitDrainTimeoutMs());
51
+ for (const stream of streams) {
52
+ // The callback runs once every earlier write on this stream has been handed to the OS
53
+ // (or failed, for example EPIPE when the reader has gone).
54
+ stream.write('', () => {
55
+ pending -= 1;
56
+ if (pending === 0) process.exit(code);
57
+ });
58
+ }
59
+ return new Promise(() => {});
21
60
  }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Veranta (formerly Avantis) naming.
3
+ *
4
+ * Avantis, the Base perps venue, rebranded to Veranta. The Thirdfy API is moving its action names
5
+ * from `*_avantis_*` to `*_veranta_*` (Veranta canonical, Avantis kept as aliases). Until every API
6
+ * deployment has the Veranta rows, the CLI has to work against both catalogs:
7
+ *
8
+ * - A Veranta action name resolves to whatever the catalog in hand has (the Avantis row on an older API).
9
+ * - An Avantis action name keeps working unchanged.
10
+ * - Hardcoded CLI lists (lanes, timeouts, address defaults) are keyed by the Avantis spelling, so they
11
+ * compare through `toLegacyVenueActionKey`.
12
+ *
13
+ * Provider ids `avantis` / `avantis-vault` stay the internal identifiers. `veranta` / `veranta-vault`
14
+ * are accepted wherever the CLI reads a provider id.
15
+ */
16
+
17
+ const LEGACY_TOKEN = 'avantis';
18
+ const BRAND_TOKEN = 'veranta';
19
+
20
+ // The token only counts as a whole word inside an action or provider id (`_`/`-` separated).
21
+ const BRAND_TOKEN_RE = /(^|[_-])veranta(?=[_-]|$)/gi;
22
+ const LEGACY_TOKEN_RE = /(^|[_-])avantis(?=[_-]|$)/gi;
23
+
24
+ /** Human-facing venue name. */
25
+ export const VERANTA_DISPLAY_NAME = 'Veranta';
26
+
27
+ /** True when the id carries the Veranta token (`place_veranta_perps_order`, `veranta-vault`). */
28
+ export function hasVerantaToken(value) {
29
+ BRAND_TOKEN_RE.lastIndex = 0;
30
+ return BRAND_TOKEN_RE.test(String(value || ''));
31
+ }
32
+
33
+ /** True when the id carries the Avantis token (`place_avantis_perps_order`, `avantis-vault`). */
34
+ export function hasAvantisToken(value) {
35
+ LEGACY_TOKEN_RE.lastIndex = 0;
36
+ return LEGACY_TOKEN_RE.test(String(value || ''));
37
+ }
38
+
39
+ /** `place_veranta_perps_order` -> `place_avantis_perps_order`. Other names pass through unchanged. */
40
+ export function toAvantisSpelling(value) {
41
+ return String(value ?? '').replace(BRAND_TOKEN_RE, `$1${LEGACY_TOKEN}`);
42
+ }
43
+
44
+ /** `place_avantis_perps_order` -> `place_veranta_perps_order`. Other names pass through unchanged. */
45
+ export function toVerantaSpelling(value) {
46
+ return String(value ?? '').replace(LEGACY_TOKEN_RE, `$1${BRAND_TOKEN}`);
47
+ }
48
+
49
+ /**
50
+ * The other spelling of a venue action name, or null when the name has no Avantis/Veranta token.
51
+ * Used to retry catalog resolution with the spelling the API in hand still uses.
52
+ */
53
+ export function alternateVenueActionSpelling(value) {
54
+ const text = String(value ?? '');
55
+ if (hasVerantaToken(text)) return toAvantisSpelling(text);
56
+ if (hasAvantisToken(text)) return toVerantaSpelling(text);
57
+ return null;
58
+ }
59
+
60
+ /**
61
+ * Brand-insensitive action key for hardcoded CLI lists: trimmed, lowercased, Veranta mapped to the
62
+ * Avantis spelling. Separators are preserved; callers still pick `_` or `-`.
63
+ */
64
+ export function toLegacyVenueActionKey(value) {
65
+ return toAvantisSpelling(String(value ?? '').trim().toLowerCase());
66
+ }
67
+
68
+ /**
69
+ * Normalize a provider or venue id the CLI reads from flags: trimmed, lowercased, and
70
+ * `veranta` / `veranta-vault` mapped to the internal `avantis` / `avantis-vault`.
71
+ */
72
+ export function normalizeVenueProviderId(value) {
73
+ const id = String(value ?? '').trim().toLowerCase();
74
+ if (!id) return id;
75
+ return toAvantisSpelling(id);
76
+ }
@@ -1,6 +1,12 @@
1
1
  import { requireFlag } from '../../core/args.mjs';
2
2
  import { getCachedActionsCatalog } from '../../core/context.mjs';
3
3
  import { suggestCatalogActions } from '../validation/actionParamsGate.mjs';
4
+ import {
5
+ alternateVenueActionSpelling,
6
+ normalizeVenueProviderId,
7
+ toAvantisSpelling,
8
+ toLegacyVenueActionKey,
9
+ } from '../../core/venueBrand.mjs';
4
10
  export function createActionSelection({ getTradingProviderHint, formatProviderAmbiguousActionHint, formatProviderUnknownActionHint }) {
5
11
  function getActionKey(action) {
6
12
  return String(action?.action || action?.id || action?.key || action?.name || '')
@@ -21,7 +27,8 @@ function formatActionList(items) {
21
27
  }
22
28
 
23
29
  function normalizeProviderId(value) {
24
- return String(value || '').trim().toLowerCase();
30
+ // Lowercased id; `veranta` / `veranta-vault` map to the internal `avantis` / `avantis-vault`.
31
+ return normalizeVenueProviderId(value || '');
25
32
  }
26
33
 
27
34
  function rowActionKey(action) {
@@ -46,7 +53,8 @@ function canonicalizeCatalogActionKey(value) {
46
53
  function inferActionProvider(action) {
47
54
  // API catalog metadata (`providerId`, `domain`, `category`) is the source of truth.
48
55
  // This fallback only keeps older catalog rows / older API deployments discoverable.
49
- const key = canonicalActionKey(action);
56
+ // Veranta action names infer like their Avantis spelling (`avantis` / `avantis-vault`).
57
+ const key = toLegacyVenueActionKey(canonicalActionKey(action));
50
58
  const knownMap = {
51
59
  swap: 'trading',
52
60
  'execute-optimal-swap': 'trading',
@@ -263,7 +271,8 @@ function isSpotTradingCatalogAction(action) {
263
271
  }
264
272
 
265
273
  function isProviderActionKey(action, provider) {
266
- const actionKey = rowActionKey(action);
274
+ // Brand-insensitive: `place_veranta_perps_order` matches provider `avantis`.
275
+ const actionKey = toLegacyVenueActionKey(rowActionKey(action));
267
276
  const normalizedProvider = normalizeProviderId(provider).replace(/-/g, '_');
268
277
  if (!actionKey || !normalizedProvider) return false;
269
278
  return (
@@ -283,6 +292,7 @@ function actionMatchesProvider(action, requestedProvider, allActions) {
283
292
  if (requestedProvider === 'hyperliquid') return isProviderActionKey(action, 'hyperliquid');
284
293
  if (requestedProvider === 'lighter') return isProviderActionKey(action, 'lighter');
285
294
  if (requestedProvider === 'avantis') {
295
+ // Veranta (formerly Avantis) perps; `--provider veranta` normalizes to `avantis` before this runs.
286
296
  return isProviderActionKey(action, 'avantis') && !actionKey.includes('vault');
287
297
  }
288
298
  if (requestedProvider === 'polymarket') return isProviderActionKey(action, 'polymarket');
@@ -338,6 +348,16 @@ function applyActionFilters(actions, flags) {
338
348
  const expectedProvider = normalizeProviderId(flags.provider);
339
349
  filtered = filtered.filter((a) => actionMatchesProvider(a, expectedProvider, filtered));
340
350
  }
351
+ if (flags.dataCategory) {
352
+ // Catalog `dataCategory` (Thirdfy API data contract); rows without one are not data reads.
353
+ const categories = new Set(
354
+ String(flags.dataCategory)
355
+ .split(',')
356
+ .map((value) => value.trim().toLowerCase())
357
+ .filter(Boolean),
358
+ );
359
+ filtered = filtered.filter((a) => categories.has(String(a?.dataCategory || '').trim().toLowerCase()));
360
+ }
341
361
  return filtered;
342
362
  }
343
363
 
@@ -351,20 +371,12 @@ async function resolveActionSelection(ctx, flags) {
351
371
  return resolveActionFromRows(requestedAction, actions, flags);
352
372
  }
353
373
 
354
- function resolveActionFromRows(requestedAction, actions, flags = {}) {
374
+ /**
375
+ * Exact catalog match for one spelling: canonical overrides, action id, `_`/`-` canonical id, then
376
+ * alias. Returns null when nothing matches; throws on ambiguity.
377
+ */
378
+ function matchCatalogEntry(requestedAction, keyed, flags = {}) {
355
379
  const requestedLower = String(requestedAction || '').toLowerCase();
356
- const keyed = actions
357
- .map((action) => ({
358
- key: getActionKey(action),
359
- aliases: getActionAliases(action),
360
- action,
361
- }))
362
- .filter((entry) => entry.key);
363
-
364
- if (!keyed.length) {
365
- return { requestedAction, resolvedAction: requestedAction, resolvedActionMeta: null, suggestions: [] };
366
- }
367
-
368
380
  const canonicalAliasOverrides = {
369
381
  get_vaults_fyi_vaults: 'get-vaults-fyi-vaults',
370
382
  get_vaults_fyi_opportunities: 'get-vaults-fyi-vaults',
@@ -446,11 +458,47 @@ function resolveActionFromRows(requestedAction, actions, flags = {}) {
446
458
  )}.${formatProviderAmbiguousActionHint(providerHint)}`
447
459
  );
448
460
  }
461
+ return null;
462
+ }
463
+
464
+ function resolveActionFromRows(requestedAction, actions, flags = {}) {
465
+ const requestedLower = String(requestedAction || '').toLowerCase();
466
+ const keyed = actions
467
+ .map((action) => ({
468
+ key: getActionKey(action),
469
+ aliases: getActionAliases(action),
470
+ action,
471
+ }))
472
+ .filter((entry) => entry.key);
473
+
474
+ if (!keyed.length) {
475
+ // No catalog to check against: send the Avantis spelling of a Veranta name, which every API
476
+ // deployment accepts (older ones only know `*_avantis_*`; newer ones keep it as an alias).
477
+ return {
478
+ requestedAction,
479
+ resolvedAction: alternateVenueActionSpelling(requestedAction) ? toAvantisSpelling(requestedAction) : requestedAction,
480
+ resolvedActionMeta: null,
481
+ suggestions: [],
482
+ };
483
+ }
484
+
485
+ const direct = matchCatalogEntry(requestedAction, keyed, flags);
486
+ if (direct) return direct;
487
+
488
+ // Veranta (formerly Avantis): retry with the other spelling so `place_veranta_perps_order` finds the
489
+ // `place_avantis_perps_order` row on an API that has not shipped Veranta names, and the reverse.
490
+ const alternate = alternateVenueActionSpelling(requestedAction);
491
+ if (alternate) {
492
+ const viaAlternate = matchCatalogEntry(alternate, keyed, flags);
493
+ if (viaAlternate) return { ...viaAlternate, requestedAction };
494
+ }
495
+ const alternateLower = alternate ? alternate.toLowerCase() : null;
449
496
 
497
+ const fuzzyNeedles = alternateLower ? [requestedLower, alternateLower] : [requestedLower];
450
498
  const fuzzy = keyed.filter(
451
499
  (entry) =>
452
- entry.key.toLowerCase().includes(requestedLower) ||
453
- entry.aliases.some((a) => a.toLowerCase().includes(requestedLower))
500
+ fuzzyNeedles.some((needle) => entry.key.toLowerCase().includes(needle)) ||
501
+ entry.aliases.some((a) => fuzzyNeedles.some((needle) => a.toLowerCase().includes(needle)))
454
502
  );
455
503
  if (fuzzy.length === 1) {
456
504
  return { requestedAction, resolvedAction: fuzzy[0].key, resolvedActionMeta: fuzzy[0].action || null };
@@ -3,6 +3,7 @@ import { createHash } from 'node:crypto';
3
3
  import { CLI_MANIFEST } from '../../cli/manifest.mjs';
4
4
  import { validateParamsSchema, sanitizeParamsForSchemaValidation } from '../validation/paramsSchema.mjs';
5
5
  import { VENUE_WRITE_ACTION_FIXTURES } from './venueActionSchemas.mjs';
6
+ import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
6
7
 
7
8
  const require = createRequire(import.meta.url);
8
9
 
@@ -23,7 +24,8 @@ const AUTHORITY_INPUT_CHECKS = [
23
24
  ];
24
25
 
25
26
  function normalizeActionKey(action) {
26
- return String(action || '').trim().replace(/_/g, '-').toLowerCase();
27
+ // Brand-insensitive: a live `place_veranta_perps_order` row replaces the `place_avantis_perps_order` fixture.
28
+ return toLegacyVenueActionKey(action).replace(/_/g, '-');
27
29
  }
28
30
 
29
31
  function isWriteAction(action) {
@@ -218,8 +218,10 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
218
218
  validSample: { orderId: '0xabc' },
219
219
  },
220
220
  {
221
+ // Veranta (formerly Avantis). Fixture ids keep the Avantis spelling so the default schema digest
222
+ // is unchanged; the Veranta names are aliases.
221
223
  action: 'place_avantis_perps_order',
222
- aliases: ['place-avantis-perps-order'],
224
+ aliases: ['place-avantis-perps-order', 'place_veranta_perps_order', 'place-veranta-perps-order'],
223
225
  provider: 'avantis',
224
226
  authorityInputs: ['agentApiKey', 'estimatedAmountUsd'],
225
227
  paramsSchema: {
@@ -249,7 +251,7 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
249
251
  },
250
252
  {
251
253
  action: 'cancel_avantis_perps_order',
252
- aliases: ['cancel-avantis-perps-order'],
254
+ aliases: ['cancel-avantis-perps-order', 'cancel_veranta_perps_order', 'cancel-veranta-perps-order'],
253
255
  provider: 'avantis',
254
256
  authorityInputs: ['agentApiKey'],
255
257
  paramsSchema: {
@@ -267,7 +269,7 @@ export const VENUE_WRITE_ACTION_FIXTURES = [
267
269
  },
268
270
  {
269
271
  action: 'close_avantis_position',
270
- aliases: ['close-avantis-position'],
272
+ aliases: ['close-avantis-position', 'close_veranta_position', 'close-veranta-position'],
271
273
  provider: 'avantis',
272
274
  authorityInputs: ['agentApiKey', 'estimatedAmountUsd'],
273
275
  paramsSchema: {
@@ -1,4 +1,5 @@
1
1
  import { createCliError } from '../../core/envelope.mjs';
2
+ import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
2
3
  export function createExecutionLanes() {
3
4
  const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
4
5
  'place_hyperliquid_perps_order',
@@ -29,6 +30,7 @@ const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
29
30
  'condor-executor-create',
30
31
  'condor_executor_stop',
31
32
  'condor-executor-stop',
33
+ // Veranta (formerly Avantis): keyed by the Avantis spelling; Veranta names map onto it below.
32
34
  'place_avantis_perps_order',
33
35
  'place-avantis-perps-order',
34
36
  'cancel_avantis_perps_order',
@@ -57,11 +59,11 @@ const OFFCHAIN_VENUE_ORDER_ACTIONS = new Set([
57
59
  'cancel-avantis-mm-order',
58
60
  ]);
59
61
  function enforceOffchainVenueLaneCompatibility(action, runMode) {
60
- const normalizedAction = String(action || '').trim().toLowerCase();
62
+ const normalizedAction = toLegacyVenueActionKey(action);
61
63
  if (runMode !== 'self' || !OFFCHAIN_VENUE_ORDER_ACTIONS.has(normalizedAction)) return;
62
64
  throw createCliError(
63
65
  'OFFCHAIN_VENUE_ORDER_SELF_UNSUPPORTED',
64
- 'Polymarket CLOB, Hyperliquid perps, Lighter perps, Avantis perps, Hummingbot executor, and Condor executor actions are offchain signed API actions, not EVM build-tx actions. Use self for setup/funding signatures, then run venue orders with --run-mode thirdfy, --run-mode hybrid, or --run-mode agent_wallet.',
66
+ 'Polymarket CLOB, Hyperliquid perps, Lighter perps, Veranta perps, Hummingbot executor, and Condor executor actions are offchain signed API actions, not EVM build-tx actions. Use self for setup/funding signatures, then run venue orders with --run-mode thirdfy, --run-mode hybrid, or --run-mode agent_wallet.',
65
67
  {
66
68
  recommendedRunModes: ['thirdfy', 'hybrid', 'agent_wallet'],
67
69
  setupOnlyRunMode: 'self',
@@ -4,6 +4,7 @@ import { resolveExecutionContextFromEnv } from '../../core/executionContext.mjs'
4
4
  import { requireFlag, parseJsonFlag } from '../../core/args.mjs';
5
5
  import { loadProfileConfig } from '../../core/context.mjs';
6
6
  import { createCliError } from '../../core/envelope.mjs';
7
+ import { toLegacyVenueActionKey } from '../../core/venueBrand.mjs';
7
8
  import { normalizeRunMode, normalizeManagedRunMode, normalizeHybridWalletMode } from '../../core/runMode.mjs';
8
9
  import { assertHostedRuntimePolymarketWriteIdempotency } from './hostedRuntimeWriteGate.mjs';
9
10
  import { resolveTokenInForFunding } from '../../core/fundingTokenIn.mjs';
@@ -29,10 +30,8 @@ export function mergeConfirmWritesIntoParams(params, flags) {
29
30
  export function resolveExecuteEstimatedAmountUsd(action, flags) {
30
31
  const fromFlags = Number(flags?.estimatedAmountUsd);
31
32
  if (Number.isFinite(fromFlags) && fromFlags > 0) return fromFlags;
32
- const key = String(action || '')
33
- .trim()
34
- .toLowerCase()
35
- .replace(/-/g, '_');
33
+ const key = toLegacyVenueActionKey(action).replace(/-/g, '_');
34
+ // Veranta onboarding (either spelling).
36
35
  if (key === 'complete_avantis_onboarding') return 1;
37
36
  return undefined;
38
37
  }
@@ -527,7 +527,7 @@ async function executeManagedWalletRun(ctx, flags, resolved, options) {
527
527
  // (often as a JSON string under data.result). Reads skip funding gates; routing still fails closed.
528
528
  const isRead = isReadOnlyAction(resolved);
529
529
  const skipPreflight = Boolean(options?.skipPreflight);
530
- // A seeded preflight (Hyperliquid/Lighter/Avantis address defaults) already ran; reuse it.
530
+ // A seeded preflight (Hyperliquid/Lighter/Veranta address defaults) already ran; reuse it.
531
531
  const readFastPath =
532
532
  !skipPreflight && !options?.managedPreflight && shouldUseManagedReadFastPath(flags, resolved, options);
533
533
  let preflight = readFastPath ? READ_FAST_PATH_PREFLIGHT : null;
@@ -11,6 +11,7 @@ import { createDelegationCommands } from '../commands/delegation.mjs';
11
11
  import { createConsoleMandateCommands } from '../commands/consoleMandate.mjs';
12
12
  import { createBootstrapCommands } from '../commands/bootstrap.mjs';
13
13
  import { createDiscoveryCommands } from '../commands/discovery.mjs';
14
+ import { createSignalsCommands } from '../commands/signals/command.mjs';
14
15
  import { createCreditsCommands } from '../commands/credits.mjs';
15
16
  import { createChatCommands } from '../commands/chat.mjs';
16
17
  import { createBrainCommands } from '../commands/brain.mjs';
@@ -67,6 +68,8 @@ export function createRuntimeHandlers(deps) {
67
68
  resolveAgentApiKey,
68
69
  });
69
70
 
71
+ const __signals = createSignalsCommands({ printEnvelope });
72
+
70
73
  const __credits = createCreditsCommands({
71
74
  printEnvelope,
72
75
  getAuthToken,
@@ -232,6 +235,7 @@ export function createRuntimeHandlers(deps) {
232
235
  commandManagedSetup: __doctor.commandManagedSetup,
233
236
  commandCatalogsList: __discovery.commandCatalogsList,
234
237
  commandActions: __discovery.commandActions,
238
+ commandSignals: __signals.commandSignals,
235
239
  commandCreditsBalance: __discovery.commandCreditsBalance,
236
240
  commandCreditsTransactions: __credits.commandCreditsTransactions,
237
241
  commandCreditsUsage: __credits.commandCreditsUsage,