sneakoscope 10.7.0 → 10.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -5
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/cli/command-manifest-lite.js +1 -1
- package/dist/cli/install-helpers-codex-lb-config.js +55 -20
- package/dist/commands/bridge.js +14 -1
- package/dist/commands/codex.js +5 -1
- package/dist/commands/doctor-console.js +2 -0
- package/dist/commands/doctor.js +8 -9
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/agents/agent-role-config.js +6 -2
- package/dist/core/agents/sks-model-pin-hygiene.js +150 -0
- package/dist/core/codex/codex-config-guard.js +18 -13
- package/dist/core/codex-app/codex-app-ui-state-snapshot.js +7 -1
- package/dist/core/codex-app/menubar/constants.js +0 -1
- package/dist/core/codex-lb/bridge-contracts.js +5 -0
- package/dist/core/codex-lb/combined-catalog/builder.js +4 -2
- package/dist/core/codex-lb/combined-catalog/latest-generation.js +7 -34
- package/dist/core/codex-lb/combined-catalog/normalize.js +3 -1
- package/dist/core/codex-lb/combined-catalog/persistence.js +4 -0
- package/dist/core/codex-lb/combined-catalog/storage.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/codex-lb-turn-state.js +35 -0
- package/dist/core/codex-lb/desktop-bridge/compaction-adapter.js +3 -0
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +10 -4
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +138 -36
- package/dist/core/codex-lb/desktop-bridge/models-endpoint.js +62 -0
- package/dist/core/codex-lb/desktop-bridge/provider-handoff.js +88 -0
- package/dist/core/codex-lb/desktop-bridge/provider-request-compat.js +89 -0
- package/dist/core/codex-lb/desktop-bridge/rejection-log.js +61 -0
- package/dist/core/codex-lb/desktop-bridge/responses-websocket.js +108 -16
- package/dist/core/codex-lb/desktop-bridge/security.js +101 -55
- package/dist/core/codex-lb/desktop-bridge/server.js +15 -2
- package/dist/core/codex-lb/desktop-bridge/types.js +3 -0
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +3 -0
- package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +2 -6
- package/dist/core/codex-lb/desktop-bridge-migration.js +14 -2
- package/dist/core/codex-lb/desktop-controller-v3/catalog.js +125 -3
- package/dist/core/codex-lb/desktop-controller-v3/model-refresh.js +140 -0
- package/dist/core/codex-lb/desktop-controller-v3/openrouter-only-commands.js +5 -5
- package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +14 -11
- package/dist/core/codex-lb/desktop-controller-v3/status.js +2 -3
- package/dist/core/codex-lb/desktop-controller-v3.js +3 -1
- package/dist/core/codex-lb/desktop-service.js +51 -17
- package/dist/core/codex-lb/model-refresh-after.js +16 -0
- package/dist/core/codex-lb/request-route-resolver.js +1 -1
- package/dist/core/codex-runtime/codex-desktop-config-policy.js +0 -1
- package/dist/core/commands/basic-cli.js +5 -0
- package/dist/core/commands/menubar-command.js +5 -1
- package/dist/core/doctor/desktop-bridge-catalog-repair.js +20 -17
- package/dist/core/hooks-runtime/jev-spawn-routing.js +1 -5
- package/dist/core/hooks-runtime/parent-orchestration-gate.js +6 -8
- package/dist/core/hooks-runtime.js +13 -9
- package/dist/core/init/skills.js +22 -3
- package/dist/core/provider/provider-context.js +4 -1
- package/dist/core/search/context.js +2 -2
- package/dist/core/subagents/model-tiers.js +19 -3
- package/dist/core/update/update-migration-state/desktop-bridge-catalog-repair-stage.js +4 -3
- package/dist/core/update/update-migration-state.js +10 -40
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +0 -1
- package/dist/native/sks-menubar/Sources/OverviewSummary.swift +32 -0
- package/dist/native/sks-menubar/Sources/OverviewViewController.swift +15 -5
- package/dist/native/sks-menubar/Sources/SidebarItem.swift +0 -2
- package/dist/native/sks-menubar/Sources/UpdatesViewController.swift +3 -0
- package/package.json +2 -2
- package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +0 -49
package/README.md
CHANGED
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
Sneakoscope Codex (`sks`) is an open-source local tool for the Codex CLI and Codex desktop app. It adds a macOS control center, bounded agent workflows, repository context, and evidence checks for SKS releases.
|
|
18
18
|
<!-- END SKS SEARCH VISIBILITY MARKETING -->
|
|
19
19
|
|
|
20
|
-
Current package: **SKS 10.7.
|
|
20
|
+
Current package: **SKS 10.7.2**, prepared for npm publication.
|
|
21
21
|
[Latest release notes](https://github.com/mandarange/Sneakoscope-Codex/releases/latest)
|
|
22
|
+
· [npm package](https://www.npmjs.com/package/sneakoscope)
|
|
22
23
|
· [Changelog](CHANGELOG.md). Install the latest stable release from npm.
|
|
23
24
|
|
|
24
25
|
Ordinary work stays with the main agent. Optional Jev decisions use OpenRouter
|
|
@@ -132,9 +133,13 @@ sks doctor --json
|
|
|
132
133
|
|
|
133
134
|
`sks update` updates the **global** SKS package, verifies the installed command,
|
|
134
135
|
and reconciles SKS-managed skills, hooks, configuration and SKS Center where
|
|
135
|
-
available.
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
available. Cleanup follows the current package's manifest and ownership markers
|
|
137
|
+
regardless of the previous version, including mixed leftovers from interrupted
|
|
138
|
+
installs or a missing old generation manifest. Running it again when already
|
|
139
|
+
current still repairs the managed setup. It also repairs the known ignored
|
|
140
|
+
Codex settings and refreshes managed guidance. User-authored text, credentials
|
|
141
|
+
and explicit preferences are preserved. A required cleanup failure blocks
|
|
142
|
+
completion and is reported with its recovery action.
|
|
138
143
|
|
|
139
144
|
To check available updates first, run `sks update check`. For a repair without
|
|
140
145
|
requesting a newer package, run `sks doctor --fix` yourself in the terminal.
|
|
@@ -155,7 +160,24 @@ references and rebuilds TriWiki. Update and user-run `sks doctor --fix` share
|
|
|
155
160
|
that maintenance and repair known ignored configuration settings. User text
|
|
156
161
|
and model choices are preserved. See [harness maintenance](docs/align-modernization.md).
|
|
157
162
|
|
|
158
|
-
### New in 10.
|
|
163
|
+
### New in 10.7.0
|
|
164
|
+
|
|
165
|
+
- **A smaller command surface:** duplicate and unused commands were removed;
|
|
166
|
+
use `sks help <command>` for the supported command path.
|
|
167
|
+
- **Project-aware verification:** Plans and Proof Field reports use the target
|
|
168
|
+
project's own package scripts and checks instead of SKS maintainer commands.
|
|
169
|
+
- **Project-owned marketing:** SEO/GEO research and apply flows describe the
|
|
170
|
+
project they run in and preserve an existing project description.
|
|
171
|
+
- **Essential-profile guidance:** read-only paths stay lightweight while
|
|
172
|
+
safety checks remain active; destructive defaults and misleading proof paths
|
|
173
|
+
are refused.
|
|
174
|
+
- **Faster read-only commands:** status and help paths no longer load unrelated
|
|
175
|
+
update-migration machinery.
|
|
176
|
+
|
|
177
|
+
These changes are product behavior and validation improvements; they do not
|
|
178
|
+
claim a measured model-quality or general speed increase.
|
|
179
|
+
|
|
180
|
+
### Earlier highlights: 10.5.7
|
|
159
181
|
|
|
160
182
|
- **Jev execution profiles:** a decision can propose a bounded execution plan.
|
|
161
183
|
The default policy is `baseline`; fast profiles require `optimize` and an
|
|
@@ -273,7 +273,7 @@ const COMMAND_HELP_LITE = {
|
|
|
273
273
|
},
|
|
274
274
|
doctor: {
|
|
275
275
|
usage: 'sks doctor [--fix] [--local-only] [--json] [--install-scope global|project]',
|
|
276
|
-
description: 'Check and repair SKS generated files, while blocking setup if another Codex harness is detected.'
|
|
276
|
+
description: 'Check and repair SKS generated files, while blocking setup if another Codex harness is detected. Every run refreshes the Desktop Bridge model list; without --fix it writes only SKS\'s own catalog files, never ~/.codex/config.toml.'
|
|
277
277
|
},
|
|
278
278
|
git: {
|
|
279
279
|
usage: 'sks git policy|install|status|doctor|precommit|publish-plan|summary [--json]',
|
|
@@ -1,26 +1,57 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { ensureTrailingNewline, upsertTopLevelTomlString } from '../core/codex-runtime/codex-desktop-config-policy.js';
|
|
3
3
|
import { escapeRegExp } from '../core/text/regex.js';
|
|
4
|
+
export function codexReadsBridgeModels(text) {
|
|
5
|
+
const source = String(text || '');
|
|
6
|
+
return topLevelHasLine(source, DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER)
|
|
7
|
+
&& Boolean(topLevelTomlString(source, 'openai_base_url'))
|
|
8
|
+
&& !topLevelTomlString(source, 'model_catalog_json');
|
|
9
|
+
}
|
|
10
|
+
export function hasUserOwnedModelCatalog(text) {
|
|
11
|
+
const source = String(text || '');
|
|
12
|
+
return Boolean(topLevelTomlString(source, 'model_catalog_json'))
|
|
13
|
+
&& !topLevelHasLine(source, DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER);
|
|
14
|
+
}
|
|
15
|
+
export function setDesktopBridgeManagedModelCatalog(text, combinedCatalogPath) {
|
|
16
|
+
const source = String(text || '');
|
|
17
|
+
if (!topLevelHasLine(source, DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER) || hasUserOwnedModelCatalog(source))
|
|
18
|
+
return source;
|
|
19
|
+
if (combinedCatalogPath === null) {
|
|
20
|
+
if (!topLevelTomlString(source, 'model_catalog_json'))
|
|
21
|
+
return source;
|
|
22
|
+
return ensureTrailingNewline(removeTopLevelLine(removeTopLevelTomlKey(source, 'model_catalog_json'), DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER));
|
|
23
|
+
}
|
|
24
|
+
assertCombinedCatalogPath(combinedCatalogPath);
|
|
25
|
+
if (topLevelTomlString(source, 'model_catalog_json') === combinedCatalogPath)
|
|
26
|
+
return source;
|
|
27
|
+
const next = upsertTopLevelTomlString(source, 'model_catalog_json', combinedCatalogPath);
|
|
28
|
+
return ensureTrailingNewline(addTopLevelMarkerBeforeKey(next, 'model_catalog_json', DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER));
|
|
29
|
+
}
|
|
30
|
+
function assertCombinedCatalogPath(combinedCatalogPath) {
|
|
31
|
+
if (!combinedCatalogPath || !path.isAbsolute(combinedCatalogPath)
|
|
32
|
+
|| path.basename(combinedCatalogPath) !== 'sks-bridge-catalog.json') {
|
|
33
|
+
throw new Error('desktop_bridge_combined_catalog_path_invalid');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
4
36
|
export const DESKTOP_BRIDGE_MANAGED_MARKER = '# sks-desktop-bridge-managed';
|
|
5
37
|
export const DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER = '# sks-desktop-bridge-managed-base-url';
|
|
6
38
|
export const DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER = '# sks-desktop-bridge-managed-model-catalog';
|
|
7
|
-
export function
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
return
|
|
11
|
-
if (
|
|
12
|
-
return
|
|
13
|
-
return upsertTopLevelTomlString(
|
|
39
|
+
export function repairManagedCodexLbClientSelection(text) {
|
|
40
|
+
const source = String(text || '');
|
|
41
|
+
if (topLevelTomlString(source, 'model_provider') !== 'codex-lb')
|
|
42
|
+
return source;
|
|
43
|
+
if (!topLevelHasLine(source, DESKTOP_BRIDGE_MANAGED_MARKER) || hasCodexLbProviderTable(source))
|
|
44
|
+
return source;
|
|
45
|
+
return upsertTopLevelTomlString(source, 'model_provider', 'openai');
|
|
46
|
+
}
|
|
47
|
+
export function hasCodexLbProviderTable(text) {
|
|
48
|
+
return /^\s*\[model_providers\.(?:"codex-lb"|codex-lb)\]\s*(?:#.*)?$/m.test(text);
|
|
14
49
|
}
|
|
15
50
|
export function upsertDesktopBridgeManagedConfig(text, input) {
|
|
16
51
|
const bridgeBaseUrl = normalizeManagedBridgeBaseUrl(input.bridgeBaseUrl);
|
|
17
52
|
const combinedCatalogPath = String(input.combinedCatalogPath || '').trim();
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
if (path.basename(combinedCatalogPath) !== 'sks-bridge-catalog.json') {
|
|
22
|
-
throw new Error('desktop_bridge_combined_catalog_path_invalid');
|
|
23
|
-
}
|
|
53
|
+
assertCombinedCatalogPath(combinedCatalogPath);
|
|
54
|
+
const bindModelCatalog = input.bindModelCatalog !== false;
|
|
24
55
|
const orphanCleanup = removeDesktopBridgeOrphanManagedMarkers(String(text || ''));
|
|
25
56
|
let next = orphanCleanup.text;
|
|
26
57
|
const selectedProvider = topLevelTomlString(next, 'model_provider');
|
|
@@ -36,17 +67,20 @@ export function upsertDesktopBridgeManagedConfig(text, input) {
|
|
|
36
67
|
}
|
|
37
68
|
const existingCatalog = topLevelTomlString(next, 'model_catalog_json');
|
|
38
69
|
const catalogOwned = topLevelHasLine(next, DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER);
|
|
39
|
-
if (existingCatalog && !catalogOwned) {
|
|
70
|
+
if (existingCatalog && !catalogOwned && bindModelCatalog) {
|
|
40
71
|
throw new Error('historical_user_owned_config_conflict:model_catalog_json');
|
|
41
72
|
}
|
|
42
|
-
const
|
|
43
|
-
|
|
73
|
+
const userCatalog = existingCatalog && !catalogOwned ? existingCatalog : null;
|
|
74
|
+
const keepCodexLb = selectedProvider === 'codex-lb' && hasCodexLbProviderTable(next);
|
|
75
|
+
next = removeManagedBridgeTopLevelBindings(next, { keepModelCatalog: userCatalog !== null });
|
|
44
76
|
next = upsertTopLevelTomlString(next, 'model_provider', keepCodexLb ? 'codex-lb' : 'openai');
|
|
45
77
|
next = addTopLevelMarkerBeforeKey(next, 'model_provider', DESKTOP_BRIDGE_MANAGED_MARKER);
|
|
46
78
|
next = upsertTopLevelTomlString(next, 'openai_base_url', bridgeBaseUrl);
|
|
47
79
|
next = addTopLevelMarkerBeforeKey(next, 'openai_base_url', DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER);
|
|
48
|
-
|
|
49
|
-
|
|
80
|
+
if (bindModelCatalog) {
|
|
81
|
+
next = upsertTopLevelTomlString(next, 'model_catalog_json', combinedCatalogPath);
|
|
82
|
+
next = addTopLevelMarkerBeforeKey(next, 'model_catalog_json', DESKTOP_BRIDGE_MANAGED_MODEL_CATALOG_MARKER);
|
|
83
|
+
}
|
|
50
84
|
return ensureTrailingNewline(next);
|
|
51
85
|
}
|
|
52
86
|
export function removeDesktopBridgeManagedConfig(text) {
|
|
@@ -132,11 +166,12 @@ function removeTopLevelTomlKey(text, key) {
|
|
|
132
166
|
.replace(/^\n+/, '')
|
|
133
167
|
.replace(/\n{3,}/g, '\n\n');
|
|
134
168
|
}
|
|
135
|
-
function removeManagedBridgeTopLevelBindings(text) {
|
|
169
|
+
function removeManagedBridgeTopLevelBindings(text, options = {}) {
|
|
136
170
|
let next = String(text || '');
|
|
137
171
|
next = removeTopLevelTomlKey(next, 'model_provider');
|
|
138
172
|
next = removeTopLevelTomlKey(next, 'openai_base_url');
|
|
139
|
-
|
|
173
|
+
if (!options.keepModelCatalog)
|
|
174
|
+
next = removeTopLevelTomlKey(next, 'model_catalog_json');
|
|
140
175
|
for (const marker of [
|
|
141
176
|
DESKTOP_BRIDGE_MANAGED_MARKER,
|
|
142
177
|
DESKTOP_BRIDGE_MANAGED_BASE_URL_MARKER,
|
package/dist/commands/bridge.js
CHANGED
|
@@ -31,6 +31,7 @@ export function usage(command = 'bridge') {
|
|
|
31
31
|
` sks ${command} catalog sync|status [--json]`,
|
|
32
32
|
` sks ${command} models list [--json]`,
|
|
33
33
|
` sks ${command} models select --set <public-id,...> [--json]`,
|
|
34
|
+
` sks ${command} models refresh [--json]`,
|
|
34
35
|
` sks ${command} route list [--json]`,
|
|
35
36
|
` sks ${command} route set-default <codex-lb|openrouter> [--json]`,
|
|
36
37
|
` sks ${command} route official-models <passthrough|gateway|auto> [--json]`,
|
|
@@ -86,7 +87,7 @@ export async function executeBridgeCommand(args = [], options = {}) {
|
|
|
86
87
|
: await (options.facade || await loadBridgeCommandFacade()).execute(parsed.request);
|
|
87
88
|
const output = parsed.request.operation === 'verify'
|
|
88
89
|
? verificationOutput(raw, parsed.request.level, parsed.strict)
|
|
89
|
-
: ordinaryOutput(raw, parsed.label);
|
|
90
|
+
: ordinaryOutput(raw, parsed.request.operation === 'models.refresh' ? modelRefreshLine(raw) ?? parsed.label : parsed.label);
|
|
90
91
|
const sanitized = sanitizeBridgeValue(output, parsed.secrets);
|
|
91
92
|
const decorated = mergeMetadata(sanitized, options.metadata);
|
|
92
93
|
return {
|
|
@@ -104,6 +105,10 @@ export async function executeBridgeCommand(args = [], options = {}) {
|
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
107
|
}
|
|
108
|
+
function modelRefreshLine(raw) {
|
|
109
|
+
const result = raw?.result;
|
|
110
|
+
return typeof result?.model_refresh?.line === 'string' ? result.model_refresh.line : null;
|
|
111
|
+
}
|
|
107
112
|
export async function loadBridgeCommandFacade() {
|
|
108
113
|
const controller = await import('../core/codex-lb/desktop-controller.js');
|
|
109
114
|
const execute = controller[CONTROLLER_FACADE_EXPORT];
|
|
@@ -260,6 +265,14 @@ async function parseInvocation(args, io) {
|
|
|
260
265
|
allowOnly(parsed, ['--json'], []);
|
|
261
266
|
return { ...base, request: { operation: 'models.list' }, label: 'Selectable bridge models' };
|
|
262
267
|
}
|
|
268
|
+
if (action === 'refresh' && target === undefined) {
|
|
269
|
+
allowOnly(parsed, ['--json'], []);
|
|
270
|
+
return {
|
|
271
|
+
...base,
|
|
272
|
+
request: { operation: 'models.refresh', reason: 'cli', force: true, allow_config_writes: true },
|
|
273
|
+
label: 'Model list refresh'
|
|
274
|
+
};
|
|
275
|
+
}
|
|
263
276
|
if (action === 'select') {
|
|
264
277
|
allowOnly(parsed, ['--json'], ['--set']);
|
|
265
278
|
const raw = parsed.values.get('--set') || '';
|
package/dist/commands/codex.js
CHANGED
|
@@ -45,7 +45,9 @@ export async function run(_command, args = []) {
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
if (action === 'update' || action === 'update-now') {
|
|
48
|
-
const
|
|
48
|
+
const updated = await updateCodexCliNow();
|
|
49
|
+
const modelRefresh = await (await import('../core/codex-lb/model-refresh-after.js')).refreshModelListAfter('codex-update');
|
|
50
|
+
const result = modelRefresh ? { ...updated, model_refresh: modelRefresh } : updated;
|
|
49
51
|
if (flag(args, '--json')) {
|
|
50
52
|
printJson(result);
|
|
51
53
|
if (!result.ok)
|
|
@@ -54,6 +56,8 @@ export async function run(_command, args = []) {
|
|
|
54
56
|
}
|
|
55
57
|
for (const line of codexCliUpdateConsoleLines(result))
|
|
56
58
|
console.log(line);
|
|
59
|
+
if (typeof modelRefresh?.line === 'string')
|
|
60
|
+
console.log(modelRefresh.line);
|
|
57
61
|
if (!result.ok)
|
|
58
62
|
process.exitCode = 1;
|
|
59
63
|
return;
|
|
@@ -238,6 +238,8 @@ export function renderDoctorConsoleReport(result, extras) {
|
|
|
238
238
|
if (codexCurrentAppDoctor.fixed?.length)
|
|
239
239
|
line(` doctor --fix repaired: ${codexCurrentAppDoctor.fixed.join(', ')}`);
|
|
240
240
|
line(`Desktop Bridge: ${desktopBridge.ok ? 'ready' : 'blocked'} (${desktopBridge.status?.readiness?.state || 'unavailable'})`);
|
|
241
|
+
if (desktopBridge.model_refresh?.line)
|
|
242
|
+
line(` ${desktopBridge.model_refresh.line}`);
|
|
241
243
|
for (const providerId of ['codex-lb', 'openrouter']) {
|
|
242
244
|
const provider = desktopBridge.providers?.[providerId];
|
|
243
245
|
if (!provider)
|
package/dist/commands/doctor.js
CHANGED
|
@@ -376,6 +376,8 @@ async function runDoctorGlobalOnlyFix(args = [], root, deps = {}) {
|
|
|
376
376
|
? 'repaired'
|
|
377
377
|
: bridgeRepair?.ok === false ? 'failed' : 'nothing_to_repair'}`);
|
|
378
378
|
console.log(`Desktop Bridge: ${result.desktop_bridge.ok ? 'ready' : 'blocked'} (${result.desktop_bridge.status?.readiness?.state || 'unavailable'})`);
|
|
379
|
+
if (result.desktop_bridge_repair?.model_refresh?.line)
|
|
380
|
+
console.log(result.desktop_bridge_repair.model_refresh.line);
|
|
379
381
|
if (result.migration_receipt) {
|
|
380
382
|
console.log(`Migration receipt: ${result.migration_receipt.status} (${result.home})`);
|
|
381
383
|
}
|
|
@@ -737,9 +739,8 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
|
|
|
737
739
|
const codexApp = deepDiagnostics
|
|
738
740
|
? await codexAppIntegrationStatus({ codex }).catch((err) => ({ ok: false, error: err.message }))
|
|
739
741
|
: { ok: false, skipped: true, warnings: ['codex_app_optional_diagnostic_skipped'] };
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}, { desktopBridgeStatusImpl: deps.desktopBridgeStatusImpl });
|
|
742
|
+
let desktopBridgeModelRefresh = doctorFix ? null : (await (deps.desktopBridgeRepairImpl || repairDoctorDesktopBridgeCatalog)({ fix: false, reason: 'doctor' }).catch(() => null))?.model_refresh ?? null;
|
|
743
|
+
const desktopBridgeBeforeFix = await inspectDoctorDesktopBridgeStatus({ processEnv: process.env }, { desktopBridgeStatusImpl: deps.desktopBridgeStatusImpl });
|
|
743
744
|
const providerContext = deepDiagnostics
|
|
744
745
|
? await resolveProviderContext({ root, route: '$Doctor', serviceTier: process.env.SKS_SERVICE_TIER || 'fast' }).catch((err) => ({
|
|
745
746
|
schema: 'sks.provider-context.v2',
|
|
@@ -1091,7 +1092,7 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
|
|
|
1091
1092
|
{
|
|
1092
1093
|
id: 'desktop_bridge_catalog_repair',
|
|
1093
1094
|
required_for_ready: false,
|
|
1094
|
-
run: async () => repairDoctorDesktopBridgeCatalog({ fix: doctorFix })
|
|
1095
|
+
run: async () => { const repair = await repairDoctorDesktopBridgeCatalog({ fix: doctorFix }); desktopBridgeModelRefresh = repair.model_refresh ?? null; return repair; }
|
|
1095
1096
|
}
|
|
1096
1097
|
].filter((phase) => doctorPhaseIds.includes(phase.id))
|
|
1097
1098
|
}).catch((err) => ({
|
|
@@ -1118,11 +1119,9 @@ async function runDoctor(args = [], root, doctorFix, deps = {}) {
|
|
|
1118
1119
|
}))
|
|
1119
1120
|
: null;
|
|
1120
1121
|
const doctorFixPostcheck = doctorFix ? (await import('../core/doctor/doctor-repair-postcheck.js')).doctorRepairPostcheck(doctorFixTransaction) : null;
|
|
1121
|
-
const desktopBridge = doctorFixTransaction
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
}, { desktopBridgeStatusImpl: deps.desktopBridgeStatusImpl })
|
|
1125
|
-
: desktopBridgeBeforeFix;
|
|
1122
|
+
const desktopBridge = { ...(doctorFixTransaction
|
|
1123
|
+
? await inspectDoctorDesktopBridgeStatus({ processEnv: process.env }, { desktopBridgeStatusImpl: deps.desktopBridgeStatusImpl })
|
|
1124
|
+
: desktopBridgeBeforeFix), model_refresh: desktopBridgeModelRefresh };
|
|
1126
1125
|
const startupRoleRepair = startupConfigRepair?.role_repair;
|
|
1127
1126
|
const agentRoleConfigRepair = doctorFix && startupRoleRepair
|
|
1128
1127
|
? startupRoleRepair
|
|
@@ -5,6 +5,7 @@ import { nowIso, readText, writeJsonAtomic } from '../fsx.js';
|
|
|
5
5
|
import { inspectConfinedPath, moveConfinedPath, removeConfinedDirectoryIfEmpty, removeManagedPathVerified, uniqueConfinedPath, walkConfinedEntries } from '../managed-path-safety.js';
|
|
6
6
|
import { MANAGED_OFFICIAL_SUBAGENT_ROLES, RETIRED_MANAGED_AGENT_ROLE_TOMBSTONES, managedAgentRoleOwnsText } from '../managed-assets/managed-assets-manifest.js';
|
|
7
7
|
import { installOfficialSubagentAgentConfigs, refreshGlobalOfficialSubagentAgentConfigs } from '../subagents/official-subagent-config.js';
|
|
8
|
+
import { reconcileStaleSksModelPins } from './sks-model-pin-hygiene.js';
|
|
8
9
|
export const AGENT_ROLE_CONFIG_REPAIR_SCHEMA = 'sks.agent-role-config-repair.v1';
|
|
9
10
|
export async function repairAgentRoleConfigs(input) {
|
|
10
11
|
const root = path.resolve(input.root);
|
|
@@ -18,6 +19,7 @@ export async function repairAgentRoleConfigs(input) {
|
|
|
18
19
|
...(input.globalRuntimeRoot ? { globalRuntimeRoot: input.globalRuntimeRoot } : {}),
|
|
19
20
|
fix: input.apply === true
|
|
20
21
|
});
|
|
22
|
+
const modelPinHygiene = await reconcileStaleSksModelPins({ root, codexHome, fix: input.apply === true });
|
|
21
23
|
const officialRepair = await installOfficialSubagentAgentConfigs(root, { apply: input.apply === true });
|
|
22
24
|
const globalRepair = path.join(root, '.codex') === codexHome
|
|
23
25
|
? null
|
|
@@ -38,7 +40,7 @@ export async function repairAgentRoleConfigs(input) {
|
|
|
38
40
|
schema: AGENT_ROLE_CONFIG_REPAIR_SCHEMA,
|
|
39
41
|
generated_at: nowIso(),
|
|
40
42
|
ok: input.apply
|
|
41
|
-
? requiredFixes === appliedFixes && manualBlockers.length === 0 && retiredRoleCleanup.ok
|
|
43
|
+
? requiredFixes === appliedFixes && manualBlockers.length === 0 && retiredRoleCleanup.ok && modelPinHygiene.errors.length === 0
|
|
42
44
|
: manualBlockers.length === 0 && retiredRoleCleanup.ok && (globalRepair?.stale.length || 0) === 0,
|
|
43
45
|
apply: input.apply === true,
|
|
44
46
|
missing,
|
|
@@ -51,12 +53,14 @@ export async function repairAgentRoleConfigs(input) {
|
|
|
51
53
|
manual_blockers: manualBlockers,
|
|
52
54
|
manifest_role_ids: MANAGED_OFFICIAL_SUBAGENT_ROLES.map((role) => role.id),
|
|
53
55
|
retired_role_cleanup: retiredRoleCleanup,
|
|
56
|
+
model_pin_hygiene: modelPinHygiene,
|
|
54
57
|
warnings_suppressed: true,
|
|
55
58
|
blockers: [
|
|
56
59
|
...manualBlockers,
|
|
57
60
|
...(!input.apply ? (globalRepair?.stale.map((file) => `stale_global_official_subagent_agent:${globalPath(file)}`) || []) : []),
|
|
58
61
|
...(input.apply && requiredFixes !== appliedFixes ? ['agent_role_config_repair_incomplete'] : []),
|
|
59
|
-
...(!retiredRoleCleanup.ok ? ['retired_agent_role_cleanup_incomplete'] : [])
|
|
62
|
+
...(!retiredRoleCleanup.ok ? ['retired_agent_role_cleanup_incomplete'] : []),
|
|
63
|
+
...modelPinHygiene.errors.map((error) => `model_pin_hygiene_failed:${error}`)
|
|
60
64
|
]
|
|
61
65
|
};
|
|
62
66
|
if (input.reportPath)
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readText, writeTextAtomic } from '../fsx.js';
|
|
4
|
+
import { hasCodexLbProviderTable } from '../../cli/install-helpers-codex-lb-config.js';
|
|
5
|
+
import { hasSksManagedCodexConfigMarker, writeCodexConfigGuarded } from '../codex/codex-config-guard.js';
|
|
6
|
+
import { bridgeRouteIndexPath, combinedBridgeCatalogPath } from '../codex-lb/combined-catalog.js';
|
|
7
|
+
import { readActiveCombinedBridgeCatalog } from '../codex-lb/combined-catalog/persistence.js';
|
|
8
|
+
import { backupInvalidToml } from '../subagents/official-subagent-config.js';
|
|
9
|
+
export const MODEL_PIN_HYGIENE_SCHEMA = 'sks.model-pin-hygiene.v1';
|
|
10
|
+
const LEGACY_ROLE_NAMES = new Set(['sks-planner', 'sks-implementer', 'sks-explorer', 'sks-checker']);
|
|
11
|
+
async function catalogPinState(codexHome) {
|
|
12
|
+
const read = await readActiveCombinedBridgeCatalog(combinedBridgeCatalogPath(codexHome), bridgeRouteIndexPath(codexHome)).catch(() => null);
|
|
13
|
+
if (!read?.ok)
|
|
14
|
+
return null;
|
|
15
|
+
const rows = new Map();
|
|
16
|
+
for (const row of read.catalog.models) {
|
|
17
|
+
for (const id of [row.public_id, row.slug])
|
|
18
|
+
if (typeof id === 'string' && id)
|
|
19
|
+
rows.set(id, row);
|
|
20
|
+
}
|
|
21
|
+
return (model) => {
|
|
22
|
+
const row = rows.get(model);
|
|
23
|
+
if (!row)
|
|
24
|
+
return 'unlisted';
|
|
25
|
+
const visibility = String(row.visibility || 'list').toLowerCase();
|
|
26
|
+
const upgrade = row.upgrade && typeof row.upgrade === 'object' ? row.upgrade.model : null;
|
|
27
|
+
return visibility === 'hide' || visibility === 'hidden' || (typeof upgrade === 'string' && upgrade) ? 'superseded' : 'current';
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function topLevelEnd(lines) {
|
|
31
|
+
const end = lines.findIndex((line) => /^\s*\[/.test(line) || /^\s*developer_instructions\s*=/.test(line));
|
|
32
|
+
return end === -1 ? lines.length : end;
|
|
33
|
+
}
|
|
34
|
+
function stalePinLines(text, pinState, codexLbProviderDefined) {
|
|
35
|
+
const lines = text.split('\n');
|
|
36
|
+
const indexes = [];
|
|
37
|
+
const labels = [];
|
|
38
|
+
for (let index = 0; index < topLevelEnd(lines); index += 1) {
|
|
39
|
+
const match = /^\s*(model|model_provider)\s*=\s*"([^"]*)"\s*(?:#.*)?$/.exec(lines[index] || '');
|
|
40
|
+
if (!match)
|
|
41
|
+
continue;
|
|
42
|
+
const [, key, value] = match;
|
|
43
|
+
const stale = key === 'model_provider'
|
|
44
|
+
? value === 'codex-lb' && !codexLbProviderDefined
|
|
45
|
+
: pinState !== null && pinState(value) !== 'current';
|
|
46
|
+
if (!stale)
|
|
47
|
+
continue;
|
|
48
|
+
indexes.push(index);
|
|
49
|
+
labels.push(`${key} = "${value}"`);
|
|
50
|
+
}
|
|
51
|
+
return { indexes, labels };
|
|
52
|
+
}
|
|
53
|
+
function withoutLines(text, indexes) {
|
|
54
|
+
const drop = new Set(indexes);
|
|
55
|
+
return text.split('\n').filter((_, index) => !drop.has(index)).join('\n');
|
|
56
|
+
}
|
|
57
|
+
const CODEX_LB_SELECTION_EVIDENCE = [
|
|
58
|
+
/^\s*model_provider\s*=\s*["']codex-lb["']\s*(?:#.*)?$/,
|
|
59
|
+
/^\s*\[model_providers\.(?:"codex-lb"|codex-lb)\]\s*(?:#.*)?$/
|
|
60
|
+
];
|
|
61
|
+
function sksOwnedProjectConfig(text) {
|
|
62
|
+
const withoutSelection = text.split('\n')
|
|
63
|
+
.filter((line) => !CODEX_LB_SELECTION_EVIDENCE.some((pattern) => pattern.test(line)))
|
|
64
|
+
.join('\n');
|
|
65
|
+
return hasSksManagedCodexConfigMarker(withoutSelection);
|
|
66
|
+
}
|
|
67
|
+
function isLegacySksRole(file, text) {
|
|
68
|
+
if (/sks_managed_body_sha256/.test(text))
|
|
69
|
+
return false;
|
|
70
|
+
const name = /^\s*name\s*=\s*"([^"]+)"/m.exec(text)?.[1] ?? path.basename(file, '.toml');
|
|
71
|
+
return /^\s*description\s*=\s*"SKS managed\b/m.test(text) || LEGACY_ROLE_NAMES.has(name);
|
|
72
|
+
}
|
|
73
|
+
async function regularFile(file) {
|
|
74
|
+
const stat = await fsp.lstat(file).catch(() => null);
|
|
75
|
+
return Boolean(stat?.isFile() && !stat.isSymbolicLink());
|
|
76
|
+
}
|
|
77
|
+
async function roleFiles(dir) {
|
|
78
|
+
const entries = await fsp.readdir(dir).catch(() => []);
|
|
79
|
+
return entries.filter((name) => name.endsWith('.toml')).sort().map((name) => path.join(dir, name));
|
|
80
|
+
}
|
|
81
|
+
export async function reconcileStaleSksModelPins(input) {
|
|
82
|
+
const root = path.resolve(input.root);
|
|
83
|
+
const codexHome = path.resolve(input.codexHome);
|
|
84
|
+
const pinState = await catalogPinState(codexHome);
|
|
85
|
+
const report = {
|
|
86
|
+
schema: MODEL_PIN_HYGIENE_SCHEMA,
|
|
87
|
+
fix: input.fix,
|
|
88
|
+
catalog_available: pinState !== null,
|
|
89
|
+
changes: [],
|
|
90
|
+
remaining: [],
|
|
91
|
+
errors: []
|
|
92
|
+
};
|
|
93
|
+
const projectConfig = path.join(root, '.codex', 'config.toml');
|
|
94
|
+
const projectText = projectConfig !== path.join(codexHome, 'config.toml') && await regularFile(projectConfig)
|
|
95
|
+
? await readText(projectConfig, '')
|
|
96
|
+
: null;
|
|
97
|
+
const providerDefinedAbove = hasCodexLbProviderTable(await readText(path.join(codexHome, 'config.toml'), ''))
|
|
98
|
+
|| (projectText !== null && hasCodexLbProviderTable(projectText));
|
|
99
|
+
const agentDirs = [...new Set([path.join(codexHome, 'agents'), path.join(root, '.codex', 'agents')])];
|
|
100
|
+
for (const dir of agentDirs) {
|
|
101
|
+
for (const file of await roleFiles(dir)) {
|
|
102
|
+
if (!(await regularFile(file)))
|
|
103
|
+
continue;
|
|
104
|
+
const text = await readText(file, '');
|
|
105
|
+
if (!isLegacySksRole(file, text))
|
|
106
|
+
continue;
|
|
107
|
+
const pins = stalePinLines(text, pinState, providerDefinedAbove || hasCodexLbProviderTable(text));
|
|
108
|
+
if (!pins.indexes.length)
|
|
109
|
+
continue;
|
|
110
|
+
if (!input.fix) {
|
|
111
|
+
report.remaining.push({ file, removed: pins.labels, backup_path: null });
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const backup = await backupInvalidToml(file, text, 'model-pin-hygiene');
|
|
116
|
+
const mode = (await fsp.stat(file)).mode & 0o777;
|
|
117
|
+
await writeTextAtomic(file, withoutLines(text, pins.indexes), { mode });
|
|
118
|
+
report.changes.push({ file, removed: pins.labels, backup_path: backup });
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
report.errors.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
122
|
+
report.remaining.push({ file, removed: pins.labels, backup_path: null });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (projectText !== null) {
|
|
127
|
+
const text = projectText;
|
|
128
|
+
const pins = sksOwnedProjectConfig(text) ? stalePinLines(text, pinState, providerDefinedAbove) : { indexes: [], labels: [] };
|
|
129
|
+
if (pins.indexes.length && !input.fix)
|
|
130
|
+
report.remaining.push({ file: projectConfig, removed: pins.labels, backup_path: null });
|
|
131
|
+
else if (pins.indexes.length) {
|
|
132
|
+
const write = await writeCodexConfigGuarded({
|
|
133
|
+
root,
|
|
134
|
+
configPath: projectConfig,
|
|
135
|
+
before: text,
|
|
136
|
+
mutate: () => withoutLines(text, pins.indexes),
|
|
137
|
+
cause: 'model-pin-hygiene',
|
|
138
|
+
backupTag: 'model-pin-hygiene',
|
|
139
|
+
verifyUnchangedBeforeWrite: true
|
|
140
|
+
});
|
|
141
|
+
if (write.ok)
|
|
142
|
+
report.changes.push({ file: projectConfig, removed: pins.labels, backup_path: write.backup_path });
|
|
143
|
+
else {
|
|
144
|
+
report.errors.push(`${projectConfig}: ${write.status}`);
|
|
145
|
+
report.remaining.push({ file: projectConfig, removed: pins.labels, backup_path: null });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return report;
|
|
150
|
+
}
|
|
@@ -585,19 +585,24 @@ export function removeLegacyTopLevelCodexModeLocks(text = '') {
|
|
|
585
585
|
const lines = String(ensureTrailingNewline(text) || '').split('\n');
|
|
586
586
|
const firstTable = lines.findIndex((x) => /^\s*\[.+\]\s*$/.test(x));
|
|
587
587
|
const end = firstTable === -1 ? lines.length : firstTable;
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
588
|
+
const drop = new Set();
|
|
589
|
+
for (let index = 0; index < end; index += 1) {
|
|
590
|
+
if (!/^\s*(?:model|model_reasoning_effort)\s*=/.test(lines[index] || ''))
|
|
591
|
+
continue;
|
|
592
|
+
const marker = sksModeLockProvenance(lines, index);
|
|
593
|
+
if (marker === null)
|
|
594
|
+
continue;
|
|
595
|
+
drop.add(index);
|
|
596
|
+
if (marker >= 0)
|
|
597
|
+
drop.add(marker);
|
|
598
|
+
}
|
|
599
|
+
return ensureTrailingNewline(lines.filter((_, index) => !drop.has(index)).join('\n').replace(/^\n+/, '').replace(/\n{3,}/g, '\n\n'));
|
|
595
600
|
}
|
|
596
|
-
function
|
|
601
|
+
function sksModeLockProvenance(lines, index) {
|
|
597
602
|
const current = String(lines[index] || '');
|
|
598
603
|
const inlineComment = current.includes('#') ? current.slice(current.indexOf('#')) : '';
|
|
599
604
|
if (isSksModeLockMarker(inlineComment))
|
|
600
|
-
return
|
|
605
|
+
return -1;
|
|
601
606
|
const allowedManagedKeys = new Set([
|
|
602
607
|
'service_tier', 'model', 'model_reasoning_effort', 'model_provider',
|
|
603
608
|
'approval_policy', 'sandbox_mode', 'web_search', 'notify', 'preferred_auth_method'
|
|
@@ -609,17 +614,17 @@ function hasSksModeLockProvenance(lines, index) {
|
|
|
609
614
|
continue;
|
|
610
615
|
if (candidate.startsWith('#')) {
|
|
611
616
|
if (isSksModeLockMarker(candidate))
|
|
612
|
-
return
|
|
617
|
+
return cursor;
|
|
613
618
|
continue;
|
|
614
619
|
}
|
|
615
620
|
const key = candidate.match(/^([A-Za-z0-9_-]+)\s*=/)?.[1] || '';
|
|
616
621
|
if (!allowedManagedKeys.has(key))
|
|
617
|
-
return
|
|
622
|
+
return null;
|
|
618
623
|
}
|
|
619
|
-
return
|
|
624
|
+
return null;
|
|
620
625
|
}
|
|
621
626
|
function isSksModeLockMarker(value = '') {
|
|
622
|
-
return /^#\s*(?:SKS|Sneakoscope)\b.*(?:
|
|
627
|
+
return /^#\s*(?:SKS|Sneakoscope)\b.*(?:forced fast UI|managed (?:Codex )?(?:model|reasoning)|codex-lb (?:model|reasoning))/i.test(String(value || '').trim());
|
|
623
628
|
}
|
|
624
629
|
function mergeLostFastUiKeys(before, nextInput) {
|
|
625
630
|
let next = String(nextInput || '');
|
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { exists, nowIso, readText, sha256, writeJsonAtomic } from '../fsx.js';
|
|
5
|
+
import { HISTORICAL_DESKTOP_COMPAT_MARKER, HISTORICAL_OAUTH_SELECTION_MARKER, HISTORICAL_PROVIDER_SELECTION_MARKER } from '../codex-lb/desktop-bridge-migration/historical-toml.js';
|
|
5
6
|
export const CODEX_APP_UI_STATE_SNAPSHOT_SCHEMA = 'sks.codex-app-ui-state-snapshot.v1';
|
|
6
7
|
export const PROJECT_LOCAL_FORBIDDEN_CODEX_KEYS = [
|
|
7
8
|
'openai_base_url',
|
|
@@ -14,6 +15,9 @@ export const PROJECT_LOCAL_FORBIDDEN_CODEX_KEYS = [
|
|
|
14
15
|
const HOST_OWNED_KEY_RE = /^(?:model|model_reasoning_effort|openai_base_url|chatgpt_base_url|apps_mcp_product_sku|model_provider|model_providers(?:\.|$)|notify|profile|profiles(?:\.|$)|experimental_realtime_ws_base_url|otel(?:\.|$)|features\.fast_mode|service_tier|user\.fast_mode(?:\.|$))/;
|
|
15
16
|
const SECRET_KEY_RE = /(?:key|token|secret|password|credential|cookie|authorization|auth|bearer|refresh|access|headers?|env)/i;
|
|
16
17
|
const SKS_GLOBAL_UI_LOCK_KEYS = new Set(['model_provider', 'model', 'model_reasoning_effort']);
|
|
18
|
+
const RETIRED_SKS_SELECTION_MARKERS = new Set([
|
|
19
|
+
HISTORICAL_DESKTOP_COMPAT_MARKER, HISTORICAL_PROVIDER_SELECTION_MARKER, HISTORICAL_OAUTH_SELECTION_MARKER
|
|
20
|
+
]);
|
|
17
21
|
const SKS_GLOBAL_UI_LOCK_CONTEXT_KEYS = new Set([
|
|
18
22
|
'service_tier', 'model_provider', 'model', 'model_reasoning_effort',
|
|
19
23
|
'approval_policy', 'sandbox_mode', 'web_search', 'notify', 'preferred_auth_method'
|
|
@@ -155,6 +159,8 @@ export function isSksOwnedGlobalUiLock(lines, index) {
|
|
|
155
159
|
const inlineComment = current.includes('#') ? current.slice(current.indexOf('#')) : '';
|
|
156
160
|
if (isSksGlobalUiLockMarker(inlineComment))
|
|
157
161
|
return true;
|
|
162
|
+
if (key === 'model_provider' && RETIRED_SKS_SELECTION_MARKERS.has(String(lines[index - 1] || '').trim()))
|
|
163
|
+
return true;
|
|
158
164
|
const lowerBound = Math.max(0, index - 16);
|
|
159
165
|
for (let cursor = index - 1; cursor >= lowerBound; cursor -= 1) {
|
|
160
166
|
const candidate = String(lines[cursor] || '').trim();
|
|
@@ -174,7 +180,7 @@ export function isSksOwnedGlobalUiLock(lines, index) {
|
|
|
174
180
|
return false;
|
|
175
181
|
}
|
|
176
182
|
function isSksGlobalUiLockMarker(value) {
|
|
177
|
-
return /^#\s*(?:SKS|Sneakoscope)\b.*(?:
|
|
183
|
+
return /^#\s*(?:SKS|Sneakoscope)\b.*(?:forced fast UI|legacy (?:provider|model|reasoning) lock|managed (?:Codex )?(?:provider|model|reasoning)|codex-lb (?:provider|model|reasoning))/i.test(String(value || '').trim());
|
|
178
184
|
}
|
|
179
185
|
export function scanProjectLocalForbiddenKeys(text) {
|
|
180
186
|
const { signals } = scanTomlSignals(text);
|
|
@@ -33,7 +33,6 @@ export const NATIVE_SOURCE_FILES = [
|
|
|
33
33
|
'ProvidersOpenRouterOnly.swift',
|
|
34
34
|
'SubagentModelsModels.swift',
|
|
35
35
|
'SubagentModelsViewController.swift',
|
|
36
|
-
'RemoteCodingViewController.swift',
|
|
37
36
|
'DiagnosticsViewController.swift',
|
|
38
37
|
'SettingsViewController.swift',
|
|
39
38
|
'LocalDecisionModels.swift',
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export const MANAGED_ROUTING_RUNTIME = 'desktop-bridge';
|
|
2
2
|
export const BRIDGE_PROVIDER_IDS = ['codex-lb', 'openrouter'];
|
|
3
3
|
export const BRIDGE_OFFICIAL_ROUTE_ID = 'openai';
|
|
4
|
+
export const BRIDGE_REMOTE_COMPACTION = Object.freeze({
|
|
5
|
+
openai: 'native',
|
|
6
|
+
'codex-lb': 'native',
|
|
7
|
+
openrouter: 'bridge_summary',
|
|
8
|
+
});
|