@bitkyc08/opencodex 2.62.0-preview.20260923 → 2.63.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.
- package/gui/dist/assets/App-BqrsSrIR.js +50 -0
- package/gui/dist/assets/{Tray-DdvPBVLi.js → Tray-CncKDBTp.js} +1 -1
- package/gui/dist/assets/index-C6SJrh0N.js +86 -0
- package/gui/dist/assets/index-DdDunwDb.css +1 -0
- package/gui/dist/assets/{usage-companion-chart-BIhvlbQW.js → usage-companion-chart-CzAAAB1o.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/cursor/catalog.ts +15 -0
- package/src/adapters/cursor/effort-map.ts +7 -0
- package/src/adapters/cursor/envelope-echo.ts +51 -25
- package/src/adapters/cursor/protobuf-request.ts +39 -13
- package/src/adapters/cursor.ts +52 -4
- package/src/adapters/devin/cloud-direct/chat.ts +50 -16
- package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +4 -1
- package/src/adapters/devin/live-models.ts +7 -0
- package/src/adapters/devin.ts +7 -5
- package/src/adapters/kiro/reasoning.ts +5 -0
- package/src/adapters/openai-responses/passthrough.ts +4 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +5 -3
- package/src/claude/desktop-gateway-state.ts +7 -2
- package/src/claude/desktop-policy.ts +105 -1
- package/src/cli/config-command.ts +22 -7
- package/src/cli/doctor.ts +14 -4
- package/src/codex/catalog/effort.ts +35 -4
- package/src/codex/catalog/metadata.ts +33 -5
- package/src/codex/catalog/native-models.ts +43 -2
- package/src/codex/catalog/pinned-models.ts +37 -0
- package/src/codex/catalog-auto-refresh.ts +6 -0
- package/src/codex/data/roster-pinned-models.json +359 -0
- package/src/codex/data/upstream-models.json +365 -271
- package/src/codex/inject/provider-table.ts +108 -0
- package/src/codex/inject/remove.ts +2 -114
- package/src/codex/model-entitlements.ts +14 -10
- package/src/codex/subagent-defaults.ts +2 -109
- package/src/codex/toml-source-lines.ts +112 -0
- package/src/config/live-reconcile.ts +145 -27
- package/src/config/load-degrade.ts +3 -4
- package/src/config.ts +2 -2
- package/src/generated/compatibility-version.json +83 -67
- package/src/generated/model-metadata.ts +5 -5
- package/src/lab/artifacts/sanitize.ts +60 -19
- package/src/lib/app-owned-memory-stores.ts +7 -0
- package/src/lib/app-owned-memory.ts +13 -2
- package/src/lib/errors.ts +6 -4
- package/src/lib/upstream-retry.ts +35 -5
- package/src/oauth/devin.ts +43 -37
- package/src/providers/codebuddy-models.ts +11 -0
- package/src/providers/kiro-models.ts +9 -0
- package/src/providers/quota/vendor-probes-oauth.ts +9 -2
- package/src/providers/registry/entries-core.ts +19 -8
- package/src/providers/registry/entries-extended.ts +4 -1
- package/src/providers/registry/model-seeds.ts +22 -2
- package/src/responses/bridge-search-replay-cache.ts +20 -10
- package/src/responses/plaintext-v2-agent-messages.ts +10 -1
- package/src/routing/identity-domains.ts +22 -15
- package/src/server/index/websocket-handler.ts +22 -2
- package/src/server/management/agent-settings-routes.ts +19 -10
- package/src/server/management/context.ts +4 -2
- package/src/server/responses/codex-ws-exchange.ts +24 -8
- package/src/server/responses/core-codex-account.ts +4 -0
- package/src/server/responses/core-combo-failure.ts +16 -9
- package/src/server/responses/core-combo.ts +7 -3
- package/src/server/responses/core-options.ts +6 -0
- package/src/server/responses/native-injection-replay.ts +13 -1
- package/src/server/responses/native-injection.ts +66 -7
- package/src/server/responses/native-response-control.ts +6 -2
- package/src/server/responses/native-steering-replay.ts +60 -0
- package/src/server/responses/native-steering.ts +9 -1
- package/src/server/responses/passthrough-delivery.ts +3 -4
- package/src/server/responses/passthrough-dispatch.ts +7 -0
- package/src/server/responses/request-prepare.ts +12 -0
- package/src/server/responses/request-transport.ts +5 -2
- package/src/server/responses/ws-upstream.ts +1 -1
- package/src/server/ws-bridge.ts +17 -1
- package/src/types/request.ts +5 -0
- package/src/usage/expected-prices.ts +38 -8
- package/src/web-search/executor.ts +38 -13
- package/gui/dist/assets/App-CANWata-.js +0 -50
- package/gui/dist/assets/index-BIS5HXEP.js +0 -86
- package/gui/dist/assets/index-_bpvxJu0.css +0 -1
|
@@ -47,6 +47,16 @@ type PersistedServerBinding = Pick<OcxConfig, "port" | "hostname">;
|
|
|
47
47
|
|
|
48
48
|
const persistedLiveServerBinding = new WeakMap<OcxConfig, PersistedServerBinding>();
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Config instances nobody holds long-term — the catalog auto-refresh tick's
|
|
52
|
+
* per-tick `loadConfig()` snapshot. A detached snapshot cannot express a
|
|
53
|
+
* deliberate deletion and owns no live listener socket, so the live policy's
|
|
54
|
+
* `hostname`/`port` and disk-only-key skips would only discard concurrent hand
|
|
55
|
+
* edits wholesale. Keyed on the instance so the mode cannot leak into the
|
|
56
|
+
* long-lived server config.
|
|
57
|
+
*/
|
|
58
|
+
const detachedConfigSnapshots = new WeakSet<OcxConfig>();
|
|
59
|
+
|
|
50
60
|
/**
|
|
51
61
|
* Arm the baseline for a long-lived config. MANDATORY at `startServer`, not lazy on
|
|
52
62
|
* first save — arming lazily would lose exactly the hand edit made before that first
|
|
@@ -57,6 +67,17 @@ export function armClaudeCodeBaseline(config: OcxConfig): void {
|
|
|
57
67
|
claudeCodeBaseline.set(config, structuredClone(config.claudeCode));
|
|
58
68
|
}
|
|
59
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Arm a freshly loaded config instance no long-lived server owns. The save path
|
|
72
|
+
* then reconciles every field — the listener binding and keys that exist only
|
|
73
|
+
* on disk included — against this arming baseline, so a concurrent hand edit is
|
|
74
|
+
* adopted rather than overwritten by the snapshot's stale values.
|
|
75
|
+
*/
|
|
76
|
+
export function armDetachedConfigBaseline(config: OcxConfig): void {
|
|
77
|
+
armClaudeCodeBaseline(config);
|
|
78
|
+
detachedConfigSnapshots.add(config);
|
|
79
|
+
}
|
|
80
|
+
|
|
60
81
|
/**
|
|
61
82
|
* Adopt one schema-validated provider that was read from the authoritative disk
|
|
62
83
|
* config into a long-lived server config without rebasing any unrelated field.
|
|
@@ -80,6 +101,42 @@ export function claudeCodeBaselineArmed(config: OcxConfig): boolean {
|
|
|
80
101
|
return claudeCodeBaseline.has(config);
|
|
81
102
|
}
|
|
82
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Adopt a field-scoped Claude Code write into a long-lived config snapshot.
|
|
106
|
+
*
|
|
107
|
+
* Scoped writers commit against the current file rather than serializing the
|
|
108
|
+
* whole snapshot. Mirror that committed subtree and rebase the hand-edit guard
|
|
109
|
+
* together so a later unrelated save does not mistake the scoped write for an
|
|
110
|
+
* outstanding in-memory mutation.
|
|
111
|
+
*
|
|
112
|
+
* The live subtree may already hold pending mutations a concurrent request
|
|
113
|
+
* assigned but has not saved yet — the Claude settings PUT yields between
|
|
114
|
+
* assigning `config.claudeCode` and saving. Adopt through the same three-way
|
|
115
|
+
* reconcile guarded saves use, so pending live leaves survive, disjoint
|
|
116
|
+
* committed changes merge in, and only the baseline moves wholesale to the
|
|
117
|
+
* committed subtree.
|
|
118
|
+
*/
|
|
119
|
+
export function adoptPersistedClaudeCode(
|
|
120
|
+
config: OcxConfig,
|
|
121
|
+
persistedClaudeCode: OcxConfig["claudeCode"],
|
|
122
|
+
): void {
|
|
123
|
+
const storedBaseline: ConfigMergeValue = claudeCodeBaseline.has(config)
|
|
124
|
+
? claudeCodeBaseline.get(config)
|
|
125
|
+
: MISSING_CONFIG_VALUE;
|
|
126
|
+
const merged = reconcileConfigValue(
|
|
127
|
+
storedBaseline === undefined ? MISSING_CONFIG_VALUE : storedBaseline,
|
|
128
|
+
config.claudeCode === undefined ? MISSING_CONFIG_VALUE : config.claudeCode,
|
|
129
|
+
persistedClaudeCode === undefined ? MISSING_CONFIG_VALUE : persistedClaudeCode,
|
|
130
|
+
);
|
|
131
|
+
if (merged === MISSING_CONFIG_VALUE) delete config.claudeCode;
|
|
132
|
+
else config.claudeCode = merged as OcxConfig["claudeCode"];
|
|
133
|
+
const baseline = liveConfigBaseline.get(config);
|
|
134
|
+
if (baseline) baseline.claudeCode = structuredClone(persistedClaudeCode);
|
|
135
|
+
if (claudeCodeBaseline.has(config)) {
|
|
136
|
+
claudeCodeBaseline.set(config, structuredClone(persistedClaudeCode));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
83
140
|
/**
|
|
84
141
|
* Structural compare of parsed subtrees. NOT `JSON.stringify`: key order must not
|
|
85
142
|
* decide whether a user's hand edit survives.
|
|
@@ -124,6 +181,42 @@ type IndexedCustomModels = {
|
|
|
124
181
|
byId: Map<string, Record<string, unknown>>;
|
|
125
182
|
};
|
|
126
183
|
|
|
184
|
+
function indexDisabledModels(value: ConfigMergeValue): { order: string[]; members: Set<string> } | null {
|
|
185
|
+
if (value === MISSING_CONFIG_VALUE) return { order: [], members: new Set() };
|
|
186
|
+
if (!Array.isArray(value)) return null;
|
|
187
|
+
const members = new Set<string>();
|
|
188
|
+
for (const item of value) {
|
|
189
|
+
if (typeof item !== "string" || members.has(item)) return null;
|
|
190
|
+
members.add(item);
|
|
191
|
+
}
|
|
192
|
+
return { order: value as string[], members };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Merge disabled-model lists by membership instead of treating the array as one
|
|
197
|
+
* opaque leaf: a slug either side deleted stays deleted (deletion wins over the
|
|
198
|
+
* other side's unchanged retention) while slugs added on either side are kept.
|
|
199
|
+
* Discovery only appends, so live additions are its arrivals; a hand edit that
|
|
200
|
+
* hid or un-hid a model mid-refresh survives the discovery save.
|
|
201
|
+
*/
|
|
202
|
+
function reconcileDisabledModels(
|
|
203
|
+
baseline: ConfigMergeValue,
|
|
204
|
+
live: ConfigMergeValue,
|
|
205
|
+
persisted: ConfigMergeValue,
|
|
206
|
+
): ConfigMergeValue | null {
|
|
207
|
+
const baselineSet = indexDisabledModels(baseline);
|
|
208
|
+
const liveSet = indexDisabledModels(live);
|
|
209
|
+
const persistedSet = indexDisabledModels(persisted);
|
|
210
|
+
if (!baselineSet || !liveSet || !persistedSet) return null;
|
|
211
|
+
const order = [...liveSet.order, ...persistedSet.order.filter(id => !liveSet.members.has(id))];
|
|
212
|
+
const merged: string[] = [];
|
|
213
|
+
for (const id of order) {
|
|
214
|
+
if (baselineSet.members.has(id) && (!liveSet.members.has(id) || !persistedSet.members.has(id))) continue;
|
|
215
|
+
merged.push(id);
|
|
216
|
+
}
|
|
217
|
+
return merged;
|
|
218
|
+
}
|
|
219
|
+
|
|
127
220
|
function indexCustomModels(value: ConfigMergeValue): IndexedCustomModels | null {
|
|
128
221
|
if (!Array.isArray(value)) return null;
|
|
129
222
|
const order: string[] = [];
|
|
@@ -192,7 +285,10 @@ function reconcileConfigRecord(
|
|
|
192
285
|
: key === "customModels"
|
|
193
286
|
? reconcileCustomModels(baselineValue, liveValue, persistedValue)
|
|
194
287
|
?? reconcileConfigValue(baselineValue, liveValue, persistedValue)
|
|
195
|
-
:
|
|
288
|
+
: key === "disabledModels"
|
|
289
|
+
? reconcileDisabledModels(baselineValue, liveValue, persistedValue)
|
|
290
|
+
?? reconcileConfigValue(baselineValue, liveValue, persistedValue)
|
|
291
|
+
: reconcileConfigValue(baselineValue, liveValue, persistedValue, key === "providers");
|
|
196
292
|
if (merged === MISSING_CONFIG_VALUE) delete live[key];
|
|
197
293
|
else live[key] = merged;
|
|
198
294
|
}
|
|
@@ -302,7 +398,11 @@ function readPersistedServerBinding(
|
|
|
302
398
|
}
|
|
303
399
|
|
|
304
400
|
/**
|
|
305
|
-
* The save entry point for every writer holding a LIVE server config
|
|
401
|
+
* The save entry point for every writer holding a LIVE server config, and for a
|
|
402
|
+
* detached snapshot armed through {@link armDetachedConfigBaseline}. The live
|
|
403
|
+
* policy keeps `hostname`/`port` and disk-only keys out of the merge; a detached
|
|
404
|
+
* snapshot has neither hazard, so it rebases every field against its arming
|
|
405
|
+
* baseline instead.
|
|
306
406
|
*
|
|
307
407
|
* Conflict policy, chosen deliberately:
|
|
308
408
|
* - disk changed, we did not → their hand edit wins;
|
|
@@ -312,8 +412,9 @@ function readPersistedServerBinding(
|
|
|
312
412
|
* live state edited that same row;
|
|
313
413
|
* - file missing/unreadable → save what we have, no throw.
|
|
314
414
|
*
|
|
315
|
-
* Custom-model rows are merged by their stable `id`,
|
|
316
|
-
* edits and deletions across stale whole-config
|
|
415
|
+
* Custom-model rows are merged by their stable `id`, and `disabledModels` by
|
|
416
|
+
* member, preserving independent edits and deletions across stale whole-config
|
|
417
|
+
* saves.
|
|
317
418
|
*/
|
|
318
419
|
export function saveConfigPreservingClaudeCode(config: OcxConfig): void {
|
|
319
420
|
const pinError = configReasoningPinsConfigError(config);
|
|
@@ -327,28 +428,43 @@ export function saveConfigPreservingClaudeCode(config: OcxConfig): void {
|
|
|
327
428
|
if (baseline && onDisk !== undefined) {
|
|
328
429
|
const persistedDiagnostics = configDiagnosticsFromRaw(JSON.stringify(onDisk));
|
|
329
430
|
if (persistedDiagnostics.source === "file") {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
431
|
+
// A detached snapshot diverged only where this pipeline mutated it (model
|
|
432
|
+
// discovery fields for the auto-refresh tick): it cannot express a
|
|
433
|
+
// deliberate deletion, so the disk-only-key and listener-binding skips
|
|
434
|
+
// below would only discard concurrent hand edits. It merges every top-level
|
|
435
|
+
// key — including configRebaseProvenance, so a cooperating writer's
|
|
436
|
+
// deletion marker adopted from disk is honored instead of silently dropped —
|
|
437
|
+
// and captures deletion intent from the current disk snapshot before the
|
|
438
|
+
// merge can temporarily restore a key and invalidate its deletion marker.
|
|
439
|
+
const detached = detachedConfigSnapshots.has(config);
|
|
440
|
+
const deletedKeys = detached
|
|
441
|
+
? configRebaseDeletionKeys(persistedDiagnostics.config)
|
|
442
|
+
: configRebaseDeletionKeys(config);
|
|
443
|
+
const skipped = detached
|
|
444
|
+
? new Set(["claudeCode"])
|
|
445
|
+
: new Set(["hostname", "port", "claudeCode", CONFIG_REBASE_PROVENANCE_KEY]);
|
|
446
|
+
if (!detached) {
|
|
447
|
+
const provenanceExists = configHasRebaseProvenance(config);
|
|
448
|
+
// Only keys this live config is actually known to have diverged on may be
|
|
449
|
+
// rebased. The baseline is captured once when the server arms it, so any key
|
|
450
|
+
// that appeared on disk afterwards — through saveConfig(), a hand edit, or
|
|
451
|
+
// another process — is absent from the baseline as well as from the live
|
|
452
|
+
// config. Reconciling those keys reads "live never changed this" and adopts
|
|
453
|
+
// the disk value, which resurrects a field the live writer had deliberately
|
|
454
|
+
// deleted (#1462 regression: PUT /api/grok/selection with an empty list).
|
|
455
|
+
// Restrict the merge to keys the baseline knew about, plus keys the live
|
|
456
|
+
// config still carries; a key that exists only on disk is left to the
|
|
457
|
+
// ordinary whole-config write below.
|
|
458
|
+
const rebaseableKeys = new Set([
|
|
459
|
+
...Object.keys(baseline as unknown as Record<string, unknown>),
|
|
460
|
+
...Object.keys(config as unknown as Record<string, unknown>),
|
|
461
|
+
...(provenanceExists
|
|
462
|
+
? Object.keys(persistedDiagnostics.config as unknown as Record<string, unknown>)
|
|
463
|
+
: []),
|
|
464
|
+
]);
|
|
465
|
+
for (const key of Object.keys(persistedDiagnostics.config as unknown as Record<string, unknown>)) {
|
|
466
|
+
if (!rebaseableKeys.has(key)) skipped.add(key);
|
|
467
|
+
}
|
|
352
468
|
}
|
|
353
469
|
reconcileConfigRecord(
|
|
354
470
|
config as unknown as Record<string, unknown>,
|
|
@@ -356,7 +472,9 @@ export function saveConfigPreservingClaudeCode(config: OcxConfig): void {
|
|
|
356
472
|
persistedDiagnostics.config as unknown as Record<string, unknown>,
|
|
357
473
|
skipped,
|
|
358
474
|
);
|
|
359
|
-
for (const key of deletedKeys)
|
|
475
|
+
for (const key of deletedKeys) {
|
|
476
|
+
delete (config as unknown as Record<string, unknown>)[key];
|
|
477
|
+
}
|
|
360
478
|
}
|
|
361
479
|
}
|
|
362
480
|
if (claudeCodeBaseline.has(config)) {
|
|
@@ -420,10 +420,9 @@ export function degradedCredentialGroupsWarning(rawParsed: unknown): string | nu
|
|
|
420
420
|
if (!pool || pool.credentialGroups === undefined) return null;
|
|
421
421
|
const parsed = credentialGroupsSchema.safeParse(pool.credentialGroups);
|
|
422
422
|
if (parsed.success) return null;
|
|
423
|
-
// Every issue message is redacted before it is joined. The custom messages
|
|
424
|
-
//
|
|
425
|
-
//
|
|
426
|
-
// — a config file is exactly where a pasted token ends up in the wrong field.
|
|
423
|
+
// Every issue message is redacted before it is joined. The custom messages now name
|
|
424
|
+
// group/member positions instead of the offending strings; the redaction stays as a
|
|
425
|
+
// second layer for any schema default message that still embeds a value.
|
|
427
426
|
const details = parsed.error.issues.map(issue => redactSecretString(issue.message)).join("; ");
|
|
428
427
|
return `pool.credentialGroups is invalid (${details}) — declared quota grouping is disabled; other pool settings were preserved`;
|
|
429
428
|
}
|
package/src/config.ts
CHANGED
|
@@ -133,8 +133,8 @@ export {
|
|
|
133
133
|
withExpectedConfigGenerationSync,
|
|
134
134
|
} from "./config/mutation-lock";
|
|
135
135
|
export {
|
|
136
|
-
armClaudeCodeBaseline,
|
|
137
|
-
adoptPersistedProviderIntoLiveConfig,
|
|
136
|
+
armClaudeCodeBaseline, armDetachedConfigBaseline,
|
|
137
|
+
adoptPersistedClaudeCode, adoptPersistedProviderIntoLiveConfig,
|
|
138
138
|
claudeCodeBaselineArmed,
|
|
139
139
|
reconcileLiveConfigFromDisk,
|
|
140
140
|
saveConfigPreservingClaudeCode,
|