@vtxmacro/cli 2026.9.61 → 2026.9.62

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/README.md CHANGED
@@ -97,9 +97,8 @@ control Main in **Agent** mode using the same VTX OAuth grant and its own local
97
97
  adapter-specific vendor credential: ChatGPT, Copilot, or Grok login, or a DeepSeek
98
98
  or Pi provider API key. VTX assigns the running bot and supplies canonical Agent instructions. The
99
99
  assigned runtime keeps a durable thread, requests assignment-scoped data as
100
- needed, and chooses whether to submit a structured decision. A configured
101
- observation schedule runs independently of model turns; it never forces HOLD
102
- or trading JSON. Without a schedule, the model chooses its next wake. Review and Screener remain Provider-only.
100
+ needed, and chooses whether to submit a structured decision. The model chooses
101
+ its next wake through its adapter. A wake never forces HOLD or trading JSON. Review and Screener remain Provider-only.
103
102
 
104
103
  Before the first Codex login, enable **Device code authorization for Codex** in
105
104
  ChatGPT Security settings. Only enter a device code from a login you initiated,
@@ -167,7 +166,7 @@ vtx inference-host service install --adapter pi --instance pi-1
167
166
 
168
167
  Pi uses the package-owned SDK and exact provider/model identities. The existing
169
168
  Provider / Agent controls apply: Provider receives VTX's prompt and has no
170
- tools; Main Agent keeps an isolated session and the canonical VTX assignment tools, including schedule control.
169
+ tools; Main Agent keeps an isolated session and the canonical VTX assignment tools.
171
170
  The initial qualified models use an OpenAI API key; the release embeds VTX's
172
171
  configured exact model IDs and SDK capability mappings. API compatibility alone
173
172
  does not qualify a provider. Ordinary Pi
@@ -319,73 +318,22 @@ schema plus `provenance` with `source: "external_agent"`, a stable
319
318
  `agent_run_id`, and the requested and actual model and reasoning effort. Use
320
319
  `agent-decision-status` after an uncertain submit response. Use
321
320
  `agent-assignment-release` only when intentionally ending the foreground Agent
