@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 +19 -71
- package/bin/exo-harness.ts +13 -17
- package/bin/vtx-service-bootstrap.js +12 -6
- package/bin/vtx-service-launcher.js +10 -4
- package/bin/vtx.js +16060 -17163
- package/package.json +2 -3
- package/bin/vtx-managed-codex.js +0 -3125
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.
|
|
101
|
-
|
|
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
|
|
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;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
`
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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.
|
package/bin/exo-harness.ts
CHANGED
|
@@ -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' ? [] :
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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"
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1027
|
+
throw new InferenceHostProcessLockBusyError();
|
|
1022
1028
|
}
|
|
1023
1029
|
}
|
|
1024
1030
|
let current;
|