sneakoscope 9.1.0 → 9.2.0

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 (33) hide show
  1. package/README.md +1 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/commands/bridge.js +11 -0
  5. package/dist/config/skills-manifest.json +1 -1
  6. package/dist/core/codex-app.js +1 -1
  7. package/dist/core/codex-control/codex-sdk-env-policy.js +44 -9
  8. package/dist/core/codex-lb/bridge-contracts.js +1 -0
  9. package/dist/core/codex-lb/desktop-bridge/header-policy.js +38 -0
  10. package/dist/core/codex-lb/desktop-bridge/http-forward.js +135 -49
  11. package/dist/core/codex-lb/desktop-bridge/rejection-log.js +1 -0
  12. package/dist/core/codex-lb/desktop-bridge/security.js +55 -3
  13. package/dist/core/codex-lb/desktop-bridge/server.js +7 -15
  14. package/dist/core/codex-lb/desktop-bridge/types.js +1 -0
  15. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +35 -14
  16. package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +1 -3
  17. package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +1 -1
  18. package/dist/core/codex-lb/desktop-bridge-migration.js +2 -4
  19. package/dist/core/codex-lb/desktop-controller-v3/catalog.js +6 -4
  20. package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +36 -2
  21. package/dist/core/codex-lb/desktop-controller-v3.js +3 -1
  22. package/dist/core/codex-lb/desktop-service.js +95 -11
  23. package/dist/core/codex-lb/provider-route-policy.js +30 -1
  24. package/dist/core/codex-lb/request-route-resolver.js +14 -0
  25. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +28 -0
  26. package/dist/core/hooks-runtime.js +2 -2
  27. package/dist/core/imagegen/desktop-bridge-imagegen-target.js +2 -2
  28. package/dist/core/init/skills.js +19 -4
  29. package/dist/core/provider/provider-context.js +2 -1
  30. package/dist/core/release/gate-affected-globs.js +9 -0
  31. package/dist/core/update/update-migration-state/simple-stages.js +22 -16
  32. package/dist/core/version.js +1 -1
  33. package/package.json +1 -1
@@ -1,10 +1,11 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  import net from 'node:net';
3
3
  import tls from 'node:tls';
4
- import { buildProviderWebSocketHeaders } from './header-policy.js';
4
+ import { BRIDGE_OFFICIAL_ROUTE_ID } from '../bridge-contracts.js';
5
+ import { buildOfficialPassthroughWebSocketHeaders, buildProviderWebSocketHeaders } from './header-policy.js';
5
6
  import { createDesktopBridgeRejectionLogger } from './rejection-log.js';
6
7
  import { rewriteLocationHeader } from './location-rewrite.js';
7
- import { resolveAndBindDesktopBridgeRouteContext, resolveCodexSessionIdentity, resolveDesktopBridgeTarget, safeBridgeErrorCode, singleBridgeHeader, canonicalSessionId } from './security.js';
8
+ import { desktopBridgeOfficialPassthroughEnabled, resolveAndBindDesktopBridgeRouteContext, resolveCodexSessionIdentity, resolveDesktopBridgeTarget, safeBridgeErrorCode, singleBridgeHeader, canonicalSessionId } from './security.js';
8
9
  import { desktopBridgeListenOrigin } from './state.js';
9
10
  import { DESKTOP_BRIDGE_DIAGNOSTIC_PROTOCOL, DesktopBridgeError, } from './types.js';
10
11
  const MAX_HEAD = 64 * 1024;