322
- session; normal cadence is recorded with `agent-assignment-heartbeat`.
323
-
324
- For recurring checks in any supported live Agent harness, configure a schedule
325
- with `agent-schedule-set --json` after claiming the assignment, then loop
326
- `agent-check-next --wait-seconds 50 --json`. Keep `agent-run` open: it samples
327
- markets independently of model reasoning, and the check tool handles waiting.
328
- When `check_ready` returns, review its observation using your existing
329
- authorized tools. Finish with `agent-check-complete --json` and stdin
330
- `{"turn_id":"<returned-id>"}`. No HOLD or trading JSON is required. If you
331
- choose to submit a decision, include that `turn_id` with the existing
332
- candidate/provenance fields. A `waiting` response means call check-next again;
333
- `check_pending` means finish the exact unfinished check first. Resolve an
334
- uncertain decision through decision-status before completing its check.
335
- This preserves the original harness session and all its authorized tools
336
- without writing a harness-specific scheduler. It cannot reopen an exited
337
- harness session; the managed wrapper below supplies unattended process restart.
338
-
339
- ## Managed Agent cadence and native tools
340
-
341
- A compatible managed wrapper lets the harness keep its own authorized tools
342
- while VTX handles observation timing, thread handoff, heartbeats and service
343
- supervision. The packaged `vtx-managed-codex` binary is the native Codex
344
- reference wrapper. Other harnesses need a compatible wrapper; ordinary
345
- foreground support alone does not prove unattended managed invocation.
346
-
347
- Create a private, current-user-owned local launch file. For example, point Node
348
- at the installed wrapper JavaScript and the exact local Codex executable:
349
-
350
- ```json
351
- {
352
- "schema_version": "vtx_managed_harness_launch_v1",
353
- "executable": "/absolute/path/to/node",
354
- "argv": ["/absolute/path/to/vtx-managed-codex.js", "/absolute/path/to/codex"],
355
- "workspace": "/absolute/path/to/agent-workspace",
356
- "recovery_marker": "/absolute/private/path/to/agent-turn.json"
357
- }
358
- ```
359
-
360
- Use native absolute paths on Windows. The file must be private to the current
361
- user. The wrapper creates a dedicated native thread with the user's local
362
- configuration and tools; it does not adopt a visible conversation or approve
363
- interactive permission requests. Launch commands come only from this local
364
- file. The host never accepts executable paths from an assignment or model.
365
-
366
- Pass an initial request on stdin, such as
367
- `{"schedule":{"interval_seconds":60,"objective":"Observe markets; act only when appropriate.","observation":{"capability":"market.snapshot","arguments":{"symbols":["BTC"],"timeframe":"1m"}}}}`.
368
- This is an example cadence; the server's configured bounds apply. After the
369
- normal VTX login for this isolated instance, install and start its worker:
370
-
371
- ```bash
372
- vtx inference-host service install --adapter codex --instance agent-1 --launch-config /absolute/private/launch.json --model <exact-model-id> --effort <effort> --json < /absolute/private/schedule.json
373
- ```
374
-
375
- The existing OS service manager supervises the worker and starts it at login.
376
- Use the same instance for service status, logs, stop/start and uninstall. The
377
- foreground alternative is `agent-managed-run` with the same launch/model/effort
378
- arguments and JSON input; it requires its process to stay open. Neither command
379
- starts a VTX bot or changes its profile settings. Bind and start the intended
380
- bot separately through its authorized normal control path.
381
-
382
- `agent-schedule-read --instance agent-1 --json` inspects the active assignment.
383
- `agent-schedule-set --instance agent-1 --json` accepts
384
- `{"schedule":...}` on stdin; `{"schedule":null}` cancels recurring checks
385
- without stopping the bot or closing positions. The persisted objective and
386
- revision travel with the assignment. Saved profile trading interval is separate.
387
- Long model turns may delay review even while observations continue. Sleep,
388
- outages and stale sources are recorded as gaps, not successful fresh checks.
321
+ session; `agent-assignment-heartbeat` maintains the assignment lease.
322
+ Heartbeat metadata is not evidence that market data was checked.
323
+
324
+ For recurring checks, ask your harness to run this Agent workflow at the desired
325
+ cadence using its own scheduling facility. On each wake, fetch current VTX data,
326
+ inspect source timestamps, and decide whether to submit trading JSON. A check
327
+ may end without submitting anything, including HOLD. VTX does not configure a
328
+ second scheduler, and saved profile trading interval does not schedule Agent
329
+ turns. Keep `agent-run` open for heartbeats; it does not fetch market data or
330
+ wake or restart your harness.
331
+
332
+ Use a single active turn for the assignment. Long turns, sleep, outages, and
333
+ harness scheduling limits can delay checks; report missed checks and resume
334
+ with fresh data instead of replaying old scheduled work. Continuous operation
335
+ requires the harness and its host to remain available. Record actual successful
336
+ read times and source timestamps to verify cadence and freshness.
389
337
 
390
338
  `prompt.variables` discovers every canonical trading prompt variable;
391
339
  `prompt.context` reads selected names or all names for one owned profile.
@@ -16,7 +16,6 @@ type Input = {
16
16
  provider: 'openai' | 'venice';
17
17
  mode: 'provider' | 'agent'; systemPrompt: string; userPrompt: string;
18
18
  outputSchema: Json; decisionSchema: Json; dataContract: { id: string; input_schema: Json }[];
19
- toolSpecs: Json[];
20
19
  requestedModel: string; providerModel: string; reasoningEffort: string;
21
20
  nativeReasoningEffort: string | null; maxRounds: number;
22
21
  deadlineAtMs: number;
@@ -156,22 +155,19 @@ const chatRequestMessages = (harness: ExoHarnessModule, events: unknown[]): Mess
156
155
  return messages;
157
156
  };
158
157
 
