@vtxmacro/cli 2026.8.40 → 2026.8.42

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.
Files changed (3) hide show
  1. package/README.md +29 -13
  2. package/bin/vtx.js +572 -71
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -10,9 +10,9 @@ vtx --version
10
10
  ```
11
11
 
12
12
  The host supports Windows x64 and Linux x64, including WSL. The package installs
13
- the exact supported OpenAI Codex runtime for those platforms.
14
- The VTX host verifies that native runtime's version and digest before use; it
15
- does not borrow a Codex binary from PATH or an editor extension.
13
+ the exact supported OpenAI Codex runtime and GitHub Copilot SDK. The VTX host
14
+ verifies the Codex runtime's version and digest before use; it does not borrow a
15
+ Codex binary from PATH or an editor extension.
16
16
  For Windows-login auto-start, install and configure the CLI from native Windows;
17
17
  a CLI installed only inside WSL cannot start the WSL virtual machine at login.
18
18
  An npm update does not replace code already loaded by a running durable service.
@@ -52,7 +52,15 @@ Both paths use a separate least-privilege `insights:inference` OAuth grant.
52
52
  VTX never receives an agent vendor password, OAuth token, or subscription
53
53
  credential.
54
54
 
55
- ### Automated Codex host
55
+ ### Durable subscription hosts
56
+
57
+ Codex remains the reference implementation and its existing App Server,
58
+ authentication, quota, recovery, and provider-mode path are unchanged. GitHub
59
+ Copilot is an additive preview adapter that uses the official SDK and validates
60
+ final text against VTX's immutable schema. An uncertain interrupted Copilot call
61
+ is quarantined as `outcome_unknown` and is never blindly repeated. A different
62
+ adapter cannot replace an installed Codex instance; it needs a distinct instance
63
+ and VTX grant.
56
64
 
57
65
  Before the first Codex login, enable **Device code authorization for Codex** in
58
66
  ChatGPT Security settings. Only enter a device code from a login you initiated,
@@ -80,6 +88,14 @@ vtx inference-host codex-logout
80
88
  vtx inference-host revoke
81
89
  ```
82
90
 
91
+ For Copilot, sign in with the official Copilot CLI, then run:
92
+
93
+ ```bash
94
+ vtx inference-host login --instance copilot-1
95
+ vtx inference-host doctor --adapter copilot --instance copilot-1 --json
96
+ vtx inference-host service install --adapter copilot --instance copilot-1
97
+ ```
98
+
83
99
  To connect another Codex subscription to the same VTX account, repeat the
84
100
  login and service-install commands with a stable local name such as
85
101
  `--instance codex-2`. Each named instance has an isolated VTX grant, Codex
@@ -121,10 +137,10 @@ capacity beyond the authenticated account's current entitlement. Updating from
121
137
  an older bounded host removes its previous numeric limit; run
122
138
  `service install --max-concurrency <positive-integer>` only to add one back.
123
139
 
124
- This durable host uses a separate least-privilege `insights:inference`
125
- OAuth grant. It also keeps its ChatGPT subscription login in a dedicated private
126
- Codex home. Neither credential is copied from or widens the ordinary CLI or Codex
127
- session. The per-user OS service starts immediately, starts again at login, and
140
+ Every durable host uses a separate least-privilege `insights:inference`
141
+ OAuth grant. Codex additionally keeps its ChatGPT subscription login in a dedicated
142
+ private Codex home; Copilot uses its official cached user login.
143
+ No vendor credential is written to the VTX service manifest. The per-user OS service starts immediately, starts again at login, and
128
144
  reconnects after sleep, network loss, or a worker exit. Stopping a VTX bot leaves
129
145
  the provider online and idle. Use `vtx inference-host run` only for foreground
130
146
  diagnostics. VTX logout/revoke and Codex logout are intentionally separate operations.
@@ -184,11 +200,11 @@ Use `not_dispatched` only when no harness inference began,
184
200
  `outcome_unknown` when dispatch itself cannot be confirmed. Pipe exactly one
185
201
  object to `vtx inference-host agent-fail --json`.
186
202
 
187
- Claude Code, GitHub Copilot, Cursor, and Google Antigravity remain on the
188
- explicit foreground agent loop. Their supported CLI or account contracts do not
189
- provide the durable, private third-party service boundary VTX requires, so
190
- `service install --adapter ...` rejects them instead of advertising false
191
- durability.
203
+ Claude Code, Kiro, Grok Build, Cursor, Amp, Auggie, Junie, and Warp/Oz remain on
204
+ the explicit foreground agent loop. Their current subscription routing,
205
+ effective-model identity, schema, usage, or crash-recovery contracts do not yet
206
+ meet every durable VTX acceptance fence. The public Insights matrix records the
207
+ specific limitation instead of treating headless support alone as durability.
192
208
 
193
209
  Keep `agent-run` open in a separate terminal and keep calling `agent-next`
194
210
  while the host should remain available. The independent keeper preserves