@@ -82,7 +83,7 @@ export async function prepareDesktopBridgeWebSocketRequest(req, config) {
82
83
  const sessionIdentity = resolveCodexSessionIdentity(req.headers);
83
84
  const pinnedModel = websocketPinnedModel(sessionIdentity.thread_id, config);
84
85
  const publicModel = singleBridgeHeader(req.headers, 'x-sks-model') || pinnedModel || '';
85
- if (!publicModel) {
86
+ if (!publicModel && !(desktopBridgeOfficialPassthroughEnabled(config) && config.officialRemote)) {
86
87
  throw new DesktopBridgeError('bridge_websocket_route_unresolvable');
87
88
  }
88
89
  const route = await resolveAndBindDesktopBridgeRouteContext({
@@ -91,6 +92,8 @@ export async function prepareDesktopBridgeWebSocketRequest(req, config) {
91
92
  pathname: new URL(req.url || '/', 'http://bridge.invalid').pathname,
92
93
  transport: 'websocket', headers: req.headers,
93
94
  }, config);
95
+ if (route.provider_id === BRIDGE_OFFICIAL_ROUTE_ID)
96
+ return { route, provider: null, credential: null };
94
97
  const provider = config.providers[route.provider_id];
95
98
  if (!provider)
96
99
  throw new DesktopBridgeError('bridge_provider_route_unavailable');
@@ -107,6 +110,17 @@ const PERMANENT_UPGRADE_REFUSALS = new Set([
107
110
  'catalog_model_route_missing',
108
111
  'bridge_provider_route_unavailable',
109
112
  ]);
113
+ export function safeEndUpgradeSocket(socket, payload) {
114
+ socket.on('error', () => undefined);
115
+ if (socket.destroyed || socket.writableEnded)
116
+ return;
117
+ try {
118
+ socket.end(payload);
119
+ }
120
+ catch {
121
+ socket.destroy();
122
+ }
123
+ }
110
124
  function writeUpgradeFailure(client, error, req) {
111
125
  const code = safeBridgeErrorCode(error) || 'bridge_websocket_upstream_unavailable';
112
126
  const permanent = PERMANENT_UPGRADE_REFUSALS.has(code);
@@ -119,9 +133,7 @@ function writeUpgradeFailure(client, error, req) {
119
133
  ...(req?.url === undefined ? {} : { url: req.url }),
120
134
  status,
121
135
  });
122
- if (client.destroyed)
123
- return;
124
- client.end(`HTTP/1.1 ${status} ${reason}\r\nContent-Type: application/json\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n`
136
+ safeEndUpgradeSocket(client, `HTTP/1.1 ${status} ${reason}\r\nContent-Type: application/json\r\nCache-Control: no-store\r\nConnection: close\r\n\r\n`
125
137
  + JSON.stringify({ error: { type: 'sks_bridge_error', code, retryable: !permanent } }));
126
138
  }
127
139
  export async function forwardWebSocket(req, client, head, config, authenticatedLocalBaseUrl = desktopBridgeListenOrigin(config)) {
@@ -133,11 +145,18 @@ export async function forwardWebSocket(req, client, head, config, authenticatedL
133
145
  writeUpgradeFailure(client, error, req);
134
146
  return;
135
147
  }
136
- const { provider, credential } = prepared;
137
- const target = resolveDesktopBridgeTarget(req.url, provider.remote);
138
- const upstream = provider.remote.secure
139
- ? tls.connect({ host: provider.remote.address, port: provider.remote.port, ...(provider.remote.tlsServername ? { servername: provider.remote.tlsServername } : {}) })
140
- : net.connect({ host: provider.remote.address, port: provider.remote.port, family: provider.remote.family });
148
+ const { provider, credential, route } = prepared;
149
+ const official = route.provider_id === BRIDGE_OFFICIAL_ROUTE_ID;
150
+ const remote = official ? config.officialRemote : provider?.remote;
151
+ if (!remote) {
152
+ writeUpgradeFailure(client, new DesktopBridgeError(official ? 'bridge_official_passthrough_unavailable' : 'bridge_provider_route_unavailable'), req);
153
+ return;
154
+ }
155
+ const upstreamBaseUrl = official ? remote.baseUrl : provider.base_url;
156
+ const target = resolveDesktopBridgeTarget(req.url, remote);
157
+ const upstream = remote.secure
158
+ ? tls.connect({ host: remote.address, port: remote.port, ...(remote.tlsServername ? { servername: remote.tlsServername } : {}) })
159
+ : net.connect({ host: remote.address, port: remote.port, family: remote.family });
141
160
  const key = String(req.headers['sec-websocket-key'] || '');
142
161
  const requestedProtocol = String(req.headers['sec-websocket-protocol'] || '').split(',')[0]?.trim() || null;
143
162
  let connected = false;
@@ -156,7 +175,9 @@ export async function forwardWebSocket(req, client, head, config, authenticatedL
156
175
  const clientSocket = client;
157
176
  if (typeof clientSocket.setKeepAlive === 'function')
158
177
  clientSocket.setKeepAlive(true, 30_000);
159
- const headers = buildProviderWebSocketHeaders(req.headers, { providerId: provider.provider_id, authTransport: provider.auth_transport, credential }, target.host);
178
+ const headers = official
179
+ ? buildOfficialPassthroughWebSocketHeaders(req.headers, target.host)
180
+ : buildProviderWebSocketHeaders(req.headers, { providerId: provider.provider_id, authTransport: provider.auth_transport, credential: credential }, target.host);
160
181
  upstream.write([`${req.method || 'GET'} ${target.pathname}${target.search} HTTP/1.1`, ...serializeHeaders(headers), '', ''].join('\r\n'));
161
182
  if (head.length)
162
183
  upstream.write(head);
@@ -174,7 +195,7 @@ export async function forwardWebSocket(req, client, head, config, authenticatedL
174
195
  const remaining = response.subarray(boundary + 4);
175
196
  try {
176
197
  validateUpgrade(raw, key, requestedProtocol);
177
- client.write(rewriteUpgradeResponseHead(raw, provider.base_url, authenticatedLocalBaseUrl));
198
+ client.write(rewriteUpgradeResponseHead(raw, upstreamBaseUrl, authenticatedLocalBaseUrl));
178
199
  if (remaining.length)
179
200
  client.write(remaining);
180
201
  }
@@ -187,7 +208,7 @@ export async function forwardWebSocket(req, client, head, config, authenticatedL
187
208
  };
188
209
  upstream.on('data', onData);
189
210
  };
190
- if (provider.remote.secure)
211
+ if (remote.secure)
191
212
  upstream.once('secureConnect', onConnected);
192
213
  else
193
214
  upstream.once('connect', onConnected);
@@ -1,8 +1,6 @@
1
1
  export function authSemanticIdentityPreserved(before, after) {
2
2
  if (before.path !== after.path || before.exists !== after.exists)
3
3
  return false;
4
- if (before.sha256 !== after.sha256)
5
- return false;
6
4
  const beforeIsOAuth = before.mode === 'chatgpt_oauth' || before.mode === 'mixed';
7
5
  const afterIsOAuth = after.mode === 'chatgpt_oauth' || after.mode === 'mixed';
8
6
  if (beforeIsOAuth || afterIsOAuth) {
@@ -11,7 +9,7 @@ export function authSemanticIdentityPreserved(before, after) {
11
9
  && before.semantic_fingerprint !== null
12
10
  && before.semantic_fingerprint === after.semantic_fingerprint;
13
11
  }
14
- return true;
12
+ return before.sha256 === after.sha256;
15
13
  }
16
14
  export function buildDesktopBridgeMigrationReceipt(input) {
17
15
  const common = {
@@ -17,7 +17,7 @@ const SETTINGS_V1_KEYS = new Set([
17
17
  const SETTINGS_V2_KEYS = new Set([
18
18
  'schema', 'listen_host', 'listen_port', 'provider_registry', 'route_policy',
19
19
  'provider_session_pins', 'client_capability_sha256', 'allowed_origins',
20
- 'connect_timeout_ms', 'idle_timeout_ms',
20
+ 'connect_timeout_ms', 'idle_timeout_ms', 'official_passthrough',
21
21
  ]);
22
22
  const TRANSFERABLE_V1_KEYS = [
23
23
  'listen_host', 'listen_port', 'allowed_origins', 'connect_timeout_ms', 'idle_timeout_ms',
@@ -90,8 +90,7 @@ async function migrateDesktopBridgeConfigUnlocked(input) {
90
90
  const authPreserved = authSemanticIdentityPreserved(authBefore, authAfter);
91
91
  const configSha = sha256(currentConfig);
92
92
  const configUnchanged = await fileSha256OrMissing(configPath) === configSha;
93
- const authBytesUnchanged = authBefore.sha256 === authAfter.sha256;
94
- if (!authPreserved || !configUnchanged || !authBytesUnchanged) {
93
+ if (!authPreserved || !configUnchanged) {
95
94
  return {
96
95
  ...baseResult,
97
96
  ok: false,
@@ -101,8 +100,7 @@ async function migrateDesktopBridgeConfigUnlocked(input) {
101
100
  auth_semantic_identity_preserved: authPreserved,
102
101
  blockers: [
103
102
  ...(!configUnchanged ? ['desktop_bridge_config_changed_during_noop'] : []),
104
- ...(!authPreserved ? ['desktop_oauth_identity_changed'] : []),
105
- ...(authPreserved && !authBytesUnchanged ? ['desktop_auth_changed_during_noop'] : [])
103
+ ...(!authPreserved ? ['desktop_oauth_identity_changed'] : [])
106
104
  ]
107
105
  };
108
106
  }
@@ -5,12 +5,12 @@ import { listOpenRouterModels } from '../../providers/openrouter/openrouter-acco
5
5
  import { buildCombinedBridgeCatalog, readActiveCombinedBridgeCatalog, stageCombinedBridgeCatalog } from '../combined-catalog.js';
6
6
  import { COMBINED_BRIDGE_CATALOG_TTL_MS } from '../combined-catalog/contracts.js';
7
7
  import { codexLbEnvPath, codexLbMetadataPath, loadCodexLbEnv, readCodexLbModelCatalog } from '../codex-lb-env.js';
8
- import { bootstrapExistingDesktopBridgeService, desktopBridgeServicePaths, desktopBridgeServiceStatus, resolveDesktopBridgeActivationSettings } from '../desktop-service.js';
8
+ import { bootstrapExistingDesktopBridgeService, desktopBridgeServicePaths, desktopBridgeServiceStatus, readDesktopBridgeServiceSettings, resolveDesktopBridgeActivationSettings, resolveEffectiveOfficialModelsMode } from '../desktop-service.js';
9
9
  import { inspectHistoricalDesktopBridgeIntent, migrateDesktopBridgeConfig } from '../desktop-bridge-migration.js';
10
10
  import { rollbackDesktopBridgeUnificationReceipt } from '../migration-receipt.js';
11
11
  import { recordProviderCredentialValidation } from '../provider-credentials.js';
12
12
  import { bridgeProviderRegistryPath, buildStoredBridgeProviderRegistry, loadStoredBridgeProviderRegistry, resolveBridgeProviderRegistry, serializeStoredBridgeProviderRegistry } from '../provider-registry.js';
13
- import { buildBridgeRoutingPolicy, readBridgeRoutingPolicy } from '../provider-route-policy.js';
13
+ import { applyOfficialModelPassthrough, buildBridgeRoutingPolicy, readBridgeRoutingPolicy } from '../provider-route-policy.js';
14
14
  import { sha256Stable } from '../route-index.js';
15
15
  import { bridgeBaseUrl, commandResult, controllerEnv, controllerPaths, nowIso, providerCode, providerRegistrySnapshot, resolveRawCredentials, resolveValidatedCredentials, safeCode, serializedSettings, stringArray, timeoutMs, unique } from './shared.js';
16
16
  import { desktopBridgeStatusV3 } from './status.js';
@@ -76,12 +76,14 @@ export async function syncCatalogInternal(options) {
76
76
  ? previousDefault
77
77
  : historicalDefault && readyProviders.includes(historicalDefault) ? historicalDefault
78
78
  : readyProviders.length === 1 ? readyProviders[0] || null : null;
79
- const policy = buildBridgeRoutingPolicy({
79
+ const persistedSettings = await readDesktopBridgeServiceSettings(desktopBridgeServicePaths(paths.home).settings_path).catch(() => null);
80
+ const officialModelsMode = await resolveEffectiveOfficialModelsMode(persistedSettings?.official_passthrough, { home: paths.home, authPath: paths.authPath });
81
+ const policy = applyOfficialModelPassthrough(buildBridgeRoutingPolicy({
80
82
  route_index: build.route_index,
81
83
  catalog_generation: build.catalog.generation,
82
84
  default_provider_id: defaultProvider,
83
85
  changed_at: nowIso(options)
84
- });
86
+ }), { mode: officialModelsMode, changedAt: nowIso(options) });
85
87
  let settings;
86
88
  let managedBridgeBaseUrl;
87
89
  try {
@@ -2,10 +2,11 @@ import path from 'node:path';
2
2
  import { removeDesktopBridgeManagedConfig } from '../../../cli/install-helpers-codex-lb-config.js';
3
3
  import { safeWriteCodexConfigToml } from '../../codex-runtime/codex-desktop-config-policy.js';
4
4
  import { exists, readText } from '../../fsx.js';
5
- import { bootstrapExistingDesktopBridgeService, desktopBridgeServiceStatus, installAndStartDesktopBridgeService, stopDesktopBridgeService } from '../desktop-service.js';
5
+ import { bootstrapExistingDesktopBridgeService, desktopBridgeServicePaths, desktopBridgeServiceStatus, installAndStartDesktopBridgeService, readDesktopBridgeServiceSettings, resolveEffectiveOfficialModelsMode, stopDesktopBridgeService } from '../desktop-service.js';
6
6
  import { rollbackDesktopBridgeUnificationReceipt } from '../migration-receipt.js';
7
+ import { DESKTOP_BRIDGE_OFFICIAL_UPSTREAM_BASE_URL } from '../desktop-bridge/index.js';
7
8
  import { resolveBridgeRequestRoute } from '../request-route-resolver.js';
8
- import { setBridgeRoutingDefault, writeBridgeRoutingPolicy } from '../provider-route-policy.js';
9
+ import { applyOfficialModelPassthrough, buildBridgeRoutingPolicy, setBridgeRoutingDefault, writeBridgeRoutingPolicy } from '../provider-route-policy.js';
9
10
  import { syncCatalogInternal } from './catalog.js';
10
11
  import { commandResult, controllerPaths, nowIso, persistRuntimeSettings, providerCode, providerRegistrySnapshot, stringArray } from './shared.js';
11
12
  import { desktopBridgeStatusV3, loadCore, statusFromCore } from './status.js';
@@ -70,6 +71,39 @@ export async function setDefaultProvider(providerId, options) {
70
71
  const status = await desktopBridgeStatusV3(options);
71
72
  return commandResult('route.set-default', true, status, { provider_id: providerId, policy_generation: policy.policy_generation }, [], options);
72
73
  }
74
+ export async function setOfficialModelsMode(mode, options) {
75
+ const core = await loadCore(options);
76
+ if (!core.policy || !core.activeCatalog.ok)
77
+ throw new Error('bridge_route_policy_missing');
78
+ const settingsPath = options.settingsPath || desktopBridgeServicePaths(core.paths.home).settings_path;
79
+ const persisted = await readDesktopBridgeServiceSettings(settingsPath).catch(() => null);
80
+ const nextOfficial = {
81
+ enabled: persisted?.official_passthrough?.enabled ?? true,
82
+ base_url: persisted?.official_passthrough?.base_url || DESKTOP_BRIDGE_OFFICIAL_UPSTREAM_BASE_URL,
83
+ models: mode,
84
+ };
85
+ const effective = mode === 'auto'
86
+ ? await resolveEffectiveOfficialModelsMode(nextOfficial, { home: core.paths.home })
87
+ : mode;
88
+ const policy = effective === 'passthrough'
89
+ ? applyOfficialModelPassthrough(core.policy, { mode: 'passthrough', changedAt: nowIso(options) })
90
+ : buildBridgeRoutingPolicy({
91
+ route_index: core.activeCatalog.route_index,
92
+ catalog_generation: core.policy.catalog_generation,
93
+ default_provider_id: core.policy.default_provider_id,
94
+ changed_at: nowIso(options)
95
+ });
96
+ await writeBridgeRoutingPolicy(core.paths.routePolicyPath, policy, core.activeCatalog.route_index);
97
+ await persistRuntimeSettings({ ...core, policy, policyBlockers: [] }, {
98
+ ...options,
99
+ settings: { ...(options.settings || {}), official_passthrough: nextOfficial },
100
+ });
101
+ const status = await desktopBridgeStatusV3(options);
102
+ const officialModels = Object.entries(policy.model_routes)
103
+ .filter(([, route]) => route.provider_id === 'openai')
104
+ .map(([model]) => model);
105
+ return commandResult('route.official-models', true, status, { mode, effective_mode: effective, official_models: officialModels, policy_generation: policy.policy_generation }, [], options);
106
+ }
73
107
  export async function explainRoute(model, options) {
74
108
  const core = await loadCore(options);
75
109
  if (!core.policy || !core.activeCatalog.ok)
@@ -1,5 +1,5 @@
1
1
  import { listSelectableModels, selectExposedModels, syncCatalog } from './desktop-controller-v3/catalog.js';
2
- import { explainRoute, ensureDesktopBridge, repairDesktopBridge, rollbackDesktopBridge, setDefaultProvider, unmanageDesktopBridge } from './desktop-controller-v3/lifecycle-commands.js';
2
+ import { explainRoute, ensureDesktopBridge, repairDesktopBridge, rollbackDesktopBridge, setDefaultProvider, setOfficialModelsMode, unmanageDesktopBridge } from './desktop-controller-v3/lifecycle-commands.js';
3
3
  import { configureProvider, removeCredential, setProviderState, validateProvider } from './desktop-controller-v3/provider-commands.js';
4
4
  import { commandResult, safeCode } from './desktop-controller-v3/shared.js';
5
5
  import { desktopBridgeStatusV3 } from './desktop-controller-v3/status.js';
@@ -56,6 +56,8 @@ export async function executeDesktopBridgeCommandV3(request, options = {}) {
56
56
  }
57
57
  if (request.operation === 'route.set-default')
58
58
  return setDefaultProvider(request.provider_id, options);
59
+ if (request.operation === 'route.official-models')
60
+ return setOfficialModelsMode(request.mode, options);
59
61
  if (request.operation === 'route.explain')
60
62
  return explainRoute(request.model, options);
61
63
  if (request.operation === 'unmanage')
@@ -9,9 +9,11 @@ import { loadCodexLbEnv } from './codex-lb-env.js';
9
9
  import { resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
10
10
  import { cleanupRetiredDesktopBridgeRuntime, prepareRetiredDesktopBridgeRuntime, } from './desktop-bridge-migration/retired-runtime-cleanup.js';
11
11
  import { canonicalizeBridgeModelId, normalizeBridgeUpstreamModelId, sha256Stable } from './route-index.js';
12
+ import { captureCodexAuthSnapshot } from './desktop-auth-invariant.js';
13
+ import { applyOfficialModelPassthrough, bridgeRoutePolicyPath, writeBridgeRoutingPolicy } from './provider-route-policy.js';
12
14
  import { desktopBridgeRuntimeVersion, desktopBridgeRuntimeVersionStale } from './desktop-bridge/state.js';
13
15
  import { PACKAGE_VERSION } from '../version.js';
14
- import { DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES, DESKTOP_BRIDGE_LAUNCHD_LABEL, desktopBridgeConfigGeneration, desktopBridgeLaunchdPlistPath, desktopBridgeProcessExists, desktopBridgeStatePath, getDesktopBridgeStatus, preflightDesktopBridge, readDesktopBridgeState, safeBridgeErrorCode, writeDesktopBridgeLaunchdPlist, selectAvailableDesktopBridgePort, startPreparedDesktopBridge, DESKTOP_BRIDGE_STATE_SCHEMA, DesktopBridgeError, } from './desktop-bridge/index.js';
16
+ import { DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES, DESKTOP_BRIDGE_LAUNCHD_LABEL, desktopBridgeConfigGeneration, desktopBridgeLaunchdPlistPath, desktopBridgeProcessExists, desktopBridgeStatePath, getDesktopBridgeStatus, preflightDesktopBridge, readDesktopBridgeState, safeBridgeErrorCode, writeDesktopBridgeLaunchdPlist, selectAvailableDesktopBridgePort, startPreparedDesktopBridge, DESKTOP_BRIDGE_STATE_SCHEMA, DESKTOP_BRIDGE_OFFICIAL_UPSTREAM_BASE_URL, DesktopBridgeError, } from './desktop-bridge/index.js';
15
17
  export const DEFAULT_DESKTOP_BRIDGE_HOST = '127.0.0.1';
16
18
  export const DEFAULT_DESKTOP_BRIDGE_PORT = 49_152;
17
19
  export const DESKTOP_BRIDGE_SETTINGS_SCHEMA = 'sks.desktop-bridge-settings.v2';
@@ -159,7 +161,7 @@ export async function writeDesktopBridgeServiceSettings(file, settings) {
159
161
  }
160
162
  async function writeDesktopBridgeServiceSettingsUnlocked(file, settings) {
161
163
  const validated = validateDesktopBridgeServiceSettings(settings);
162
- const persisted = { schema: validated.schema, listen_host: validated.listen_host, listen_port: validated.listen_port, provider_registry: validated.provider_registry, route_policy: validated.route_policy, provider_session_pins: validated.provider_session_pins, client_capability_sha256: validated.client_capability_sha256, allowed_origins: validated.allowed_origins, connect_timeout_ms: validated.connect_timeout_ms, idle_timeout_ms: validated.idle_timeout_ms };
164
+ const persisted = { schema: validated.schema, listen_host: validated.listen_host, listen_port: validated.listen_port, provider_registry: validated.provider_registry, route_policy: validated.route_policy, provider_session_pins: validated.provider_session_pins, client_capability_sha256: validated.client_capability_sha256, allowed_origins: validated.allowed_origins, connect_timeout_ms: validated.connect_timeout_ms, idle_timeout_ms: validated.idle_timeout_ms, official_passthrough: validated.official_passthrough };
163
165
  await writeTextAtomic(file, `${JSON.stringify(persisted, null, 2)}\n`, { mode: 0o600 });
164
166
  await fsp.chmod(file, 0o600);
165
167
  }
@@ -246,11 +248,13 @@ export async function resolveDesktopBridgeRuntimeConfig(options = {}) {
246
248
  if (!Object.keys(settings.route_policy.model_routes).length)
247
249
  throw new Error('catalog_model_route_missing');
248
250
  const enabledRoutes = new Set(Object.values(settings.route_policy.model_routes).map((route) => route.provider_id));
249
- if (![...enabledRoutes].some((id) => credentials.registry.providers[id].credential_state === 'ready'))
251
+ const providerRouteIds = [...enabledRoutes].filter((id) => id === 'codex-lb' || id === 'openrouter');
252
+ const officialOnly = providerRouteIds.length === 0 && settings.official_passthrough.enabled;
253
+ if (!officialOnly && !providerRouteIds.some((id) => credentials.registry.providers[id].credential_state === 'ready'))
250
254
  throw new Error('desktop_bridge_provider_credentials_unavailable');
251
255
  const persistProviderSessionPins = options.persistProviderSessionPins
252
256
  || ((pins) => persistDesktopBridgeSessionPins(paths.settings_path, settings, pins));
253
- const config = { providerRegistry: settings.provider_registry, routePolicy: settings.route_policy, providerSessionPins: settings.provider_session_pins, ...(options.resolveRequestRoute ? { resolveRequestRoute: options.resolveRequestRoute } : {}), persistProviderSessionPins, resolveProviderCredential: credentials.resolver, clientCapabilitySha256: settings.client_capability_sha256, listenHost: settings.listen_host, listenPort: settings.listen_port, allowedPathPrefixes: DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES, allowedOrigins: settings.allowed_origins, connectTimeoutMs: settings.connect_timeout_ms, idleTimeoutMs: settings.idle_timeout_ms };
257
+ const config = { providerRegistry: settings.provider_registry, routePolicy: settings.route_policy, providerSessionPins: settings.provider_session_pins, ...(options.resolveRequestRoute ? { resolveRequestRoute: options.resolveRequestRoute } : {}), persistProviderSessionPins, resolveProviderCredential: credentials.resolver, clientCapabilitySha256: settings.client_capability_sha256, listenHost: settings.listen_host, listenPort: settings.listen_port, allowedPathPrefixes: DESKTOP_BRIDGE_ALLOWED_PATH_PREFIXES, allowedOrigins: settings.allowed_origins, connectTimeoutMs: settings.connect_timeout_ms, idleTimeoutMs: settings.idle_timeout_ms, officialPassthrough: settings.official_passthrough.enabled ? { baseUrl: settings.official_passthrough.base_url } : null };
254
258
  const primary = credentials.sources['codex-lb'] || credentials.sources.openrouter;
255
259
  if (!primary)
256
260
  throw new Error('desktop_bridge_provider_credentials_unavailable');
@@ -434,24 +438,67 @@ async function installedPackageVersion() {
434
438
  return null;
435
439
  }
436
440
  }
441
+ export const DESKTOP_BRIDGE_SKEW_RESTART_COOLDOWN_MS = 30 * 60_000;
442
+ export function desktopBridgeSkewRestartSuppressed(marker, running, installed, nowMs) {
443
+ return Boolean(marker
444
+ && marker.running === running
445
+ && marker.installed === installed
446
+ && nowMs - Date.parse(marker.at) < DESKTOP_BRIDGE_SKEW_RESTART_COOLDOWN_MS);
447
+ }
448
+ async function readSkewRestartMarker(file) {
449
+ try {
450
+ const parsed = JSON.parse(await fsp.readFile(file, 'utf8'));
451
+ if (typeof parsed.running !== 'string' || typeof parsed.installed !== 'string' || !Number.isFinite(Date.parse(String(parsed.at))))
452
+ return null;
453
+ return { running: parsed.running, installed: parsed.installed, at: String(parsed.at) };
454
+ }
455
+ catch {
456
+ return null;
457
+ }
458
+ }
459
+ async function autoApplyOfficialModelsAtServe(runtime, home, options) {
460
+ const settings = runtime.settings;
461
+ const mode = await resolveEffectiveOfficialModelsMode(settings.official_passthrough, { home });
462
+ if (mode !== 'passthrough')
463
+ return;
464
+ const flipped = applyOfficialModelPassthrough(settings.route_policy, { mode: 'passthrough' });
465
+ if (flipped.policy_generation === settings.route_policy.policy_generation)
466
+ return;
467
+ const nextSettings = { ...settings, route_policy: flipped };
468
+ await writeDesktopBridgeServiceSettings(options.settingsPath || desktopBridgeServicePaths(home).settings_path, nextSettings);
469
+ await writeBridgeRoutingPolicy(bridgeRoutePolicyPath(path.join(path.resolve(home), '.codex')), flipped).catch(() => undefined);
470
+ settings.route_policy = flipped;
471
+ runtime.config.routePolicy = flipped;
472
+ process.stdout.write(`${JSON.stringify({ schema: 'sks.desktop-bridge-log.v2', event: 'sks.desktop_bridge.official_models_auto_applied', at: new Date().toISOString(), sks_version: PACKAGE_VERSION, mode: 'passthrough', policy_generation: flipped.policy_generation, secret_fields_redacted: true })}\n`);
473
+ }
437
474
  export async function serveDesktopBridge(options = {}) {
438
475
  let handle = null;
439
476
  try {
440
477
  const runtime = await resolveDesktopBridgeRuntimeConfig(options);
478
+ const serveHome = options.home || options.env?.HOME || process.env.HOME || os.homedir();
479
+ await autoApplyOfficialModelsAtServe(runtime, serveHome, options);
441
480
  const supervised = desktopBridgeIsSupervised();
481
+ const skewMarkerPath = path.join(path.dirname(runtime.paths.state_path), 'desktop-bridge-skew-restart.json');
442
482
  handle = await startPreparedDesktopBridge(await preflightDesktopBridge(runtime.config), {
443
483
  statePath: runtime.paths.state_path,
444
484
  versionSkew: {
445
485
  readInstalledVersion: installedPackageVersion,
446
486
  onSkew: (installedVersion) => {
447
- process.stdout.write(`${JSON.stringify({ schema: 'sks.desktop-bridge-log.v2', event: 'sks.desktop_bridge.version_skew', running: PACKAGE_VERSION, installed: installedVersion, supervised, action: supervised ? 'restarting' : 'logged_only', secret_fields_redacted: true })}\n`);
448
- if (!supervised)
449
- return;
450
- void handle?.stop().catch(() => undefined).finally(() => process.exit(64));
487
+ void (async () => {
488
+ const marker = await readSkewRestartMarker(skewMarkerPath);
489
+ const cooldownActive = desktopBridgeSkewRestartSuppressed(marker, PACKAGE_VERSION, installedVersion, Date.now());
490
+ const action = !supervised ? 'logged_only' : cooldownActive ? 'suppressed_cooldown' : 'restarting';
491
+ process.stdout.write(`${JSON.stringify({ schema: 'sks.desktop-bridge-log.v2', event: 'sks.desktop_bridge.version_skew', at: new Date().toISOString(), running: PACKAGE_VERSION, installed: installedVersion, supervised, action, secret_fields_redacted: true })}\n`);
492
+ if (!supervised || cooldownActive)
493
+ return;
494
+ await writeTextAtomic(skewMarkerPath, `${JSON.stringify({ running: PACKAGE_VERSION, installed: installedVersion, at: new Date().toISOString() })}\n`, { mode: 0o600 }).catch(() => undefined);
495
+ await handle?.stop().catch(() => undefined);
496
+ process.exit(64);
497
+ })();
451
498
  },
452
499
  },
453
500
  });
454
- process.stdout.write(`${JSON.stringify({ schema: 'sks.desktop-bridge-log.v2', event: 'sks.desktop_bridge.started', pid: handle.state.pid, process_generation: handle.state.schema === DESKTOP_BRIDGE_STATE_SCHEMA ? handle.state.process_generation : null, provider_registry_generation: runtime.config.providerRegistry?.generation, route_policy_generation: runtime.config.routePolicy?.policy_generation, secret_fields_redacted: true })}\n`);
501
+ process.stdout.write(`${JSON.stringify({ schema: 'sks.desktop-bridge-log.v2', event: 'sks.desktop_bridge.started', at: new Date().toISOString(), sks_version: PACKAGE_VERSION, pid: handle.state.pid, process_generation: handle.state.schema === DESKTOP_BRIDGE_STATE_SCHEMA ? handle.state.process_generation : null, provider_registry_generation: runtime.config.providerRegistry?.generation, route_policy_generation: runtime.config.routePolicy?.policy_generation, secret_fields_redacted: true })}\n`);
455
502
  await waitForShutdown(handle);
456
503
  return { schema: 'sks.desktop-bridge-serve.v1', ok: true, status: 'stopped', state: handle.state };
457
504
  }
@@ -470,7 +517,7 @@ function validateDesktopBridgeServiceSettings(value) {
470
517
  const input = row;
471
518
  if (input.schema !== DESKTOP_BRIDGE_SETTINGS_SCHEMA)
472
519
  throw new Error('desktop_bridge_settings_schema_invalid');
473
- const allowedKeys = new Set(['schema', 'listen_host', 'listen_port', 'provider_registry', 'route_policy', 'provider_session_pins', 'client_capability_sha256', 'allowed_origins', 'connect_timeout_ms', 'idle_timeout_ms']);
520
+ const allowedKeys = new Set(['schema', 'listen_host', 'listen_port', 'provider_registry', 'route_policy', 'provider_session_pins', 'client_capability_sha256', 'allowed_origins', 'connect_timeout_ms', 'idle_timeout_ms', 'official_passthrough']);
474
521
  if (Object.keys(input).some((key) => !allowedKeys.has(key)))
475
522
  throw new Error('desktop_bridge_settings_unknown_field');
476
523
  const serialized = JSON.stringify(input);
@@ -502,7 +549,44 @@ function validateDesktopBridgeServiceSettings(value) {
502
549
  throw new Error('desktop_bridge_settings_connect_timeout_invalid');
503
550
  if (!Number.isFinite(idle) || idle < 1_000 || idle > 86_400_000)
504
551
  throw new Error('desktop_bridge_settings_idle_timeout_invalid');
505
- return { schema: DESKTOP_BRIDGE_SETTINGS_SCHEMA, listen_host: host, listen_port: port, provider_registry: registry, route_policy: policy, provider_session_pins: pins, client_capability_sha256: clientCapabilitySha256, allowed_origins: [...new Set(origins)], connect_timeout_ms: connect, idle_timeout_ms: idle };
552
+ const officialPassthrough = validateOfficialPassthroughSettings(input.official_passthrough);
553
+ return { schema: DESKTOP_BRIDGE_SETTINGS_SCHEMA, listen_host: host, listen_port: port, provider_registry: registry, route_policy: policy, provider_session_pins: pins, client_capability_sha256: clientCapabilitySha256, allowed_origins: [...new Set(origins)], connect_timeout_ms: connect, idle_timeout_ms: idle, official_passthrough: officialPassthrough };
554
+ }
555
+ function validateOfficialPassthroughSettings(value) {
556
+ if (value === undefined || value === null) {
557
+ return { enabled: true, base_url: DESKTOP_BRIDGE_OFFICIAL_UPSTREAM_BASE_URL, models: 'auto' };
558
+ }
559
+ if (typeof value !== 'object' || Array.isArray(value))
560
+ throw new Error('desktop_bridge_settings_official_passthrough_invalid');
561
+ const row = value;
562
+ if (Object.keys(row).some((key) => key !== 'enabled' && key !== 'base_url' && key !== 'models'))
563
+ throw new Error('desktop_bridge_settings_official_passthrough_invalid');
564
+ const enabled = row.enabled === true;
565
+ const baseUrl = typeof row.base_url === 'string' && row.base_url.trim() ? row.base_url.trim() : DESKTOP_BRIDGE_OFFICIAL_UPSTREAM_BASE_URL;
566
+ try {
567
+ new URL(baseUrl);
568
+ }
569
+ catch {
570
+ throw new Error('desktop_bridge_settings_official_passthrough_invalid');
571
+ }
572
+ const models = row.models === 'passthrough' || row.models === 'gateway' || row.models === 'auto'
573
+ ? row.models
574
+ : row.models === undefined ? 'auto' : null;
575
+ if (models === null)
576
+ throw new Error('desktop_bridge_settings_official_passthrough_invalid');
577
+ return { enabled, base_url: baseUrl, models };
578
+ }
579
+ export async function resolveEffectiveOfficialModelsMode(official, input) {
580
+ if (official && official.enabled === false)
581
+ return 'gateway';
582
+ const models = official?.models || 'auto';
583
+ if (models === 'passthrough' || models === 'gateway')
584
+ return models;
585
+ const snapshot = await captureCodexAuthSnapshot({
586
+ home: input.home,
587
+ ...(input.authPath ? { authPath: input.authPath } : {}),
588
+ }).catch(() => null);
589
+ return snapshot && (snapshot.mode === 'chatgpt_oauth' || snapshot.mode === 'mixed') ? 'passthrough' : 'gateway';
506
590
  }
507
591
  function validateProviderSessionPins(value) {
508
592
  const pins = Array.isArray(value) ? value : [];
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
+ import { BRIDGE_OFFICIAL_ROUTE_ID } from './bridge-contracts.js';
4
5
  import { canonicalizeBridgeModelId, normalizeBridgeUpstreamModelId, sha256Stable } from './route-index.js';
5
6
  import { writeJsonAtomic } from '../fsx.js';
6
7
  import { withFileLock } from '../locks/file-lock.js';
@@ -58,6 +59,8 @@ export function validateBridgeRoutingPolicy(policy, routeIndex) {
58
59
  blockers.push('bridge_route_policy_model_invalid');
59
60
  if (routeIndex) {
60
61
  for (const [model, target] of Object.entries(canonical)) {
62
+ if (target.provider_id === BRIDGE_OFFICIAL_ROUTE_ID)
63
+ continue;
61
64
  const indexed = routeIndex.routes[model];
62
65
  if (!indexed || !sameTarget(indexed, target))
63
66
  blockers.push('bridge_route_policy_route_index_mismatch');
@@ -101,7 +104,7 @@ function canonicalRoutes(routes) {
101
104
  for (const [model, target] of Object.entries(routes)) {
102
105
  const canonicalModel = canonicalizeBridgeModelId(model);
103
106
  const upstreamModel = normalizeBridgeUpstreamModelId(target.upstream_model);
104
- if (!canonicalModel || !upstreamModel || !isProviderId(target.provider_id))
107
+ if (!canonicalModel || !upstreamModel || !isRouteTargetId(target.provider_id))
105
108
  continue;
106
109
  rows.push([canonicalModel, { provider_id: target.provider_id, upstream_model: upstreamModel }]);
107
110
  }
@@ -110,6 +113,32 @@ function canonicalRoutes(routes) {
110
113
  function isProviderId(value) {
111
114
  return value === 'codex-lb' || value === 'openrouter';
112
115
  }
116
+ function isRouteTargetId(value) {
117
+ return isProviderId(value) || value === BRIDGE_OFFICIAL_ROUTE_ID;
118
+ }
119
+ export const OFFICIAL_MODEL_ID_PATTERN = /^(?:gpt-[0-9]|o[0-9]|codex-mini)/;
120
+ export function applyOfficialModelPassthrough(policy, input = { mode: 'passthrough' }) {
121
+ if (input.mode === 'gateway')
122
+ return policy;
123
+ const routes = {};
124
+ for (const [model, target] of Object.entries(policy.model_routes)) {
125
+ routes[model] = OFFICIAL_MODEL_ID_PATTERN.test(model) && !model.includes(':')
126
+ ? { provider_id: BRIDGE_OFFICIAL_ROUTE_ID, upstream_model: model }
127
+ : target;
128
+ }
129
+ const semantic = {
130
+ default_provider_id: policy.default_provider_id,
131
+ fallback: 'none',
132
+ model_routes: canonicalRoutes(routes),
133
+ catalog_generation: policy.catalog_generation
134
+ };
135
+ return {
136
+ schema: 'sks.bridge-routing-policy.v1',
137
+ ...semantic,
138
+ policy_generation: sha256Stable(semantic),
139
+ changed_at: input.changedAt || new Date().toISOString()
140
+ };
141
+ }
113
142
  function sameTarget(left, right) {
114
143
  return left.provider_id === right.provider_id && left.upstream_model === right.upstream_model;
115
144
  }
@@ -51,6 +51,20 @@ export function resolveBridgeRequestRoute(request, policy, options) {
51
51
  const policyTarget = policy.model_routes[model];
52
52
  if (!indexed || !policyTarget)
53
53
  return blocked(base, 'catalog_model_route_missing');
54
+ if (policyTarget.provider_id === 'openai') {
55
+ return {
56
+ ...base,
57
+ ok: true,
58
+ route: policyTarget,
59
+ endpoint_url: null,
60
+ source: 'route_index',
61
+ proposed_session_pin: null,
62
+ blockers: [],
63
+ recovery_action: null
64
+ };
65
+ }
66
+ if (indexed.provider_id === 'openai')
67
+ return blocked(base, 'catalog_route_provider_unknown');
54
68
  if (!sameTarget(indexed, policyTarget))
55
69
  return blocked(base, 'bridge_route_policy_route_index_mismatch');
56
70
  if (pinClaim && !sameTarget(indexed, pinClaim))
@@ -18,6 +18,28 @@ import { MAX_LIFECYCLE_THREADS } from './subagent-skill-availability-contract.js
18
18
  import { officialSubagentEvidenceReady, terminalBlockedNarutoGate } from '../subagents/terminal-subagent-state.js';
19
19
  const SUBAGENT_LIFECYCLE_CAPTURE_FAILURE_SCHEMA = 'sks.subagent-lifecycle-capture-failure.v1';
20
20
  const MAX_SUBAGENT_LIFECYCLE_CAPTURE_FAILURES = 528;
21
+ export const ACTIVE_OFFICIAL_WORKFLOW_IDLE_MS = 2 * 60 * 60 * 1000;
22
+ function parseIsoMs(value) {
23
+ const parsed = Date.parse(String(value || ''));
24
+ return Number.isFinite(parsed) ? parsed : 0;
25
+ }
26
+ export function officialWorkflowLastActivityMs(input) {
27
+ const times = [
28
+ parseIsoMs(input.plan?.created_at),
29
+ parseIsoMs(input.plan?.updated_at),
30
+ parseIsoMs(input.plan?.wave_lifecycle?.updated_at),
31
+ parseIsoMs(input.plan?.wave_lifecycle?.created_at),
32
+ parseIsoMs(input.gate?.updated_at),
33
+ parseIsoMs(input.gate?.created_at),
34
+ ...(input.events || []).map((event) => parseIsoMs(event.occurred_at ?? event.ts))
35
+ ];
36
+ return times.reduce((max, value) => (value > max ? value : max), 0);
37
+ }
38
+ export function officialWorkflowIsIdle(activityMs, nowMs = Date.now()) {
39
+ if (activityMs <= 0)
40
+ return true;
41
+ return nowMs - activityMs > ACTIVE_OFFICIAL_WORKFLOW_IDLE_MS;
42
+ }
21
43
  export async function inspectActiveOfficialSubagentWorkflow(root, state, sessionKey = null) {
22
44
  const missionId = String(state?.mission_id || '').trim();
23
45
  const workflowRunId = String(state?.official_subagent_run_id || '').trim();
@@ -71,8 +93,14 @@ export async function inspectActiveOfficialSubagentWorkflow(root, state, session
71
93
  if (events.length > 0 && openThreads !== liveThreads.size) {
72
94
  return { status: 'invalid', missionId, workflowRunId, reason: 'active_lifecycle_event_mismatch' };
73
95
  }
96
+ if (officialWorkflowIsIdle(officialWorkflowLastActivityMs({ plan, gate, events }))) {
97
+ return { status: 'inactive' };
98
+ }
74
99
  return { status: 'active', missionId, workflowRunId, openThreads };
75
100
  }
101
+ if (officialWorkflowIsIdle(officialWorkflowLastActivityMs({ plan, gate, events }))) {
102
+ return { status: 'inactive' };
103
+ }
76
104
  return { status: 'active', missionId, workflowRunId, openThreads: liveThreads.size };
77
105
  }
78
106
  catch {
@@ -2,7 +2,7 @@ import fsp from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { projectRoot, readJson, writeJsonAtomic, writeTextAtomic, appendJsonl, nowIso, sha256, packageRoot } from './fsx.js';
4
4
  import { looksInteractiveCommand, interactiveCommandReason } from './no-question-guard.js';
5
- import { loadStateForSession, missionDir, sessionStateKey, setCurrent, stateFileForSession } from './mission.js';
5
+ import { loadOwnedRouteState, missionDir, sessionStateKey, setCurrent, stateFileForSession } from './mission.js';
6
6
  import { checkDbOperation, dbBlockReason, handleMadSksUserConfirmation } from './db-safety.js';
7
7
  import { maybeRecordMadSksSqlPlaneToolResultFromToolUse } from './mad-sks/sql-plane/result-lifecycle.js';
8
8
  import { checkHarnessModification, harnessGuardBlockReason, isHarnessSourceProject } from './harness-guard.js';
@@ -53,7 +53,7 @@ export { selftestCodexCommitHooks } from './hooks-runtime/codex-commit-hooks-sel
53
53
  async function loadState(root, payload = {}) {
54
54
  const sessionKey = conversationId(payload);
55
55
  if (!explicitConversationId(payload))
56
- return loadStateForSession(root, sessionKey);
56
+ return loadOwnedRouteState(root);
57
57
  const hashed = sessionStateKey(sessionKey);
58
58
  const sessionState = await readJson(stateFileForSession(root, sessionKey), null).catch(() => null);
59
59
  return sessionState ? { ...sessionState, _session_key: sessionState._session_key || hashed } : {};
@@ -18,7 +18,7 @@ export async function resolveDesktopBridgeImagegenTarget(opts = {}) {
18
18
  const route = model && policy?.fallback === 'none'
19
19
  ? policy.model_routes?.[model] || null
20
20
  : null;
21
- const provider = route ? status?.providers?.[route.provider_id] || null : null;
21
+ const provider = route && route.provider_id !== 'openai' ? status?.providers?.[route.provider_id] || null : null;
22
22
  const providerImagegen = provider?.capabilities?.capabilities?.image_generation || null;
23
23
  const blocker = !status || status.schema !== 'sks.desktop-bridge-status.v3'
24
24
  ? 'desktop_bridge_status_unavailable'
@@ -50,7 +50,7 @@ export async function resolveDesktopBridgeImagegenTarget(opts = {}) {
50
50
  model,
51
51
  model_source: model ? 'explicit' : null,
52
52
  route: route || null,
53
- provider_id: route?.provider_id || null,
53
+ provider_id: route && route.provider_id !== 'openai' ? route.provider_id : null,
54
54
  status_source: injected ? 'injected_fixture' : 'runtime',
55
55
  live_evidence_allowed: bridgeVerified && !injected,
56
56
  blocker,