159
- const toolsFor = (input: Input): Json[] => input.mode === 'provider' ? [] : input.toolSpecs.map((spec) => {
160
- const parameters = record(spec.inputSchema);
161
- // Chat serializers enumerate common top-level fields; preserve the complete
162
- // canonical branch schema supplied by the authenticated local broker.
163
- const branches = Array.isArray(parameters.oneOf) ? parameters.oneOf.map(record) : [];
164
- const branchProperties = branches.map((branch) => record(branch.properties));
165
- const common = branchProperties.length > 0 ? {
166
- type: 'object', additionalProperties: false, required: branches[0].required,
167
- properties: Object.fromEntries(Object.keys(branchProperties[0]).map((key) => [key,
168
- branchProperties.every((properties) => key in properties)
169
- ? key === 'capability' ? { type: 'string', enum: branchProperties.map((properties) => record(properties[key]).const) }
170
- : { type: 'object' } : {},
171
- ])),
172
- } : {};
173
- return { name: spec.name, description: spec.description, parameters: { ...common, ...parameters } };
174
- });
158
+ const toolsFor = (input: Input): Json[] => input.mode === 'provider' ? [] : [
159
+ { name: 'vtx_get_data', description: 'Request assignment-scoped VTX data.', parameters: {
160
+ // Keep the complete branch contract, while exposing its common fields to
161
+ // Chat tool renderers that enumerate only top-level properties.
162
+ type: 'object', additionalProperties: false, required: ['capability', 'arguments'],
163
+ properties: { capability: { type: 'string', enum: input.dataContract.map((entry) => entry.id) }, arguments: { type: 'object' } },
164
+ oneOf: input.dataContract.map((entry) => ({ type: 'object', additionalProperties: false,
165
+ required: ['capability', 'arguments'], properties: { capability: { const: entry.id }, arguments: entry.input_schema } })) } },
166
+ { name: 'vtx_submit_decision', description: 'Submit a canonical VTX decision.', parameters: {
167
+ type: 'object', additionalProperties: false, required: ['candidate'], properties: { candidate: input.decisionSchema } } },
168
+ { name: 'vtx_decision_status', description: 'Resolve a VTX decision operation.', parameters: {
169
+ type: 'object', additionalProperties: false, required: ['operation_id'], properties: { operation_id: { type: 'string', minLength: 1 } } } },
170
+ ];
175
171
 