package/bin/vtx.js CHANGED
@@ -38,9 +38,13 @@ var init_agent_cli_release = __esm({
38
38
  "agent-cli-release.json"() {
39
39
  agent_cli_release_default = {
40
40
  package_name: "@vtxmacro/cli",
41
- package_version: "2026.8.40",
41
+ package_version: "2026.8.42",
42
42
  codex_package_name: "@openai/codex",
43
43
  codex_version: "0.147.0",
44
+ copilot_sdk_package_name: "@github/copilot-sdk",
45
+ copilot_sdk_version: "1.0.0-beta.8",
46
+ copilot_cli_package_name: "@github/copilot",
47
+ copilot_cli_version: "1.0.80",
44
48
  platforms: {
45
49
  "linux-x64": {
46
50
  package_name: "@openai/codex-linux-x64",
@@ -14725,6 +14729,7 @@ var init_external_inference_contract = __esm({
14725
14729
  key_generation: generationSchema,
14726
14730
  adapter: externalInferenceAdapterIdSchema,
14727
14731
  display_name: displayNameSchema,
14732
+ authenticated_account_identity: safeCodeSchema.nullable().optional(),
14728
14733
  authenticated_account_email: authenticatedAccountEmailSchema.nullable().optional(),
14729
14734
  authenticated_account_plan: safeCodeSchema.nullable().optional(),
14730
14735
  protocol_version: protocolVersionSchema,
@@ -15644,6 +15649,7 @@ var init_external_inference_contract = __esm({
15644
15649
  hostStatusReadSchema = external_exports.strictObject({
15645
15650
  host_id: identifierSchema,
15646
15651
  display_name: displayNameSchema,
15652
+ authenticated_account_identity: safeCodeSchema.nullable().optional(),
15647
15653
  authenticated_account_email: authenticatedAccountEmailSchema.nullable().optional(),
15648
15654
  authenticated_account_plan: safeCodeSchema.nullable().optional(),
15649
15655
  adapter: externalInferenceAdapterIdSchema,
@@ -18179,7 +18185,7 @@ var init_mcp_client = __esm({
18179
18185
  if (!host) return invalidBoundResult(name);
18180
18186
  if (host.host_id !== request.host_id || host.host_generation !== request.host_generation || host.advertisement_generation !== request.advertisement_generation || host.key_generation !== request.key_generation || request.adapter !== void 0 && host.adapter !== request.adapter) invalidBoundResult(name);
18181
18187
  if (name !== "inference.host.heartbeat") {
18182
- if (host.display_name !== request.display_name || host.authenticated_account_email !== void 0 && host.authenticated_account_email !== null && host.authenticated_account_email !== request.authenticated_account_email || (host.authenticated_account_plan ?? null) !== (request.authenticated_account_plan ?? null) || !exactJson(host.models, request.models) || host.status === "revoked") invalidBoundResult(name);
18188
+ if (host.display_name !== request.display_name || host.authenticated_account_identity !== void 0 && host.authenticated_account_identity !== null && host.authenticated_account_identity !== request.authenticated_account_identity || host.authenticated_account_email !== void 0 && host.authenticated_account_email !== null && host.authenticated_account_email !== request.authenticated_account_email || (host.authenticated_account_plan ?? null) !== (request.authenticated_account_plan ?? null) || !exactJson(host.models, request.models) || host.status === "revoked") invalidBoundResult(name);
18183
18189
  return;
18184
18190
  }
18185
18191
  const expectedStatus = request.status === "healthy" ? "online" : request.status;
@@ -19478,8 +19484,8 @@ async function preflightCodexSubscription(options) {
19478
19484
  });
19479
19485
  }
19480
19486
  const rateLimits = await session.readAccountRateLimits(options.deadlineAtMs, options.signal);
19481
- const modelCapabilities = await session.readModelCapabilities(options.deadlineAtMs, options.signal);
19482
- if (modelCapabilities.length === 0) {
19487
+ const modelCapabilities2 = await session.readModelCapabilities(options.deadlineAtMs, options.signal);
19488
+ if (modelCapabilities2.length === 0) {
19483
19489
  throw new CodexAppServerError({
19484
19490
  message: "Codex did not return any visible models with supported reasoning efforts.",
19485
19491
  category: "model",
@@ -19491,7 +19497,7 @@ async function preflightCodexSubscription(options) {
19491
19497
  authenticated_account_email: account.authenticatedChatGptAccount.email,
19492
19498
  authenticated_account_plan: account.authenticatedChatGptAccount.planType,
19493
19499
  rate_limits: rateLimits,
19494
- model_capabilities: modelCapabilities
19500
+ model_capabilities: modelCapabilities2
19495
19501
  };
19496
19502
  } finally {
19497
19503
  await session.close();
@@ -22780,6 +22786,317 @@ $items = @(Get-CimInstance Win32_Process | Where-Object { $_.Name -in @('node.ex
22780
22786
  }
22781
22787
  });
22782
22788
 
22789
+ // lib/inference-host/copilot-adapter.ts
22790
+ import { mkdtemp as mkdtemp2, rm as rm4 } from "node:fs/promises";
22791
+ import { tmpdir as tmpdir3 } from "node:os";
22792
+ import { join as join5 } from "node:path";
22793
+ import { CopilotClient } from "@github/copilot-sdk";
22794
+ var COPILOT_SDK_VERSION, MAX_RESULT_BYTES, createPrivateWorkspace, cleanCopilotEnvironment, defaultClient, modelCapabilities, requiredUsageInteger, optionalUsageInteger, CopilotSubscriptionAdapter;
22795
+ var init_copilot_adapter = __esm({
22796
+ "lib/inference-host/copilot-adapter.ts"() {
22797
+ "use strict";
22798
+ init_codex_app_server();
22799
+ COPILOT_SDK_VERSION = "1.0.0-beta.8";
22800
+ MAX_RESULT_BYTES = 3e5;
22801
+ createPrivateWorkspace = async () => {
22802
+ const path = await mkdtemp2(join5(tmpdir3(), "vtx-copilot-"));
22803
+ return {
22804
+ path,
22805
+ cleanup: async () => {
22806
+ await rm4(path, { recursive: true, force: true });
22807
+ }
22808
+ };
22809
+ };
22810
+ cleanCopilotEnvironment = () => {
22811
+ const environment = { ...process.env };
22812
+ for (const name of [
22813
+ "COPILOT_GITHUB_TOKEN",
22814
+ "GH_TOKEN",
22815
+ "GITHUB_TOKEN",
22816
+ "COPILOT_PROVIDER_API_KEY",
22817
+ "COPILOT_PROVIDER_BASE_URL",
22818
+ "COPILOT_PROVIDER_TYPE",
22819
+ "COPILOT_CLI_PATH",
22820
+ "COPILOT_SDK_AUTH_TOKEN",
22821
+ "COPILOT_CONNECTION_TOKEN",
22822
+ "COPILOT_HOME"
22823
+ ]) delete environment[name];
22824
+ return environment;
22825
+ };
22826
+ defaultClient = (workingDirectory) => new CopilotClient({
22827
+ workingDirectory,
22828
+ env: cleanCopilotEnvironment(),
22829
+ useLoggedInUser: true,
22830
+ logLevel: "error"
22831
+ });
22832
+ modelCapabilities = (models) => {
22833
+ const capabilities = models.filter((model) => model.policy?.state !== "disabled" && model.policy?.state !== "unconfigured").flatMap((model) => {
22834
+ const efforts = model.capabilities.supports.reasoningEffort ? [...model.supportedReasoningEfforts ?? []] : ["none"];
22835
+ if (efforts.length === 0) return [];
22836
+ const defaultEffort = model.defaultReasoningEffort && efforts.includes(model.defaultReasoningEffort) ? model.defaultReasoningEffort : efforts[0];
22837
+ return [{
22838
+ id: model.id,
22839
+ model: model.id,
22840
+ displayName: model.name,
22841
+ hidden: false,
22842
+ supportedReasoningEfforts: efforts,
22843
+ defaultReasoningEffort: defaultEffort,
22844
+ isDefault: false
22845
+ }];
22846
+ });
22847
+ return capabilities.map((capability, index) => ({
22848
+ ...capability,
22849
+ isDefault: index === 0
22850
+ }));
22851
+ };
22852
+ requiredUsageInteger = (value, field) => {
22853
+ if (!Number.isSafeInteger(value) || Number(value) < 0) {
22854
+ throw new Error(`copilot_usage_${field}_missing`);
22855
+ }
22856
+ return Number(value);
22857
+ };
22858
+ optionalUsageInteger = (value, field) => {
22859
+ if (value === void 0) return 0;
22860
+ if (!Number.isSafeInteger(value) || Number(value) < 0) {
22861
+ throw new Error(`copilot_usage_${field}_invalid`);
22862
+ }
22863
+ return Number(value);
22864
+ };
22865
+ CopilotSubscriptionAdapter = class {
22866
+ constructor(dependencies = {}) {
22867
+ this.inFlight = /* @__PURE__ */ new Map();
22868
+ this.dependencies = dependencies;
22869
+ }
22870
+ async preflight(signal) {
22871
+ const workspace = await (this.dependencies.createWorkspace ?? createPrivateWorkspace)();
22872
+ const client = (this.dependencies.createClient ?? defaultClient)(workspace.path);
22873
+ try {
22874
+ signal?.throwIfAborted();
22875
+ await client.start();
22876
+ const [status, auth, models] = await Promise.all([
22877
+ client.getStatus(),
22878
+ client.getAuthStatus(),
22879
+ client.listModels()
22880
+ ]);
22881
+ if (!auth.isAuthenticated || auth.authType !== "user" && auth.authType !== "gh-cli") {
22882
+ throw new CodexAppServerError({
22883
+ message: "GitHub Copilot must use a logged-in Copilot subscription, not BYOK.",
22884
+ category: "auth",
22885
+ code: "copilot_subscription_auth_required",
22886
+ retryable: false
22887
+ });
22888
+ }
22889
+ const accountIdentity = auth.login?.trim().toLowerCase() ?? "";
22890
+ if (!/^[a-z0-9][a-z0-9._-]{0,95}$/u.test(accountIdentity)) {
22891
+ throw new CodexAppServerError({
22892
+ message: "GitHub Copilot did not return a stable authenticated login identity.",
22893
+ category: "auth",
22894
+ code: "copilot_account_identity_required",
22895
+ retryable: false
22896
+ });
22897
+ }
22898
+ const capabilities = modelCapabilities(models);
22899
+ if (capabilities.length === 0) {
22900
+ throw new CodexAppServerError({
22901
+ message: "GitHub Copilot did not return an enabled model catalog.",
22902
+ category: "model",
22903
+ code: "copilot_model_catalog_empty",
22904
+ retryable: false
22905
+ });
22906
+ }
22907
+ return {
22908
+ adapterId: "copilot",
22909
+ runtimeVersion: `${COPILOT_SDK_VERSION}+cli.${status.version}.rpc${status.protocolVersion}`,
22910
+ authenticatedAccountIdentity: accountIdentity,
22911
+ authenticatedAccountEmail: null,
22912
+ authenticatedAccountPlan: null,
22913
+ modelCapabilities: capabilities,
22914
+ rateLimits: null,
22915
+ sameAttemptRecovery: false
22916
+ };
22917
+ } finally {
22918
+ await client.stop().catch(async () => {
22919
+ await client.forceStop();
22920
+ });
22921
+ await workspace.cleanup();
22922
+ }
22923
+ }
22924
+ runAttempt(input) {
22925
+ const active = this.inFlight.get(input.attemptId);
22926
+ if (active) return active;
22927
+ const promise2 = this.executeAttempt(input).finally(() => {
22928
+ if (this.inFlight.get(input.attemptId) === promise2) this.inFlight.delete(input.attemptId);
22929
+ });
22930
+ this.inFlight.set(input.attemptId, promise2);
22931
+ return promise2;
22932
+ }
22933
+ async executeAttempt(input) {
22934
+ const startedAt = (this.dependencies.now ?? Date.now)();
22935
+ const workspace = await (this.dependencies.createWorkspace ?? createPrivateWorkspace)();
22936
+ const client = (this.dependencies.createClient ?? defaultClient)(workspace.path);
22937
+ let session = null;
22938
+ let dispatchEntered = false;
22939
+ let providerReceiptObserved = false;
22940
+ try {
22941
+ input.signal?.throwIfAborted();
22942
+ if (Date.now() >= input.deadlineAtMs) throw new Error("deadline_exceeded");
22943
+ await client.start();
22944
+ session = await client.createSession({
22945
+ clientName: "@vtxmacro/cli durable Copilot inference host",
22946
+ model: input.requestedModel,
22947
+ ...input.requestedReasoningEffort === "none" ? {} : { reasoningEffort: input.requestedReasoningEffort },
22948
+ systemMessage: {
22949
+ mode: "replace",
22950
+ content: `${input.systemPrompt}
22951
+
22952
+ Return only one JSON value matching this JSON Schema exactly:
22953
+ ${input.outputSchemaJson}`
22954
+ },
22955
+ tools: [],
22956
+ availableTools: [],
22957
+ enableConfigDiscovery: false,
22958
+ enableSessionTelemetry: true,
22959
+ workingDirectory: workspace.path,
22960
+ streaming: true
22961
+ });
22962
+ const usageEvents = [];
22963
+ session.on((event) => {
22964
+ if (event.type === "assistant.usage" && !event.agentId) {
22965
+ providerReceiptObserved = true;
22966
+ usageEvents.push(event);
22967
+ }
22968
+ });
22969
+ const onAbort = () => {
22970
+ void session?.abort().catch(() => void 0);
22971
+ };
22972
+ input.signal?.addEventListener("abort", onAbort, { once: true });
22973
+ dispatchEntered = true;
22974
+ let response;
22975
+ try {
22976
+ response = await session.sendAndWait(
22977
+ { prompt: input.userPrompt },
22978
+ Math.max(1, input.deadlineAtMs - Date.now())
22979
+ );
22980
+ providerReceiptObserved = true;
22981
+ } finally {
22982
+ input.signal?.removeEventListener("abort", onAbort);
22983
+ }
22984
+ if (!response?.data.content || Buffer.byteLength(response.data.content, "utf8") > MAX_RESULT_BYTES) {
22985
+ throw new Error("copilot_invalid_result");
22986
+ }
22987
+ const observedModels = /* @__PURE__ */ new Set();
22988
+ const observedEfforts = /* @__PURE__ */ new Set();
22989
+ let inputTokens = 0;
22990
+ let cachedInputTokens = 0;
22991
+ let outputTokens = 0;
22992
+ let reasoningOutputTokens = 0;
22993
+ let cacheWriteInputTokens = 0;
22994
+ let cacheWriteSupported = false;
22995
+ let timeToFirstTokenMs = null;
22996
+ let providerCallId = null;
22997
+ for (const event of usageEvents) {
22998
+ if (event.type !== "assistant.usage") continue;
22999
+ observedModels.add(event.data.model);
23000
+ if (event.data.reasoningEffort) observedEfforts.add(event.data.reasoningEffort);
23001
+ inputTokens += requiredUsageInteger(event.data.inputTokens, "input_tokens");
23002
+ cachedInputTokens += optionalUsageInteger(event.data.cacheReadTokens, "cache_read_tokens");
23003
+ outputTokens += requiredUsageInteger(event.data.outputTokens, "output_tokens");
23004
+ reasoningOutputTokens += optionalUsageInteger(event.data.reasoningTokens, "reasoning_tokens");
23005
+ if (event.data.cacheWriteTokens !== void 0) {
23006
+ cacheWriteSupported = true;
23007
+ cacheWriteInputTokens += optionalUsageInteger(
23008
+ event.data.cacheWriteTokens,
23009
+ "cache_write_tokens"
23010
+ );
23011
+ }
23012
+ if (timeToFirstTokenMs === null && Number.isSafeInteger(event.data.timeToFirstTokenMs)) {
23013
+ timeToFirstTokenMs = Number(event.data.timeToFirstTokenMs);
23014
+ }
23015
+ providerCallId = event.data.providerCallId ?? event.data.apiCallId ?? providerCallId;
23016
+ }
23017
+ if (usageEvents.length === 0) {
23018
+ throw new Error("copilot_usage_receipt_missing");
23019
+ }
23020
+ if (response.data.model) observedModels.add(response.data.model);
23021
+ if (observedModels.size !== 1 || !observedModels.has(input.requestedModel)) {
23022
+ throw new Error("copilot_effective_model_mismatch");
23023
+ }
23024
+ const effectiveEffort = input.requestedReasoningEffort === "none" ? observedEfforts.size === 0 ? "none" : observedEfforts.size === 1 ? [...observedEfforts][0] : null : observedEfforts.size === 1 ? [...observedEfforts][0] : null;
23025
+ if (effectiveEffort !== input.requestedReasoningEffort) {
23026
+ throw new Error("copilot_effective_effort_mismatch");
23027
+ }
23028
+ const usage = {
23029
+ inputTokens,
23030
+ cachedInputTokens: Math.min(cachedInputTokens, inputTokens),
23031
+ outputTokens,
23032
+ reasoningOutputTokens: Math.min(reasoningOutputTokens, outputTokens),
23033
+ totalTokens: inputTokens + outputTokens,
23034
+ cacheWriteInputTokens: cacheWriteSupported ? cacheWriteInputTokens : null,
23035
+ cacheWriteSupported
23036
+ };
23037
+ return {
23038
+ text: response.data.content,
23039
+ reasoningContent: response.data.reasoningText ?? null,
23040
+ reasoningSummary: null,
23041
+ requestedModel: input.requestedModel,
23042
+ effectiveModel: input.requestedModel,
23043
+ requestedReasoningEffort: input.requestedReasoningEffort,
23044
+ effectiveReasoningEffort: effectiveEffort,
23045
+ adapterRequestId: providerCallId ?? response.data.requestId ?? response.data.messageId,
23046
+ adapterResponseId: response.data.serviceRequestId ?? response.data.messageId,
23047
+ usage,
23048
+ latencyMs: Math.max(0, (this.dependencies.now ?? Date.now)() - startedAt),
23049
+ timeToFirstTokenMs,
23050
+ terminalStatus: "completed"
23051
+ };
23052
+ } catch (error48) {
23053
+ if (error48 instanceof CodexAppServerError) throw error48;
23054
+ const cancelled = input.signal?.aborted === true;
23055
+ const deadline = Date.now() >= input.deadlineAtMs;
23056
+ if (dispatchEntered && session) {
23057
+ await session.abort().catch(() => void 0);
23058
+ }
23059
+ throw new CodexAppServerError({
23060
+ message: cancelled ? "GitHub Copilot attempt was cancelled." : deadline ? "GitHub Copilot attempt exceeded its immutable deadline." : error48 instanceof Error ? error48.message : "GitHub Copilot adapter failed.",
23061
+ category: cancelled ? "cancelled" : deadline ? "timeout" : "adapter",
23062
+ code: cancelled ? "cancelled" : deadline ? "deadline_exceeded" : "copilot_adapter_failure",
23063
+ retryable: false,
23064
+ dispatchOutcome: providerReceiptObserved ? "confirmed_dispatched" : dispatchEntered ? "outcome_unknown" : "not_dispatched",
23065
+ cause: error48
23066
+ });
23067
+ } finally {
23068
+ if (session) {
23069
+ const sessionId = session.sessionId;
23070
+ await session.disconnect().catch(() => void 0);
23071
+ await client.deleteSession(sessionId).catch(() => void 0);
23072
+ }
23073
+ await client.stop().catch(async () => {
23074
+ await client.forceStop();
23075
+ });
23076
+ await workspace.cleanup();
23077
+ }
23078
+ }
23079
+ };
23080
+ }
23081
+ });
23082
+
23083
+ // lib/inference-host/durable-adapter.ts
23084
+ var DURABLE_INFERENCE_ADAPTER_IDS, isDurableInferenceAdapterId, durableAdapterDisplayName;
23085
+ var init_durable_adapter = __esm({
23086
+ "lib/inference-host/durable-adapter.ts"() {
23087
+ "use strict";
23088
+ DURABLE_INFERENCE_ADAPTER_IDS = [
23089
+ "codex",
23090
+ "copilot"
23091
+ ];
23092
+ isDurableInferenceAdapterId = (value) => DURABLE_INFERENCE_ADAPTER_IDS.includes(value);
23093
+ durableAdapterDisplayName = (adapter) => ({
23094
+ codex: "Codex",
23095
+ copilot: "GitHub Copilot"
23096
+ })[adapter];
23097
+ }
23098
+ });
23099
+
22783
23100
  // lib/inference-host/crypto.ts
22784
23101
  import {
22785
23102
  createCipheriv,
@@ -29371,7 +29688,7 @@ function createDefaultInferenceHostRunnerDependencies(options) {
29371
29688
  envelopePublicKey: options.envelopePublicKey
29372
29689
  };
29373
29690
  }
29374
- var import_ajv, RUNTIME_RECEIPT_SCHEMA_VERSION, ATTEMPT_RECEIPT_SCHEMA_VERSION, DEFAULT_ADVERTISEMENT_TTL_MS, DEFAULT_ADVERTISEMENT_REFRESH_LEAD_MS, DEFAULT_HOST_HEARTBEAT_MS, DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS, DEFAULT_ATTEMPT_HEARTBEAT_MS, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_REMOTE_RETRY_LIMIT, MIN_SLEEP_MS, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, UNBOUNDED_AVAILABLE_SLOTS, buildCodexInferenceAdvertisedModels, summarizeInferenceHostRuntimeRecovery, FileInferenceHostRuntimeReceiptStore, InferenceHostRecoveryRequiredError, InferenceHostRunnerError, defaultSleep, abortError, defaultRegisterSignalHandlers, safeInteger, exactObjectKeys, validIso, validateAttemptReceipt, validateRuntimeReceipt, sha256, stableOperationId, buildAttemptStartRequest, isoAt, providerWeeklyQuotaFromRateLimits, finitePositiveOption, validateRunnerOptions, assertLocalCredentialIdentity, retryableRemoteError, remoteRetryAfterMs, controlPlaneFatal, defaultValidateOutput, objectRecord, positiveUtf8ByteLimit, assertCompilableOutputSchema, parseOutputContract, membershipFailureDisposition, safeFailureCode, exactJson2, runnerIdentityMismatch, assertAdvertisementResult, assertHostHeartbeatResult, assertClaimResult, assertStartResult, assertJobHeartbeatResult, assertTerminalResult, classifyFailure, ambiguousHeartbeatTransport, reportedTokenUsage, reportedUsage, InferenceHostRunner;
29691
+ var import_ajv, RUNTIME_RECEIPT_SCHEMA_VERSION, ATTEMPT_RECEIPT_SCHEMA_VERSION, DEFAULT_ADVERTISEMENT_TTL_MS, DEFAULT_ADVERTISEMENT_REFRESH_LEAD_MS, DEFAULT_HOST_HEARTBEAT_MS, DEFAULT_PROVIDER_RATE_LIMIT_REFRESH_MS, DEFAULT_ATTEMPT_HEARTBEAT_MS, DEFAULT_DRAIN_TIMEOUT_MS, DEFAULT_REMOTE_RETRY_LIMIT, MIN_SLEEP_MS, DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS, MIN_CLAIM_START_WINDOW_MS, UNBOUNDED_AVAILABLE_SLOTS, buildInferenceAdvertisedModels, summarizeInferenceHostRuntimeRecovery, FileInferenceHostRuntimeReceiptStore, InferenceHostRecoveryRequiredError, InferenceHostRunnerError, defaultSleep, abortError, defaultRegisterSignalHandlers, safeInteger, exactObjectKeys, validIso, validateAttemptReceipt, validateRuntimeReceipt, sha256, stableOperationId, buildAttemptStartRequest, isoAt, providerWeeklyQuotaFromRateLimits, finitePositiveOption, validateRunnerOptions, assertLocalCredentialIdentity, retryableRemoteError, remoteRetryAfterMs, controlPlaneFatal, defaultValidateOutput, objectRecord, positiveUtf8ByteLimit, assertCompilableOutputSchema, parseOutputContract, membershipFailureDisposition, safeFailureCode, exactJson2, runnerIdentityMismatch, assertAdvertisementResult, assertHostHeartbeatResult, assertClaimResult, assertStartResult, assertJobHeartbeatResult, assertTerminalResult, classifyFailure, ambiguousHeartbeatTransport, reportedTokenUsage, reportedUsage, InferenceHostRunner;
29375
29692
  var init_runner = __esm({
29376
29693
  "lib/inference-host/runner.ts"() {
29377
29694
  "use strict";
@@ -29396,18 +29713,18 @@ var init_runner = __esm({
29396
29713
  DEFAULT_CODEX_ACCOUNT_COOLDOWN_MS = 5 * 60 * 1e3;
29397
29714
  MIN_CLAIM_START_WINDOW_MS = 5e3;
29398
29715
  UNBOUNDED_AVAILABLE_SLOTS = Number.MAX_SAFE_INTEGER;
29399
- buildCodexInferenceAdvertisedModels = (capabilities, adapterRuntimeVersion) => {
29716
+ buildInferenceAdvertisedModels = (capabilities, adapterRuntimeVersion, adapter = "codex", structuredOutput = true, sameAttemptRecovery = adapter === "codex") => {
29400
29717
  const visible = capabilities.filter((capability) => !capability.hidden);
29401
29718
  if (visible.length === 0 || visible.length > 64) {
29402
29719
  throw new InferenceHostRunnerError(
29403
29720
  "invalid_configuration",
29404
- visible.length === 0 ? "Codex did not return any visible models that VTX can advertise." : "Codex returned more models than the VTX advertisement contract can represent."
29721
+ visible.length === 0 ? `${adapter} did not return any visible models that VTX can advertise.` : `${adapter} returned more models than the VTX advertisement contract can represent.`
29405
29722
  );
29406
29723
  }
29407
29724
  const models = visible.map((capability) => ({
29408
29725
  model_id: capability.model,
29409
29726
  label: capability.displayName,
29410
- adapter: "codex",
29727
+ adapter,
29411
29728
  supported_lanes: ["main", "review", "screener"],
29412
29729
  supported_reasoning_efforts: [...capability.supportedReasoningEfforts],
29413
29730
  default_reasoning_effort: capability.defaultReasoningEffort,
@@ -29416,14 +29733,14 @@ var init_runner = __esm({
29416
29733
  requested_model: capability.model,
29417
29734
  effective_models: [capability.model]
29418
29735
  }],
29419
- structured_output: true,
29420
- same_attempt_recovery: true,
29736
+ structured_output: structuredOutput,
29737
+ same_attempt_recovery: sameAttemptRecovery,
29421
29738
  adapter_runtime_version: adapterRuntimeVersion
29422
29739
  }));
29423
29740
  if (new Set(models.map((model) => model.model_id)).size !== models.length) {
29424
29741
  throw new InferenceHostRunnerError(
29425
29742
  "invalid_configuration",
29426
- "Codex returned duplicate runtime model identities."
29743
+ `${adapter} returned duplicate runtime model identities.`
29427
29744
  );
29428
29745
  }
29429
29746
  return models;
@@ -29699,6 +30016,10 @@ var init_runner = __esm({
29699
30016
  return result2;
29700
30017
  };
29701
30018
  validateRunnerOptions = (options) => {
30019
+ const adapterId = options.adapterId ?? "codex";
30020
+ if (!/^[a-z0-9][a-z0-9._-]{0,63}$/u.test(adapterId)) {
30021
+ throw new InferenceHostRunnerError("invalid_configuration", "Inference adapter ID is invalid.");
30022
+ }
29702
30023
  if (!options.displayName.trim() || options.displayName.length > 128) {
29703
30024
  throw new InferenceHostRunnerError("invalid_configuration", "Inference host display name is invalid.");
29704
30025
  }
@@ -29722,9 +30043,15 @@ var init_runner = __esm({
29722
30043
  );
29723
30044
  }
29724
30045
  return {
29725
- advertisedModels: buildCodexInferenceAdvertisedModels(
30046
+ adapterId,
30047
+ usageSource: options.usageSource ?? (adapterId === "codex" ? "codex_app_server" : adapterId),
30048
+ sameAttemptRecovery: options.sameAttemptRecovery ?? adapterId === "codex",
30049
+ advertisedModels: buildInferenceAdvertisedModels(
29726
30050
  options.codexModelCapabilities,
29727
- options.adapterRuntimeVersion
30051
+ options.adapterRuntimeVersion,
30052
+ adapterId,
30053
+ options.structuredOutput ?? adapterId === "codex",
30054
+ options.sameAttemptRecovery ?? adapterId === "codex"
29728
30055
  ),
29729
30056
  maxConcurrency: options.maxConcurrency === null || options.maxConcurrency === void 0 ? null : finitePositiveOption(options.maxConcurrency, 1, "Maximum concurrency"),
29730
30057
  advertisementTtlMs,
@@ -29929,12 +30256,12 @@ var init_runner = __esm({
29929
30256
  };
29930
30257
  assertAdvertisementResult = (advertisement, result2) => {
29931
30258
  const host = result2.host;
29932
- if (host.host_id !== advertisement.host_id || host.host_generation !== advertisement.host_generation || host.advertisement_generation !== advertisement.advertisement_generation || host.key_generation !== advertisement.key_generation || host.display_name !== advertisement.display_name || host.authenticated_account_email !== void 0 && host.authenticated_account_email !== null && host.authenticated_account_email !== advertisement.authenticated_account_email || (host.authenticated_account_plan ?? null) !== (advertisement.authenticated_account_plan ?? null) || host.adapter !== advertisement.adapter || host.status === "revoked" || !exactJson2(host.models, advertisement.models)) runnerIdentityMismatch("Host advertisement");
30259
+ if (host.host_id !== advertisement.host_id || host.host_generation !== advertisement.host_generation || host.advertisement_generation !== advertisement.advertisement_generation || host.key_generation !== advertisement.key_generation || host.display_name !== advertisement.display_name || host.authenticated_account_identity !== void 0 && host.authenticated_account_identity !== null && host.authenticated_account_identity !== advertisement.authenticated_account_identity || host.authenticated_account_email !== void 0 && host.authenticated_account_email !== null && host.authenticated_account_email !== advertisement.authenticated_account_email || (host.authenticated_account_plan ?? null) !== (advertisement.authenticated_account_plan ?? null) || host.adapter !== advertisement.adapter || host.status === "revoked" || !exactJson2(host.models, advertisement.models)) runnerIdentityMismatch("Host advertisement");
29933
30260
  };
29934
- assertHostHeartbeatResult = (request, result2, allowOfflineProjection = false) => {
30261
+ assertHostHeartbeatResult = (request, result2, expectedAdapter, allowOfflineProjection = false) => {
29935
30262
  const expectedStatus = request.status === "healthy" ? "online" : request.status;
29936
30263
  const expectedTemporaryOffline = allowOfflineProjection && request.status === "healthy" && result2.host.status === "offline";
29937
- if (result2.host.host_id !== request.host_id || result2.host.host_generation !== request.host_generation || result2.host.advertisement_generation !== request.advertisement_generation || result2.host.key_generation !== request.key_generation || result2.host.adapter !== "codex" || result2.host.status !== expectedStatus && !expectedTemporaryOffline) runnerIdentityMismatch("Host heartbeat");
30264
+ if (result2.host.host_id !== request.host_id || result2.host.host_generation !== request.host_generation || result2.host.advertisement_generation !== request.advertisement_generation || result2.host.key_generation !== request.key_generation || result2.host.adapter !== expectedAdapter || result2.host.status !== expectedStatus && !expectedTemporaryOffline) runnerIdentityMismatch("Host heartbeat");
29938
30265
  };
29939
30266
  assertClaimResult = (request, result2) => {
29940
30267
  if (result2.claim_request_id !== request.claim_request_id) {
@@ -30010,10 +30337,10 @@ var init_runner = __esm({
30010
30337
  "authentication_timeout"
30011
30338
  ].includes(code);
30012
30339
  };
30013
- reportedTokenUsage = (usage) => ({
30340
+ reportedTokenUsage = (usage, source = "codex_app_server") => ({
30014
30341
  schema_version: EXTERNAL_INFERENCE_USAGE_SCHEMA_VERSION,
30015
30342
  availability: "reported",
30016
- source: "codex_app_server",
30343
+ source,
30017
30344
  input_tokens: usage.inputTokens,
30018
30345
  cached_input_tokens: usage.cachedInputTokens,
30019
30346
  output_tokens: usage.outputTokens,
@@ -30022,7 +30349,7 @@ var init_runner = __esm({
30022
30349
  cache_write_input_tokens: usage.cacheWriteInputTokens,
30023
30350
  cache_write_support: usage.cacheWriteSupported ? "reported" : "unsupported"
30024
30351
  });
30025
- reportedUsage = (result2) => reportedTokenUsage(result2.usage);
30352
+ reportedUsage = (result2, source) => reportedTokenUsage(result2.usage, source);
30026
30353
  InferenceHostRunner = class {
30027
30354
  constructor(dependencies, options) {
30028
30355
  this.receiptWriteChain = Promise.resolve();
@@ -30252,8 +30579,9 @@ var init_runner = __esm({
30252
30579
  host_generation: localState.host_generation,
30253
30580
  advertisement_generation: generation,
30254
30581
  key_generation: localState.key_generation,
30255
- adapter: "codex",
30582
+ adapter: settings.adapterId,
30256
30583
  display_name: this.options.displayName.trim(),
30584
+ ...this.options.authenticatedAccountIdentity ? { authenticated_account_identity: this.options.authenticatedAccountIdentity } : {},
30257
30585
  authenticated_account_email: this.options.authenticatedAccountEmail ?? null,
30258
30586
  authenticated_account_plan: this.options.authenticatedAccountPlan ?? null,
30259
30587
  protocol_version: this.options.protocolVersion,
@@ -30362,7 +30690,7 @@ var init_runner = __esm({
30362
30690
  }
30363
30691
  throw error48;
30364
30692
  }
30365
- assertHostHeartbeatResult(request, result2, allowOfflineProjection);
30693
+ assertHostHeartbeatResult(request, result2, settings.adapterId, allowOfflineProjection);
30366
30694
  if (result2.host.status === "revoked" || result2.host.status === "offline" && !allowOfflineProjection) {
30367
30695
  const error48 = new InferenceHostRunnerError(
30368
30696
  result2.host.status === "revoked" ? "host_revoked" : "host_offline",
@@ -30806,6 +31134,9 @@ var init_runner = __esm({
30806
31134
  signal,
30807
31135
  now
30808
31136
  } = options;
31137
+ const adapterId = this.options.adapterId ?? "codex";
31138
+ const usageSource = this.options.usageSource ?? (adapterId === "codex" ? "codex_app_server" : adapterId);
31139
+ const sameAttemptRecovery = this.options.sameAttemptRecovery ?? adapterId === "codex";
30809
31140
  const decryptClaim = this.dependencies.decryptClaim ?? decryptExternalInferenceClaim;
30810
31141
  const sealCandidate = this.dependencies.sealCandidate ?? sealExternalInferenceCandidate;
30811
31142
  const validateOutput = this.dependencies.validateOutput ?? defaultValidateOutput;
@@ -30982,6 +31313,15 @@ var init_runner = __esm({
30982
31313
  phase: "dispatched",
30983
31314
  dispatch_outcome: "outcome_unknown"
30984
31315
  });
31316
+ if (options.resumeReceipt?.phase === "dispatched" && !sameAttemptRecovery) {
31317
+ throw new CodexAppServerError({
31318
+ message: `${adapterId} cannot prove the exact result of the interrupted attempt.`,
31319
+ category: "adapter",
31320
+ code: "same_attempt_recovery_unavailable",
31321
+ retryable: false,
31322
+ dispatchOutcome: "outcome_unknown"
31323
+ });
31324
+ }
30985
31325
  adapterResult = await this.dependencies.codexAdapter.runAttempt({
30986
31326
  attemptId,
30987
31327
  inputSha256: jobInput.input_sha256,
@@ -31007,7 +31347,7 @@ var init_runner = __esm({
31007
31347
  } catch (error48) {
31008
31348
  throw new InferenceHostRunnerError(
31009
31349
  "invalid_structured_output",
31010
- "Codex did not return valid structured JSON.",
31350
+ `${adapterId} did not return valid structured JSON.`,
31011
31351
  { cause: error48 }
31012
31352
  );
31013
31353
  }
@@ -31019,7 +31359,7 @@ var init_runner = __esm({
31019
31359
  if (Buffer.byteLength(adapterResult.text, "utf8") > outputContract.providerResponseTextMaxUtf8Bytes) {
31020
31360
  throw new InferenceHostRunnerError(
31021
31361
  "output_schema_response_text_too_large",
31022
- "Codex response text exceeds the immutable provider-response UTF-8 byte limit."
31362
+ `${adapterId} response text exceeds the immutable provider-response UTF-8 byte limit.`
31023
31363
  );
31024
31364
  }
31025
31365
  if (outputContract.providerReasoningContentSupported && outputContract.providerReasoningContentMaxUtf8Bytes !== null && adapterResult.reasoningContent !== null && Buffer.byteLength(adapterResult.reasoningContent, "utf8") > outputContract.providerReasoningContentMaxUtf8Bytes) {
@@ -31092,7 +31432,7 @@ var init_runner = __esm({
31092
31432
  adapter_request_id: adapterResult.adapterRequestId,
31093
31433
  adapter_response_id: adapterResult.adapterResponseId
31094
31434
  },
31095
- usage: reportedUsage(adapterResult),
31435
+ usage: reportedUsage(adapterResult, usageSource),
31096
31436
  outcome: {
31097
31437
  dispatch_outcome: "confirmed_dispatched",
31098
31438
  response_outcome: "completed"
@@ -31207,7 +31547,7 @@ var init_runner = __esm({
31207
31547
  adapter_request_id: canReportEffective ? adapterResult?.adapterRequestId ?? null : null,
31208
31548
  adapter_response_id: canReportEffective ? adapterResult?.adapterResponseId ?? null : null
31209
31549
  },
31210
- usage: adapterResult ? reportedUsage(adapterResult) : error48 instanceof CodexAppServerError && error48.usage ? reportedTokenUsage(error48.usage) : null,
31550
+ usage: adapterResult ? reportedUsage(adapterResult, usageSource) : error48 instanceof CodexAppServerError && error48.usage ? reportedTokenUsage(error48.usage, usageSource) : null,
31211
31551
  failure_category: failure.category,
31212
31552
  failure_code: failure.code,
31213
31553
  retryable: failure.retryable,
@@ -31274,7 +31614,7 @@ var init_runner = __esm({
31274
31614
  if (result2.requestedModel !== jobInput.requested_model || result2.requestedReasoningEffort !== jobInput.requested_reasoning_effort || !mapping?.effective_models.includes(result2.effectiveModel) || !jobInput.allowed_effective_reasoning_efforts.includes(result2.effectiveReasoningEffort)) {
31275
31615
  throw new InferenceHostRunnerError(
31276
31616
  "effective_model_mismatch",
31277
- "Codex effective model provenance did not match the immutable allowance."
31617
+ "The effective adapter model provenance did not match the immutable allowance."
31278
31618
  );
31279
31619
  }
31280
31620
  }
@@ -31286,13 +31626,14 @@ var init_runner = __esm({
31286
31626
  import { spawn as spawn6 } from "node:child_process";
31287
31627
  import { randomUUID } from "node:crypto";
31288
31628
  import { createWriteStream, readFileSync } from "node:fs";
31289
- import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm4, writeFile as writeFile2 } from "node:fs/promises";
31629
+ import { access as access3, chmod as chmod3, mkdir as mkdir3, readFile as readFile5, rm as rm5, writeFile as writeFile2 } from "node:fs/promises";
31290
31630
  import { homedir as homedir2 } from "node:os";
31291
- import { dirname as dirname4, join as join5, resolve as resolve4 } from "node:path";
31631
+ import { dirname as dirname4, join as join6, resolve as resolve4 } from "node:path";
31292
31632
  var SERVICE_NAME, SYSTEMD_UNIT, LAUNCHD_LABEL, SERVICE_COOPERATIVE_STOP_SECONDS, INFERENCE_HOST_SERVICE_DRAIN_COMMAND, inferenceHostServiceChildEnvironment, isWindowsSubsystemForLinux, inferenceHostServiceManifestPath, inferenceHostServiceDesiredPath, inferenceHostServiceLogPath, inferenceHostServiceRuntimePath, xmlEscape, plistEscape, systemdQuote, defaultRunCommand, managerName, assertRuntimeEnvironment, withoutConcurrencyLimit, assertServicePath, manifestGeneration, assertWorker, assertManifest, assertServiceRuntimeState, assertDesiredState, readInferenceHostServiceManifest, readManifestAcrossAtomicReplacement, readInferenceHostServiceRuntime, readRuntimeAcrossAtomicReplacement, readInferenceHostServiceDesired, readDesiredAcrossAtomicReplacement, writeDesired, runtimeEnvironment, serviceArguments, windowsOwnedCommandLine, vbScriptString, windowsServiceLauncher, windowsTaskXml, systemdUnit, launchAgentPlist, sameWorker, sameServiceDefinition, sameWorkerSet, InferenceHostServiceManager, appendServiceLog, spawnInferenceHostServiceChild, runInferenceHostServiceSupervisor;
31293
31633
  var init_service = __esm({
31294
31634
  "lib/inference-host/service.ts"() {
31295
31635
  "use strict";
31636
+ init_durable_adapter();
31296
31637
  init_config();
31297
31638
  SERVICE_NAME = "VTX Macro Inference Host";
31298
31639
  SYSTEMD_UNIT = "vtx-inference-host.service";
@@ -31381,14 +31722,15 @@ var init_service = __esm({
31381
31722
  throw new Error("Inference-host service worker is invalid.");
31382
31723
  }
31383
31724
  const record2 = value;
31384
- if (typeof record2.instance_name !== "string" || !/^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$/u.test(record2.instance_name) || record2.adapter !== "codex" || typeof record2.display_name !== "string" || !record2.display_name || record2.max_concurrency !== null && (!Number.isSafeInteger(record2.max_concurrency) || Number(record2.max_concurrency) < 1) || record2.authenticated_account_email !== null && typeof record2.authenticated_account_email !== "string" || record2.authenticated_account_plan !== null && typeof record2.authenticated_account_plan !== "string") {
31725
+ if (typeof record2.instance_name !== "string" || !/^[a-z0-9](?:[a-z0-9-]{0,46}[a-z0-9])?$/u.test(record2.instance_name) || typeof record2.adapter !== "string" || !isDurableInferenceAdapterId(record2.adapter) || typeof record2.display_name !== "string" || !record2.display_name || record2.max_concurrency !== null && (!Number.isSafeInteger(record2.max_concurrency) || Number(record2.max_concurrency) < 1) || record2.authenticated_account_email !== null && typeof record2.authenticated_account_email !== "string" || record2.authenticated_account_identity !== void 0 && record2.authenticated_account_identity !== null && typeof record2.authenticated_account_identity !== "string" || record2.authenticated_account_plan !== null && typeof record2.authenticated_account_plan !== "string") {
31385
31726
  throw new Error("Inference-host service worker is invalid.");
31386
31727
  }
31387
31728
  return {
31388
31729
  instance_name: record2.instance_name,
31389
- adapter: "codex",
31730
+ adapter: record2.adapter,
31390
31731
  display_name: record2.display_name,
31391
31732
  max_concurrency: record2.max_concurrency === null ? null : Number(record2.max_concurrency),
31733
+ authenticated_account_identity: record2.authenticated_account_identity,
31392
31734
  authenticated_account_email: record2.authenticated_account_email,
31393
31735
  authenticated_account_plan: record2.authenticated_account_plan,
31394
31736
  runtime_environment: assertRuntimeEnvironment(record2.runtime_environment)
@@ -31755,8 +32097,8 @@ WantedBy=default.target
31755
32097
  }
31756
32098
  definitionPath() {
31757
32099
  if (this.platform === "win32") return `${this.config.supervisorStatePath}.service-task.xml`;
31758
- if (this.platform === "darwin") return join5(this.home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
31759
- return join5(this.home, ".config", "systemd", "user", SYSTEMD_UNIT);
32100
+ if (this.platform === "darwin") return join6(this.home, "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
32101
+ return join6(this.home, ".config", "systemd", "user", SYSTEMD_UNIT);
31760
32102
  }
31761
32103
  windowsLauncherPath() {
31762
32104
  return `${this.config.supervisorStatePath}.service-launcher.vbs`;
@@ -31938,13 +32280,13 @@ ${cleanup.stderr}`)) {
31938
32280
  );
31939
32281
  }
31940
32282
  }
31941
- await rm4(this.definitionPath(), { force: true }).catch(() => void 0);
32283
+ await rm5(this.definitionPath(), { force: true }).catch(() => void 0);
31942
32284
  if (this.platform === "win32") {
31943
- await rm4(this.windowsLauncherPath(), { force: true }).catch(() => void 0);
32285
+ await rm5(this.windowsLauncherPath(), { force: true }).catch(() => void 0);
31944
32286
  }
31945
- await rm4(this.manifestPath(), { force: true }).catch(() => void 0);
31946
- await rm4(this.desiredPath(), { force: true }).catch(() => void 0);
31947
- await rm4(this.runtimePath(), { force: true }).catch(() => void 0);
32287
+ await rm5(this.manifestPath(), { force: true }).catch(() => void 0);
32288
+ await rm5(this.desiredPath(), { force: true }).catch(() => void 0);
32289
+ await rm5(this.runtimePath(), { force: true }).catch(() => void 0);
31948
32290
  if (this.platform === "linux") {
31949
32291
  await this.runCommand("systemctl", ["--user", "daemon-reload"]).catch(() => void 0);
31950
32292
  }
@@ -32040,18 +32382,31 @@ ${cleanup.stderr}`)) {
32040
32382
  }
32041
32383
  async installUnlocked(options) {
32042
32384
  const existing = await readInferenceHostServiceManifest(this.manifestPath());
32385
+ const currentWorker = existing?.workers.find(
32386
+ (worker) => worker.instance_name === this.config.instanceName
32387
+ );
32388
+ if (currentWorker && currentWorker.adapter !== options.adapter) {
32389
+ throw new Error(
32390
+ `Inference-host instance ${this.config.instanceName} is already bound to ${currentWorker.adapter}; install ${options.adapter} under a distinct --instance and VTX grant.`
32391
+ );
32392
+ }
32043
32393
  const normalizedEmail = options.authenticatedAccountEmail?.trim().toLowerCase() || null;
32394
+ const normalizedAccountIdentity = options.authenticatedAccountIdentity?.trim().toLowerCase() || null;
32395
+ const normalizedIdentity = normalizedAccountIdentity || normalizedEmail;
32044
32396
  const otherWorkers = existing?.workers.filter(
32045
32397
  (worker) => worker.instance_name !== this.config.instanceName
32046
32398
  ) ?? [];
32047
- if (otherWorkers.length > 0 && (!normalizedEmail || otherWorkers.some((worker) => !worker.authenticated_account_email))) {
32399
+ const peerAdapterWorkers = otherWorkers.filter(
32400
+ (worker) => worker.adapter === options.adapter
32401
+ );
32402
+ if (peerAdapterWorkers.length > 0 && (!normalizedIdentity || peerAdapterWorkers.some((worker) => !(worker.authenticated_account_identity || worker.authenticated_account_email)))) {
32048
32403
  throw new Error(
32049
- "Cannot prove that every installed instance uses a distinct ChatGPT subscription. Reinstall instances with live account identity before adding another."
32404
+ `Cannot prove that every installed ${options.adapter} instance uses a distinct subscription identity.`
32050
32405
  );
32051
32406
  }
32052
- if (normalizedEmail && otherWorkers.some((worker) => worker.authenticated_account_email?.trim().toLowerCase() === normalizedEmail)) {
32407
+ if (normalizedIdentity && peerAdapterWorkers.some((worker) => (worker.authenticated_account_identity || worker.authenticated_account_email)?.trim().toLowerCase() === normalizedIdentity)) {
32053
32408
  throw new Error(
32054
- "This ChatGPT subscription is already installed under another inference-host instance."
32409
+ `This ${options.adapter} subscription is already installed under another inference-host instance.`
32055
32410
  );
32056
32411
  }
32057
32412
  if (this.config.credentialFilePath && otherWorkers.some((worker) => {
@@ -32072,6 +32427,7 @@ ${cleanup.stderr}`)) {
32072
32427
  adapter: options.adapter,
32073
32428
  display_name: options.displayName,
32074
32429
  max_concurrency: maxConcurrency,
32430
+ ...normalizedAccountIdentity ? { authenticated_account_identity: normalizedAccountIdentity } : {},
32075
32431
  authenticated_account_email: normalizedEmail,
32076
32432
  authenticated_account_plan: options.authenticatedAccountPlan ?? null,
32077
32433
  runtime_environment: runtimeEnvironment(this.config, maxConcurrency)
@@ -32245,12 +32601,14 @@ ${result2.stderr}`)) {
32245
32601
  manager_active: managerActive,
32246
32602
  manager: managerName(this.platform),
32247
32603
  manager_state: result2.exitCode === 0 ? output3 || "installed" : "not-installed",
32248
- adapter: manifest ? "codex" : null,
32604
+ adapter: manifest && new Set(manifest.workers.map((worker) => worker.adapter)).size === 1 ? manifest.workers[0]?.adapter ?? null : null,
32249
32605
  log_path: manifest?.log_path ?? this.logPath(),
32250
32606
  workers: manifest?.workers.map((worker) => ({
32251
32607
  instance_name: worker.instance_name,
32608
+ adapter: worker.adapter,
32252
32609
  display_name: worker.display_name,
32253
32610
  max_concurrency: worker.max_concurrency,
32611
+ authenticated_account_identity: worker.authenticated_account_identity ?? null,
32254
32612
  authenticated_account_email: worker.authenticated_account_email,
32255
32613
  authenticated_account_plan: worker.authenticated_account_plan,
32256
32614
  runtime_state: runtime?.manifest_generation === manifest.generation && managerActive ? runtime.workers.find((item) => item.instance_name === worker.instance_name)?.state ?? "unknown" : "unknown"
@@ -32314,12 +32672,12 @@ ${result2.stderr}`)) {
32314
32672
  ${result2.stderr}`)) {
32315
32673
  throw new Error(`Background service uninstall failed: ${result2.stderr.trim()}`);
32316
32674
  }
32317
- await rm4(this.definitionPath(), { force: true });
32318
- if (this.platform === "win32") await rm4(this.windowsLauncherPath(), { force: true });
32675
+ await rm5(this.definitionPath(), { force: true });
32676
+ if (this.platform === "win32") await rm5(this.windowsLauncherPath(), { force: true });
32319
32677
  if (this.platform === "linux") await this.runCommand("systemctl", ["--user", "daemon-reload"]);
32320
- await rm4(this.manifestPath(), { force: true });
32321
- await rm4(this.desiredPath(), { force: true });
32322
- await rm4(this.runtimePath(), { force: true });
32678
+ await rm5(this.manifestPath(), { force: true });
32679
+ await rm5(this.desiredPath(), { force: true });
32680
+ await rm5(this.runtimePath(), { force: true });
32323
32681
  return {
32324
32682
  installed: false,
32325
32683
  desired_running: false,
@@ -32348,6 +32706,8 @@ ${result2.stderr}`)) {
32348
32706
  "inference-host",
32349
32707
  "run",
32350
32708
  "--json",
32709
+ "--adapter",
32710
+ worker.adapter,
32351
32711
  "--display-name",
32352
32712
  worker.display_name
32353
32713
  ];
@@ -32460,7 +32820,7 @@ ${result2.stderr}`)) {
32460
32820
  });
32461
32821
  const launch = options.runWorker ?? options.spawnChild ?? spawnInferenceHostServiceChild;
32462
32822
  await appendServiceLog(initialManifest.log_path, "service_supervisor_started", {
32463
- adapter: "codex",
32823
+ adapters: [...new Set(initialManifest.workers.map((worker) => worker.adapter))].sort(),
32464
32824
  instances: initialManifest.workers.map((worker) => worker.instance_name)
32465
32825
  });
32466
32826
  const workers = /* @__PURE__ */ new Map();
@@ -32681,8 +33041,8 @@ __export(cli_exports, {
32681
33041
  });
32682
33042
  import { createHash as createHash7, randomUUID as randomUUID2 } from "node:crypto";
32683
33043
  import { spawn as spawn7 } from "node:child_process";
32684
- import { lstat as lstat4, realpath as realpath4, rm as rm5 } from "node:fs/promises";
32685
- import { join as join6, resolve as resolve5 } from "node:path";
33044
+ import { lstat as lstat4, realpath as realpath4, rm as rm6 } from "node:fs/promises";
33045
+ import { join as join7, resolve as resolve5 } from "node:path";
32686
33046
  async function runInferenceHostCli(argv2, env = process.env, dependencies = {}) {
32687
33047
  const warnings = [];
32688
33048
  try {
@@ -32783,7 +33143,7 @@ async function runInferenceHostCli(argv2, env = process.env, dependencies = {})
32783
33143
  };
32784
33144
  }
32785
33145
  }
32786
- var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, credentialLifecycleLockPath, serviceRecoveryCommandLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parseHostConcurrency, parseInferenceHostArgs, defaultOpenBrowser, registerInferenceHostServiceControlInput, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, restoreInferenceHostCredentialContext, recoverInferenceHostCredentialContextTransition, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertDurableServiceUninstalled, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, recoveryBackupPath, serviceRecoveryTransactionPath, readServiceRecoveryTransaction, assertResumableCodexRecoveryEvidence, recoverInstalledCodexService, serviceCommand, hasExplicitCredentialStoreConfiguration, credentialEnvironmentForIdentity, commandRequiresRetainedCredentialStore, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
33146
+ var INFERENCE_HOST_CLI_VERSION, runtimeReceiptPath, codexRecoveryPath, codexGuardianReceiptRoot, revocationCheckpointPath, foregroundHostLockPath, credentialLifecycleLockPath, serviceRecoveryCommandLockPath, AGENT_HEARTBEAT_INTERVAL_MS, AGENT_HEARTBEAT_MAX_RETRY_DELAY_MS, retryableAgentHeartbeatError, assertRevocationCheckpoint, readRevocationCheckpoint, writeRevocationCheckpoint, clearRevocationCheckpoint, render, INFERENCE_HOST_HELP, parseHostConcurrency, parseInferenceHostArgs, defaultOpenBrowser, registerInferenceHostServiceControlInput, defaultRegisterLifecycleSignalHandlers, lifecycleCancellation, configuredCredentialStore, restoreInferenceHostCredentialContext, recoverInferenceHostCredentialContextTransition, accountKeyForState, assertCredentialMatchesState2, revocationCheckpointForCredential, assertRevocationCheckpointMatchesLocalIdentity, fileExistsPrivately, inspectCodexAuthentication, clearLocalRuntimeArtifacts, assertDurableServiceUninstalled, assertLocalRuntimeArtifactsMayBeDiscarded, defaultRunForeground, preparePortableDurableAdapter, defaultRunPortableDurableAdapter, login, codexLogin, codexLogout, localStatus, doctor, cleanupLogin, runHost, defaultReadStdin, parseAgentStdin, agentOperationId, agentSession, agentConnect, agentRun, agentNext, agentComplete, agentFail, withAgentCommandLock, recoveryBackupPath, serviceRecoveryTransactionPath, readServiceRecoveryTransaction, assertResumableCodexRecoveryEvidence, recoverInstalledCodexService, serviceCommand, hasExplicitCredentialStoreConfiguration, credentialEnvironmentForIdentity, commandRequiresRetainedCredentialStore, commandUsesInstalledServiceCredentials, resolveInferenceHostCommandConfig;
32787
33147
  var init_cli = __esm({
32788
33148
  "lib/inference-host/cli.ts"() {
32789
33149
  "use strict";
@@ -32793,6 +33153,8 @@ var init_cli = __esm({
32793
33153
  init_agent_state();
32794
33154
  init_codex_adapter();
32795
33155
  init_codex_recovery_process();
33156
+ init_copilot_adapter();
33157
+ init_durable_adapter();
32796
33158
  init_codex_app_server();
32797
33159
  init_codex_binary();
32798
33160
  init_config();
@@ -32875,7 +33237,7 @@ var init_cli = __esm({
32875
33237
  Commands:
32876
33238
  login Authorize the isolated VTX insights:inference grant
32877
33239
  codex-login Sign the automated Codex host into a ChatGPT subscription
32878
- run Run the automated Codex inference host in the foreground
33240
+ run Run a supported durable inference adapter in the foreground
32879
33241
  agent-connect Advertise a model from a compatible agent harness
32880
33242
  agent-run Keep an agent-driven host online in the foreground
32881
33243
  agent-next Claim the next exact VTX inference request
@@ -32883,7 +33245,7 @@ Commands:
32883
33245
  agent-fail Submit one truthful agent failure from stdin
32884
33246
  service Install and control the durable background host
32885
33247
  status Inspect local host and credential state
32886
- doctor Verify credentials, Codex, and private runtime state
33248
+ doctor Verify credentials, provider runtime, and private state
32887
33249
  logout Remove local VTX host state without revoking the grant
32888
33250
  revoke Revoke the VTX grant and remove local host state
32889
33251
  codex-logout Remove the automated host's dedicated Codex login
@@ -32891,7 +33253,8 @@ Commands:
32891
33253
  Common options:
32892
33254
  --json Emit machine-readable JSON
32893
33255
  --instance NAME Target an isolated local subscription instance (default: default)
32894
- --max-concurrency N Optional positive-integer Codex slot limit (default: unlimited)
33256
+ --adapter ID Durable adapter: codex or copilot
33257
+ --max-concurrency N Optional positive-integer local slot limit (default: unlimited)
32895
33258
  --help, -h Show this help
32896
33259
 
32897
33260
  If the OS credential store cannot retain the VTX grant, set
@@ -32905,6 +33268,7 @@ Durable service:
32905
33268
  vtx inference-host login --instance codex-2
32906
33269
  vtx inference-host codex-login --instance codex-2
32907
33270
  vtx inference-host service install --instance codex-2
33271
+ vtx inference-host service install --adapter copilot --instance copilot-1
32908
33272
  vtx inference-host service uninstall --instance codex-2
32909
33273
  vtx inference-host service recover --instance codex-1 --force-recovery
32910
33274
  vtx inference-host service <start|stop|status|logs|recover|uninstall>
@@ -33010,6 +33374,8 @@ Durable service:
33010
33374
  }
33011
33375
  if (command === "agent-connect" && !displayNameExplicit) {
33012
33376
  displayName = "Agent-driven inference host";
33377
+ } else if (adapter && isDurableInferenceAdapterId(adapter) && !displayNameExplicit) {
33378
+ displayName = `${durableAdapterDisplayName(adapter)} subscription host`;
33013
33379
  }
33014
33380
  return {
33015
33381
  command,
@@ -33154,7 +33520,7 @@ Durable service:
33154
33520
  return raw !== null;
33155
33521
  };
33156
33522
  inspectCodexAuthentication = async (config2) => {
33157
- const path = join6(config2.codexHomePath, "auth.json");
33523
+ const path = join7(config2.codexHomePath, "auth.json");
33158
33524
  try {
33159
33525
  const before = await lstat4(path);
33160
33526
  const canonical = await realpath4(path);
@@ -33172,7 +33538,7 @@ Durable service:
33172
33538
  runtimeReceiptPath(config2),
33173
33539
  "Inference host runtime receipt file"
33174
33540
  );
33175
- await rm5(codexGuardianReceiptRoot(config2), { recursive: true, force: true });
33541
+ await rm6(codexGuardianReceiptRoot(config2), { recursive: true, force: true });
33176
33542
  await clearInferencePrivateFile(
33177
33543
  codexRecoveryPath(config2),
33178
33544
  "Codex attempt recovery file"
@@ -33254,6 +33620,49 @@ Durable service:
33254
33620
  signal: options.signal
33255
33621
  }).run();
33256
33622
  };
33623
+ preparePortableDurableAdapter = async (adapterId, signal) => {
33624
+ const adapter = new CopilotSubscriptionAdapter();
33625
+ return { adapter, preflight: await adapter.preflight(signal) };
33626
+ };
33627
+ defaultRunPortableDurableAdapter = async (options) => {
33628
+ const dependencies = createDefaultInferenceHostRunnerDependencies({
33629
+ apiUrl: options.config.apiUrl,
33630
+ statePath: options.config.statePath,
33631
+ processLockPath: options.config.processLockPath,
33632
+ credentialStore: options.credentialStore,
33633
+ codexAdapter: options.adapter,
33634
+ envelopePublicKey: (credential) => externalInferenceEnvelopePublicKey(credential.x25519_private_key),
33635
+ clientVersion: INFERENCE_HOST_CLI_VERSION,
33636
+ runtimeReceiptPath: runtimeReceiptPath(options.config)
33637
+ });
33638
+ const borrowedProcessLock = {
33639
+ path: options.processLock.path,
33640
+ ownerToken: options.processLock.ownerToken,
33641
+ release: async () => void 0
33642
+ };
33643
+ return await new InferenceHostRunner({
33644
+ ...dependencies,
33645
+ acquireProcessLock: async () => borrowedProcessLock
33646
+ }, {
33647
+ adapterId: options.preflight.adapterId,
33648
+ usageSource: options.preflight.adapterId,
33649
+ structuredOutput: false,
33650
+ sameAttemptRecovery: options.preflight.sameAttemptRecovery,
33651
+ instanceName: options.config.instanceName,
33652
+ displayName: options.displayName,
33653
+ authenticatedAccountIdentity: options.preflight.authenticatedAccountIdentity,
33654
+ authenticatedAccountEmail: options.preflight.authenticatedAccountEmail,
33655
+ authenticatedAccountPlan: options.preflight.authenticatedAccountPlan,
33656
+ codexRateLimits: options.preflight.rateLimits,
33657
+ codexModelCapabilities: options.preflight.modelCapabilities,
33658
+ protocolVersion: EXTERNAL_INFERENCE_CONTRACT_VERSION,
33659
+ adapterRuntimeVersion: options.preflight.runtimeVersion,
33660
+ maxConcurrency: options.maxConcurrency,
33661
+ once: options.once,
33662
+ emitDiagnosticEvent: options.emitDiagnosticEvent,
33663
+ signal: options.signal
33664
+ }).run();
33665
+ };
33257
33666
  login = async (config2, parsed, env, dependencies, warnings) => {
33258
33667
  const credentialLock = await acquireInferenceHostProcessLock(
33259
33668
  credentialLifecycleLockPath(config2)
@@ -33601,6 +34010,35 @@ Waiting for approval...
33601
34010
  detail: error48 instanceof Error ? error48.message : "unavailable"
33602
34011
  });
33603
34012
  }
34013
+ const adapterId = parsed.adapter ?? "codex";
34014
+ if (!isDurableInferenceAdapterId(adapterId)) {
34015
+ checks.push({ name: "provider_runtime", ok: false, detail: "unsupported-adapter" });
34016
+ } else if (adapterId !== "codex") {
34017
+ try {
34018
+ const prepared = await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(
34019
+ adapterId
34020
+ );
34021
+ checks.push({
34022
+ name: "provider_runtime",
34023
+ ok: true,
34024
+ detail: `${adapterId}:${prepared.preflight.runtimeVersion}:${prepared.preflight.modelCapabilities.length}-models`
34025
+ });
34026
+ await prepared.adapter.close?.();
34027
+ } catch (error48) {
34028
+ checks.push({
34029
+ name: "provider_runtime",
34030
+ ok: false,
34031
+ detail: error48 instanceof Error ? error48.message : "unavailable"
34032
+ });
34033
+ }
34034
+ const healthy2 = checks.every((check2) => check2.ok);
34035
+ return {
34036
+ exitCode: healthy2 ? 0 : 1,
34037
+ stdout: render({ healthy: healthy2, adapter: adapterId, checks, source: "local_only" }, parsed.json),
34038
+ stderr: warnings.length > 0 ? `${warnings.join("\n")}
34039
+ ` : ""
34040
+ };
34041
+ }
33604
34042
  try {
33605
34043
  const binary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
33606
34044
  checks.push({
@@ -33817,6 +34255,37 @@ Waiting for approval...
33817
34255
  if (await store.readRecovery?.()) {
33818
34256
  throw new Error("Inference host credential recovery is required before the runner can start.");
33819
34257
  }
34258
+ const adapterId = parsed.adapter ?? "codex";
34259
+ if (!isDurableInferenceAdapterId(adapterId)) {
34260
+ throw new Error(`Adapter ${adapterId} does not have a supported durable integration.`);
34261
+ }
34262
+ const emitDiagnosticEvent = parsed.json ? (event) => (dependencies.emitStdout ?? ((text) => {
34263
+ process.stdout.write(text);
34264
+ }))(`${JSON.stringify(event)}
34265
+ `) : void 0;
34266
+ if (adapterId !== "codex") {
34267
+ const prepared = await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(
34268
+ adapterId,
34269
+ cancellation.signal
34270
+ );
34271
+ const summary = await (dependencies.runPortableDurableAdapter ?? defaultRunPortableDurableAdapter)({
34272
+ config: config2,
34273
+ credentialStore: store,
34274
+ ...prepared,
34275
+ displayName: parsed.displayName,
34276
+ maxConcurrency: parsed.maxConcurrency,
34277
+ once: parsed.once,
34278
+ processLock,
34279
+ emitDiagnosticEvent,
34280
+ signal: cancellation.signal
34281
+ });
34282
+ return {
34283
+ exitCode: 0,
34284
+ stdout: render({ status: "stopped", ...summary }, parsed.json),
34285
+ stderr: warnings.length > 0 ? `${warnings.join("\n")}
34286
+ ` : ""
34287
+ };
34288
+ }
33820
34289
  const verifiedBinary = await (dependencies.resolveBinary ?? resolvePinnedCodexBinary)(env);
33821
34290
  cancellation.signal.throwIfAborted();
33822
34291
  await reconcileCodexAttemptRecovery({
@@ -33847,10 +34316,7 @@ Waiting for approval...
33847
34316
  once: parsed.once,
33848
34317
  env,
33849
34318
  processLock,
33850
- emitDiagnosticEvent: parsed.json ? (event) => (dependencies.emitStdout ?? ((text) => {
33851
- process.stdout.write(text);
33852
- }))(`${JSON.stringify(event)}
33853
- `) : void 0,
34319
+ emitDiagnosticEvent,
33854
34320
  signal: cancellation.signal
33855
34321
  });
33856
34322
  return {
@@ -34399,6 +34865,17 @@ Waiting for approval...
34399
34865
  credentialLifecycleLockPath(config2)
34400
34866
  );
34401
34867
  try {
34868
+ const installedManifest = await readInferenceHostServiceManifest(
34869
+ `${config2.supervisorStatePath}.service.json`
34870
+ );
34871
+ const installedWorker = installedManifest?.workers.find(
34872
+ (worker) => worker.instance_name === config2.instanceName
34873
+ );
34874
+ if (installedWorker && installedWorker.adapter !== "codex") {
34875
+ throw new Error(
34876
+ `Guarded same-attempt recovery is not supported for ${installedWorker.adapter}; interrupted attempts remain quarantined as outcome_unknown.`
34877
+ );
34878
+ }
34402
34879
  const recoveryStore = new FileCodexAttemptRecoveryStore(codexRecoveryPath(config2));
34403
34880
  const existingTransaction = await readServiceRecoveryTransaction(config2);
34404
34881
  if (existingTransaction && existingTransaction.instance_name !== config2.instanceName) {
@@ -34584,7 +35061,7 @@ Waiting for approval...
34584
35061
  serviceRecoveryTransactionPath(config2),
34585
35062
  "Inference-host service recovery transaction"
34586
35063
  );
34587
- await rm5(backupPath, { force: true });
35064
+ await rm6(backupPath, { force: true });
34588
35065
  }
34589
35066
  }
34590
35067
  } finally {
@@ -34648,7 +35125,7 @@ Waiting for approval...
34648
35125
  );
34649
35126
  }
34650
35127
  const adapter = parsed.adapter || "codex";
34651
- if (adapter !== "codex") {
35128
+ if (!isDurableInferenceAdapterId(adapter)) {
34652
35129
  throw new Error(
34653
35130
  `Adapter ${adapter} does not have a supported durable service integration. Use foreground agent-run instead.`
34654
35131
  );
@@ -34660,7 +35137,31 @@ Waiting for approval...
34660
35137
  if (!credential) throw new Error("Inference-host credential is missing. Run login again.");
34661
35138
  assertCredentialMatchesState2(state, credential);
34662
35139
  if (parsed.modelId || parsed.modelLabel || parsed.reasoningEffort) {
34663
- throw new Error("Automated Codex service does not accept agent model or effort options.");
35140
+ throw new Error("Durable services discover their exact model and effort catalog from the authenticated runtime.");
35141
+ }
35142
+ if (adapter !== "codex") {
35143
+ const prepared = await (dependencies.prepareDurableAdapter ?? preparePortableDurableAdapter)(
35144
+ adapter
35145
+ );
35146
+ try {
35147
+ const status2 = await manager.install({
35148
+ adapter,
35149
+ displayName: parsed.displayName,
35150
+ maxConcurrency: parsed.maxConcurrency,
35151
+ authenticatedAccountIdentity: prepared.preflight.authenticatedAccountIdentity,
35152
+ authenticatedAccountEmail: prepared.preflight.authenticatedAccountEmail,
35153
+ authenticatedAccountPlan: prepared.preflight.authenticatedAccountPlan
35154
+ });
35155
+ await writeInferenceHostCredentialContext(config2);
35156
+ return {
35157
+ exitCode: 0,
35158
+ stdout: render({ status: "service_installed", ...status2 }, parsed.json),
35159
+ stderr: warnings.length > 0 ? `${warnings.join("\n")}
35160
+ ` : ""
35161
+ };
35162
+ } finally {
35163
+ await prepared.adapter.close?.();
35164
+ }
34664
35165
  }
34665
35166
  const auth = await inspectCodexAuthentication(config2);
34666
35167
  if (!auth.present || !auth.private) {
@@ -34827,11 +35328,11 @@ Waiting for approval...
34827
35328
  });
34828
35329
 
34829
35330
  // lib/agent-core/config.ts
34830
- import { mkdir as mkdir5, readFile as readFile6, rm as rm6, writeFile as writeFile3 } from "node:fs/promises";
34831
- import { dirname as dirname5, join as join7 } from "node:path";
35331
+ import { mkdir as mkdir5, readFile as readFile6, rm as rm7, writeFile as writeFile3 } from "node:fs/promises";
35332
+ import { dirname as dirname5, join as join8 } from "node:path";
34832
35333
  import { homedir as homedir3 } from "node:os";
34833
35334
  function defaultBaseDir() {
34834
- return join7(homedir3(), ".vtx");
35335
+ return join8(homedir3(), ".vtx");
34835
35336
  }
34836
35337
  function resolveAgentCliConfig(env = process.env) {
34837
35338
  const baseDir = String(env.VTX_HOME || "").trim() || defaultBaseDir();
@@ -34839,8 +35340,8 @@ function resolveAgentCliConfig(env = process.env) {
34839
35340
  const parsedProfile = rawProfile ? Number(rawProfile) : NaN;
34840
35341
  return {
34841
35342
  apiUrl: String(env.VTX_API_URL || "http://localhost:8000").replace(/\/+$/, ""),
34842
- tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join7(baseDir, "token.json"),
34843
- statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() || join7(baseDir, "runtime-state.json"),
35343
+ tokenPath: String(env.VTX_TOKEN_PATH || "").trim() || join8(baseDir, "token.json"),
35344
+ statePath: String(env.VTX_RUNTIME_STATE_PATH || "").trim() || join8(baseDir, "runtime-state.json"),
34844
35345
  runtimeDeviceId: String(env.VTX_RUNTIME_DEVICE_ID || "").trim() || null,
34845
35346
  activeProfileId: Number.isFinite(parsedProfile) && parsedProfile > 0 ? parsedProfile : null,
34846
35347
  outputJson: String(env.VTX_OUTPUT || "").trim().toLowerCase() === "json"
@@ -34871,7 +35372,7 @@ async function writeStoredAgentAuth(path, auth) {
34871
35372
  `, { encoding: "utf8", mode: 384 });
34872
35373
  }
34873
35374
  async function clearStoredAgentAuth(path) {
34874
- await rm6(path, { force: true });
35375
+ await rm7(path, { force: true });
34875
35376
  }
34876
35377
  async function readRuntimeState(path) {
34877
35378
  try {
@@ -34904,7 +35405,7 @@ async function writeRuntimeState(path, state) {
34904
35405
  `, { encoding: "utf8", mode: 384 });
34905
35406
  }
34906
35407
  async function clearRuntimeState(path) {
34907
- await rm6(path, { force: true });
35408
+ await rm7(path, { force: true });
34908
35409
  }
34909
35410
  var init_config2 = __esm({
34910
35411
  "lib/agent-core/config.ts"() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtxmacro/cli",
3
- "version": "2026.8.40",
3
+ "version": "2026.8.42",
4
4
  "description": "VTX Macro CLI, MCP server, and durable subscription inference host.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -20,7 +20,9 @@
20
20
  "node": ">=20.0.0"
21
21
  },
22
22
  "dependencies": {
23
- "@openai/codex": "0.147.0"
23
+ "@openai/codex": "0.147.0",
24
+ "@github/copilot-sdk": "1.0.0-beta.8",
25
+ "@github/copilot": "1.0.80"
24
26
  },
25
27
  "files": [
26
28
  "bin",