176
172
  export default {
177
173
  tools: [],
@@ -16,7 +16,7 @@ import { fileURLToPath } from "node:url";
16
16
  // agent-cli-release.json
17
17
  var agent_cli_release_default = {
18
18
  package_name: "@vtxmacro/cli",
19
- package_version: "2026.9.61",
19
+ package_version: "2026.9.62",
20
20
  codex_package_name: "@openai/codex",
21
21
  codex_version: "0.155.0",
22
22
  copilot_sdk_package_name: "@github/copilot-sdk",
@@ -347,6 +347,12 @@ import { constants } from "node:fs";
347
347
  import { lstat, mkdir, open, readFile, realpath, rename, rm } from "node:fs/promises";
348
348
  import { homedir } from "node:os";
349
349
  import { dirname, isAbsolute, join, relative, resolve, win32 as windowsPath } from "node:path";
350
+ var InferenceHostProcessLockBusyError = class extends Error {
351
+ constructor() {
352
+ super("Another inference host process already owns this local host.");
353
+ this.name = "InferenceHostProcessLockBusyError";
354
+ }
355
+ };
350
356
  var MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
351
357
  var WINDOWS_PRIVATE_ACL_BROKER_SCRIPT = `$ErrorActionPreference='Stop'
352
358
  $ProgressPreference='SilentlyContinue'
@@ -1059,7 +1065,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
1059
1065
  } catch (error) {
1060
1066
  if (error.code !== "EEXIST") throw error;
1061
1067
  if (acquisition > 0) {
1062
- throw new Error("Another inference host process already owns this local host.");
1068
+ throw new InferenceHostProcessLockBusyError();
1063
1069
  }
1064
1070
  let before;
1065
1071
  try {
@@ -1104,14 +1110,14 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
1104
1110
  } catch (error2) {
1105
1111
  if (error2.code === "ENOENT") continue;
1106
1112
  if (Date.now() - before.mtimeMs < 1e4) {
1107
- throw new Error("Another inference host process already owns this local host.");
1113
+ throw new InferenceHostProcessLockBusyError();
1108
1114
  }
1109
1115
  await reclaimPublicationResidue();
1110
1116
  continue;
1111
1117
  }
1112
1118
  if (staleOwner.schema_version !== 2 || !Number.isSafeInteger(staleOwner.pid) || Number(staleOwner.pid) < 1 || typeof staleOwner.owner_token !== "string" || !staleOwner.owner_token || typeof staleOwner.process_identity !== "string" || !staleOwner.process_identity || typeof staleOwner.boot_identity !== "string" || !staleOwner.boot_identity) {
1113
1119
  if (Date.now() - before.mtimeMs < 1e4) {
1114
- throw new Error("Another inference host process already owns this local host.");
1120
+ throw new InferenceHostProcessLockBusyError();
1115
1121
  }
1116
1122
  await reclaimPublicationResidue();
1117
1123
  continue;
@@ -1133,7 +1139,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
1133
1139
  observedAtMs: Date.now()
1134
1140
  });
1135
1141
  if (inferenceProcessIdentitiesMatch(staleOwner.process_identity, observedIdentity)) {
1136
- throw new Error("Another inference host process already owns this local host.");
1142
+ throw new InferenceHostProcessLockBusyError();
1137
1143
  }
1138
1144
  }
1139
1145
  let current;
@@ -1259,7 +1265,7 @@ var parseManifest = (value) => {
1259
1265
  throw new Error("Inference-host bootstrap manifest is invalid.");
1260
1266
  }
1261
1267
  const record = value;
1262
- if (!["vtx_inference_service_v1", "vtx_inference_service_v2", "vtx_inference_service_v3", "vtx_inference_service_v4"].includes(String(record.schema_version))) {
1268
+ if (!["vtx_inference_service_v1", "vtx_inference_service_v2", "vtx_inference_service_v3"].includes(String(record.schema_version))) {
1263
1269
  throw new Error("Inference-host bootstrap manifest version is unsupported.");
1264
1270
  }
1265
1271
  return {
@@ -232,6 +232,12 @@ import { constants } from "node:fs";
232
232
  import { lstat, mkdir, open, readFile, realpath, rename, rm } from "node:fs/promises";
233
233
  import { homedir } from "node:os";
234
234
  import { dirname, isAbsolute, join, relative, resolve, win32 as windowsPath } from "node:path";
235
+ var InferenceHostProcessLockBusyError = class extends Error {
236
+ constructor() {
237
+ super("Another inference host process already owns this local host.");
238
+ this.name = "InferenceHostProcessLockBusyError";
239
+ }
240
+ };
235
241
  var MAX_INFERENCE_PRIVATE_FILE_BYTES = 16 * 1024 * 1024;
236
242
  var WINDOWS_PRIVATE_ACL_BROKER_SCRIPT = `$ErrorActionPreference='Stop'
237
243
  $ProgressPreference='SilentlyContinue'
@@ -944,7 +950,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
944
950
  } catch (error) {
945
951
  if (error.code !== "EEXIST") throw error;
946
952
  if (acquisition > 0) {
947
- throw new Error("Another inference host process already owns this local host.");
953
+ throw new InferenceHostProcessLockBusyError();
948
954
  }
949
955
  let before;
950
956
  try {
@@ -989,14 +995,14 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
989
995
  } catch (error2) {
990
996
  if (error2.code === "ENOENT") continue;
991
997
  if (Date.now() - before.mtimeMs < 1e4) {
992
- throw new Error("Another inference host process already owns this local host.");
998
+ throw new InferenceHostProcessLockBusyError();
993
999
  }
994
1000
  await reclaimPublicationResidue();
995
1001
  continue;
996
1002
  }
997
1003
  if (staleOwner.schema_version !== 2 || !Number.isSafeInteger(staleOwner.pid) || Number(staleOwner.pid) < 1 || typeof staleOwner.owner_token !== "string" || !staleOwner.owner_token || typeof staleOwner.process_identity !== "string" || !staleOwner.process_identity || typeof staleOwner.boot_identity !== "string" || !staleOwner.boot_identity) {
998
1004
  if (Date.now() - before.mtimeMs < 1e4) {
999
- throw new Error("Another inference host process already owns this local host.");
1005
+ throw new InferenceHostProcessLockBusyError();
1000
1006
  }
1001
1007
  await reclaimPublicationResidue();
1002
1008
  continue;
@@ -1018,7 +1024,7 @@ async function acquireInferenceHostProcessLock(path, dependencies = {}) {
1018
1024
  observedAtMs: Date.now()
1019
1025
  });
1020
1026
  if (inferenceProcessIdentitiesMatch(staleOwner.process_identity, observedIdentity)) {
1021
- throw new Error("Another inference host process already owns this local host.");
1027
+ throw new InferenceHostProcessLockBusyError();
1022
1028
  }
1023
1029
  }
1024
1030
  let current;