@bitkyc08/opencodex 2.64.0-preview.20260923 → 2.65.0-preview.20260925
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 +28 -25
- package/bin/ocx.mjs +16 -2
- package/gui/dist/assets/App-8NMiZxT0.css +1 -0
- package/gui/dist/assets/App-CsYvvpr3.js +50 -0
- package/gui/dist/assets/Tray-DUvc_Wul.js +1 -0
- package/gui/dist/assets/{index-DiBRuK-d.css → index--EWgGQvZ.css} +1 -1
- package/gui/dist/assets/{index-BEq4OCOz.js → index-BB0iHG8-.js} +12 -12
- package/gui/dist/assets/tray-data-f0lTZ4sF.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -4
- package/src/adapters/anthropic.ts +30 -1
- package/src/adapters/claude-cli/adapter.ts +218 -0
- package/src/adapters/claude-cli/profiles.ts +38 -0
- package/src/adapters/codebuddy/profiles.ts +2 -0
- package/src/adapters/coding-agent/profile.ts +12 -4
- package/src/adapters/coding-agent/turn.ts +16 -6
- package/src/adapters/command-code-tool-text.ts +133 -23
- package/src/adapters/cursor/catalog.ts +4 -8
- package/src/adapters/cursor/discovery.ts +10 -5
- package/src/adapters/cursor/effort-map.ts +2 -6
- package/src/adapters/cursor/envelope-echo.ts +10 -5
- package/src/adapters/cursor/request-builder.ts +9 -4
- package/src/adapters/cursor/thread-continuity.ts +53 -0
- package/src/adapters/cursor.ts +29 -13
- package/src/adapters/devin/cloud-direct/chat.ts +3 -1
- package/src/adapters/google-tool-schema.ts +26 -3
- package/src/adapters/identity.ts +214 -2
- package/src/adapters/openai-chat/passthrough.ts +1 -0
- package/src/adapters/openai-chat/serialized-tool-call-content.ts +569 -0
- package/src/adapters/openai-chat.ts +43 -15
- package/src/adapters/openai-responses/canonical-forward.ts +17 -10
- package/src/adapters/openai-responses/passthrough.ts +19 -4
- package/src/adapters/openai-responses/request-strips.ts +25 -0
- package/src/adapters/openai-responses/web-search.ts +22 -4
- package/src/adapters/qoder/profiles.ts +2 -0
- package/src/adapters/registry.ts +8 -0
- package/src/adapters/run-turn-queue.ts +7 -0
- package/src/bridge/response-json.ts +6 -4
- package/src/bridge/sse.ts +7 -5
- package/src/claude/alias.ts +73 -26
- package/src/claude/context-windows.ts +15 -6
- package/src/claude/desktop-3p-library.ts +3 -2
- package/src/claude/desktop-3p.ts +1 -1
- package/src/claude/desktop-first-party.ts +63 -22
- package/src/claude/desktop-picker-profile.ts +302 -0
- package/src/claude/desktop-picker.ts +365 -0
- package/src/claude/desktop-risk.ts +20 -0
- package/src/claude/gateway-cache.ts +13 -4
- package/src/claude/inbound.ts +5 -2
- package/src/claude/intercept/connect-proxy.ts +106 -29
- package/src/claude/intercept/local-ca.ts +76 -18
- package/src/claude/intercept/model-bindings.ts +145 -0
- package/src/claude/intercept/picker-bootstrap.ts +141 -0
- package/src/claude/intercept/picker-ca.ts +126 -0
- package/src/claude/intercept/picker-listener.ts +213 -0
- package/src/claude/intercept/picker-models.ts +101 -0
- package/src/claude/intercept/picker-runtime.ts +330 -0
- package/src/claude/intercept/picker-trust.ts +125 -0
- package/src/claude/intercept/runtime.ts +149 -2
- package/src/claude/model-info.ts +18 -4
- package/src/cli/agent.ts +16 -2
- package/src/cli/capabilities.ts +71 -2
- package/src/cli/claude-desktop.ts +319 -33
- package/src/cli/claude.ts +15 -7
- package/src/cli/codex-shim-autorestore.ts +1 -0
- package/src/cli/dispatch.ts +29 -23
- package/src/cli/effort.ts +16 -6
- package/src/cli/ensure-desired-integrations.ts +29 -4
- package/src/cli/ready.ts +1 -1
- package/src/cli/registry.ts +8 -0
- package/src/cli/restart-scope.ts +9 -1
- package/src/cli/runtime-api.ts +17 -0
- package/src/cli/system-command.ts +11 -12
- package/src/client/connect.ts +12 -2
- package/src/client/state.ts +39 -1
- package/src/clients/config-export.ts +57 -9
- package/src/codex/auth-context.ts +51 -11
- package/src/codex/catalog/derive-entry.ts +5 -5
- package/src/codex/catalog/gather-capture.ts +25 -6
- package/src/codex/catalog/metadata.ts +3 -3
- package/src/codex/catalog/parsing.ts +39 -1
- package/src/codex/catalog/provider-models.ts +8 -6
- package/src/codex/catalog/retained-sync.ts +17 -7
- package/src/codex/catalog/sync.ts +2 -2
- package/src/codex/desktop-app-restart.ts +8 -0
- package/src/codex/desktop-switches.ts +9 -1
- package/src/codex/home.ts +43 -4
- package/src/codex/inject/config-toml.ts +137 -0
- package/src/codex/inject/plan.ts +23 -0
- package/src/codex/inject/remove.ts +21 -1
- package/src/codex/inject.ts +63 -8
- package/src/codex/journal.ts +36 -0
- package/src/codex/main-account-hard-lock.ts +14 -1
- package/src/codex/main-account-policy-wait.ts +109 -0
- package/src/codex/native-profile-startup.ts +24 -6
- package/src/codex/native-residue.ts +28 -10
- package/src/codex/quota-types.ts +8 -1
- package/src/codex/quota.ts +49 -7
- package/src/codex/runtime.ts +31 -10
- package/src/combos/failover.ts +7 -6
- package/src/combos/resolve.ts +91 -14
- package/src/combos/types.ts +18 -1
- package/src/config/load-degrade.ts +2 -0
- package/src/config/schema/config-schema.ts +21 -2
- package/src/config/schema/leaf-validators.ts +6 -3
- package/src/config/subagent-models.ts +3 -1
- package/src/generated/compatibility-version.json +275 -163
- package/src/images/artifacts.ts +14 -0
- package/src/integrations/owned-refresh.ts +1 -1
- package/src/integrations/ownership-policy.ts +32 -1
- package/src/integrations/state.ts +3 -1
- package/src/integrations/writer.ts +9 -0
- package/src/lib/config-ownership.ts +3 -0
- package/src/lib/errors.ts +87 -0
- package/src/lib/package-tree-integrity.ts +1 -1
- package/src/lib/request-execution-budget.ts +42 -14
- package/src/lib/request-resend-gate.ts +4 -3
- package/src/lib/retry-delay.ts +7 -1
- package/src/lib/tool-envelope-echo-filter.ts +236 -0
- package/src/lib/upstream-retry.ts +20 -12
- package/src/oauth/index.ts +1 -0
- package/src/oauth/kiro-credentials.ts +14 -1
- package/src/oauth/login-cli.ts +1 -0
- package/src/providers/derive.ts +4 -0
- package/src/providers/fast-opt-in.ts +31 -0
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/provider-id-rewrite.ts +2 -0
- package/src/providers/quota/vendor-probes-key.ts +8 -2
- package/src/providers/registry/entries-core.ts +45 -2
- package/src/providers/registry/entries-extended.ts +99 -12
- package/src/providers/registry/model-ids.ts +2 -0
- package/src/providers/registry/types.ts +7 -1
- package/src/providers/resolved-model-policy.ts +11 -4
- package/src/providers/service-tier.ts +10 -3
- package/src/responses/code-mode-helper-compat.ts +3 -0
- package/src/responses/parser.ts +45 -3
- package/src/router.ts +6 -0
- package/src/server/auth-cors.ts +8 -0
- package/src/server/chat-completions.ts +3 -1
- package/src/server/claude-messages.ts +69 -14
- package/src/server/grok-upstream-envelope-echo.ts +120 -0
- package/src/server/index/claude-intercept-lifecycle.ts +24 -0
- package/src/server/index/serve-options.ts +2 -2
- package/src/server/index.ts +7 -0
- package/src/server/management/agent-settings-routes.ts +204 -112
- package/src/server/management/claude-desktop-picker-routes.ts +128 -0
- package/src/server/management/combo-routes.ts +59 -2
- package/src/server/management/config-routes.ts +81 -39
- package/src/server/management/context.ts +3 -0
- package/src/server/management/native-integration-routes.ts +169 -93
- package/src/server/management/provider-routes.ts +46 -4
- package/src/server/management/route-registry.ts +10 -0
- package/src/server/management/routing-profile-routes.ts +9 -0
- package/src/server/management/sidebar-routes.ts +52 -0
- package/src/server/management-api.ts +2 -0
- package/src/server/proxy-liveness.ts +38 -3
- package/src/server/request-log-account-rotation.ts +53 -0
- package/src/server/request-log.ts +3 -16
- package/src/server/responses/adapter-dispatch.ts +17 -1
- package/src/server/responses/agent-task-recovery.ts +49 -26
- package/src/server/responses/codex-ws-exchange.ts +18 -7
- package/src/server/responses/codex-ws-wire.ts +27 -4
- package/src/server/responses/core-combo.ts +21 -3
- package/src/server/responses/core-normalize.ts +7 -0
- package/src/server/responses/core-opaque-recovery.ts +3 -2
- package/src/server/responses/encrypted-payload.ts +15 -14
- package/src/server/responses/fetch-helpers.ts +1 -1
- package/src/server/responses/input-admission.ts +10 -6
- package/src/server/responses/native-response-control.ts +2 -2
- package/src/server/responses/passthrough-delivery.ts +182 -12
- package/src/server/responses/passthrough-dispatch.ts +102 -44
- package/src/server/responses/policy-fallback.ts +3 -0
- package/src/server/responses/policy-refusal.ts +67 -0
- package/src/server/responses/request-send-budget.ts +4 -0
- package/src/server/responses/run-turn-execution.ts +42 -11
- package/src/server/responses/ws-upstream.ts +2 -1
- package/src/server/system-env-shell.ts +0 -1
- package/src/server/system-env.ts +25 -4
- package/src/service/cli.ts +34 -3
- package/src/tray/assets/opencodex-tray-offline-update.ico +0 -0
- package/src/tray/assets/opencodex-tray-online-update.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning-update.ico +0 -0
- package/src/tray/windows-tray.ps1 +188 -7
- package/src/tray/windows.ts +23 -2
- package/src/types/config.ts +52 -12
- package/src/types/provider.ts +22 -5
- package/src/types/tools.ts +11 -5
- package/src/types.ts +1 -0
- package/src/update/async-check.ts +109 -0
- package/src/update/badge.ts +11 -17
- package/src/update/check-types.ts +9 -0
- package/src/update/desktop-badge.ts +102 -0
- package/src/update/index.ts +48 -8
- package/src/update/install-detection.d.mts +29 -2
- package/src/update/install-detection.mjs +213 -7
- package/src/update/job.ts +17 -12
- package/src/update/notify.ts +16 -11
- package/src/update/pnpm-owner-worker.ts +12 -0
- package/src/update/refresh-scheduler.ts +141 -0
- package/src/usage/cost.ts +31 -4
- package/src/usage/expected-prices.ts +57 -0
- package/assets/download-linux.svg +0 -10
- package/assets/download-macos.svg +0 -10
- package/assets/download-windows.svg +0 -10
- package/gui/dist/assets/App-D5eN1TID.js +0 -50
- package/gui/dist/assets/App-I5AnaSLh.css +0 -1
- package/gui/dist/assets/Tray-Bh_ErQDh.js +0 -1
- package/gui/dist/assets/usage-companion-chart-DBG_37kQ.js +0 -1
package/src/claude/alias.ts
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gateway model-discovery aliases (devlog/260711_claude_inbound/020, 003 G1-G6).
|
|
3
3
|
*
|
|
4
|
-
* Claude Code's /model picker
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Claude Code's /model picker accepts discovery ids containing `claude` or
|
|
5
|
+
* `anthropic`. New routed models are exposed as `ocx-claude-<provider>--<model>`
|
|
6
|
+
* with an honest display_name. The id must contain `claude` so the picker keeps
|
|
7
|
+
* it, but must not START with `claude-`: Claude Code 2.1.278 treats an
|
|
8
|
+
* unrecognized `claude-` id as its own model and ignores
|
|
9
|
+
* CLAUDE_CODE_MAX_CONTEXT_TOKENS unless DISABLE_COMPACT=1. Starting with
|
|
10
|
+
* `ocx-claude-` keeps the real window and leaves compact enabled.
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
* - `claude
|
|
12
|
-
*
|
|
13
|
-
* contained the two-char sequences `~s` / `~t` keeps resolving.
|
|
14
|
-
* - `claude-ocx2-` (v2) —
|
|
15
|
-
*
|
|
16
|
-
* models always mint v2 so they cannot collide with v1 literals.
|
|
12
|
+
* Persisted ids stay decodable:
|
|
13
|
+
* - `ocx-claude-` — current plain ids. Decode is literal.
|
|
14
|
+
* - `claude-ocx-` (v1) — legacy plain ids. Decode stays literal, so a persisted
|
|
15
|
+
* model id that contained the two-char sequences `~s` / `~t` keeps resolving.
|
|
16
|
+
* - `claude-ocx2-` (v2) — escape encoding (`/` → `~s`, `~` → `~t`). Decode
|
|
17
|
+
* expands those escapes. New slash/tilde models still mint v2.
|
|
17
18
|
*
|
|
18
19
|
* Reversibility rules:
|
|
19
20
|
* - providers containing `--` or `/` are not aliased (split boundary safety);
|
|
20
21
|
* - model ids MAY contain `/` or `~` — minted under the v2 prefix with escapes
|
|
21
22
|
* (e.g. openrouter `anthropic/claude-opus-4-8` →
|
|
22
|
-
* `
|
|
23
|
+
* `ocx-claude2-openrouter--anthropic~sclaude-opus-4-8`);
|
|
23
24
|
* - model ids MAY contain `--` (resolve splits on the FIRST `--` only);
|
|
24
25
|
* - native OpenAI slugs use the pseudo-provider `native` and resolve back to
|
|
25
26
|
* the bare slug; a real provider named "native" is therefore never aliased.
|
|
@@ -27,15 +28,19 @@
|
|
|
27
28
|
|
|
28
29
|
import { desktop3pAlias } from "./desktop-3p";
|
|
29
30
|
|
|
30
|
-
/**
|
|
31
|
+
/** Current plain prefix. Contains "claude" but does not start with "claude-". */
|
|
32
|
+
export const CLAUDE_ALIAS_PREFIX_CURRENT = "ocx-claude-";
|
|
33
|
+
/** Current escape-encoded prefix (`~s`/`~t` expanded on decode). */
|
|
34
|
+
export const CLAUDE_ALIAS_PREFIX_CURRENT_V2 = "ocx-claude2-";
|
|
35
|
+
/** Legacy plain prefix. Still decoded; no longer minted. */
|
|
31
36
|
export const CLAUDE_ALIAS_PREFIX_V1 = "claude-ocx-";
|
|
32
|
-
/**
|
|
37
|
+
/** Legacy escape-encoded prefix. Still decoded; no longer minted. */
|
|
33
38
|
export const CLAUDE_ALIAS_PREFIX_V2 = "claude-ocx2-";
|
|
34
39
|
/**
|
|
35
40
|
* Current write prefix for plain (unescaped) model ids.
|
|
36
|
-
* Escape-needing models mint {@link
|
|
41
|
+
* Escape-needing models mint {@link CLAUDE_ALIAS_PREFIX_CURRENT_V2} instead.
|
|
37
42
|
*/
|
|
38
|
-
export const CLAUDE_ALIAS_PREFIX =
|
|
43
|
+
export const CLAUDE_ALIAS_PREFIX = CLAUDE_ALIAS_PREFIX_CURRENT;
|
|
39
44
|
|
|
40
45
|
/** Encoded `/` inside the model portion of a v2 Claude Code alias. */
|
|
41
46
|
const CLAUDE_ALIAS_SLASH_ENC = "~s";
|
|
@@ -90,9 +95,42 @@ export function aliasForRoute(provider: string, modelId: string): string | null
|
|
|
90
95
|
if (!provider || provider.includes("--") || provider.includes("/") || provider === NATIVE_PSEUDO_PROVIDER) return null;
|
|
91
96
|
if (!modelId) return null;
|
|
92
97
|
if (modelNeedsEscapeEncoding(modelId)) {
|
|
93
|
-
return `${
|
|
98
|
+
return `${CLAUDE_ALIAS_PREFIX_CURRENT_V2}${provider}--${encodeModelId(modelId)}`;
|
|
94
99
|
}
|
|
95
|
-
return `${
|
|
100
|
+
return `${CLAUDE_ALIAS_PREFIX_CURRENT}${provider}--${modelId}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The spelling a release before `ocx-claude-` minted for the same route. Nothing
|
|
105
|
+
* is minted with it any more; it exists so a selector already saved in Claude
|
|
106
|
+
* Code's settings.json keeps its context-window lookup (context-windows.ts).
|
|
107
|
+
*/
|
|
108
|
+
function toLegacyAlias(alias: string | null): string | null {
|
|
109
|
+
if (!alias) return null;
|
|
110
|
+
if (alias.startsWith(CLAUDE_ALIAS_PREFIX_CURRENT_V2)) return CLAUDE_ALIAS_PREFIX_V2 + alias.slice(CLAUDE_ALIAS_PREFIX_CURRENT_V2.length);
|
|
111
|
+
if (alias.startsWith(CLAUDE_ALIAS_PREFIX_CURRENT)) return CLAUDE_ALIAS_PREFIX_V1 + alias.slice(CLAUDE_ALIAS_PREFIX_CURRENT.length);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The current spelling of a selector that may still use a legacy prefix. Legacy and
|
|
117
|
+
* current prefixes decode to the same route (plain stays literal, v2 expands escapes), so
|
|
118
|
+
* swapping the prefix is safe. Only the prefix changes: a trailing `[1m]` marker survives.
|
|
119
|
+
*/
|
|
120
|
+
export function currentClaudeAliasSpelling(selector: string): string {
|
|
121
|
+
if (selector.startsWith(CLAUDE_ALIAS_PREFIX_V2)) return CLAUDE_ALIAS_PREFIX_CURRENT_V2 + selector.slice(CLAUDE_ALIAS_PREFIX_V2.length);
|
|
122
|
+
if (selector.startsWith(CLAUDE_ALIAS_PREFIX_V1)) return CLAUDE_ALIAS_PREFIX_CURRENT + selector.slice(CLAUDE_ALIAS_PREFIX_V1.length);
|
|
123
|
+
return selector;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Legacy `claude-ocx-`/`claude-ocx2-` spelling of {@link aliasForRoute}. */
|
|
127
|
+
export function legacyAliasForRoute(provider: string, modelId: string): string | null {
|
|
128
|
+
return toLegacyAlias(aliasForRoute(provider, modelId));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Legacy `claude-ocx-`/`claude-ocx2-` spelling of {@link aliasForNative}. */
|
|
132
|
+
export function legacyAliasForNative(slug: string): string | null {
|
|
133
|
+
return toLegacyAlias(aliasForNative(slug));
|
|
96
134
|
}
|
|
97
135
|
|
|
98
136
|
/** Alias for a native OpenAI slug (bare model id, no provider namespace). */
|
|
@@ -100,9 +138,9 @@ export function aliasForNative(slug: string): string | null {
|
|
|
100
138
|
// Reject "/" — native ids are bare slugs. Literal `~` is fine via v2 + ~t.
|
|
101
139
|
if (!slug || slug.includes("/") || slug.includes("--")) return null;
|
|
102
140
|
if (modelNeedsEscapeEncoding(slug)) {
|
|
103
|
-
return `${
|
|
141
|
+
return `${CLAUDE_ALIAS_PREFIX_CURRENT_V2}${NATIVE_PSEUDO_PROVIDER}--${encodeModelId(slug)}`;
|
|
104
142
|
}
|
|
105
|
-
return `${
|
|
143
|
+
return `${CLAUDE_ALIAS_PREFIX_CURRENT}${NATIVE_PSEUDO_PROVIDER}--${slug}`;
|
|
106
144
|
}
|
|
107
145
|
|
|
108
146
|
/**
|
|
@@ -110,7 +148,15 @@ export function aliasForNative(slug: string): string | null {
|
|
|
110
148
|
* routed -> "<provider>/<model>", native -> bare slug. Null when not an alias.
|
|
111
149
|
*/
|
|
112
150
|
export function resolveAlias(id: string): string | null {
|
|
113
|
-
//
|
|
151
|
+
// Current v2 before legacy v2, then plain prefixes. `ocx-claude2-` and
|
|
152
|
+
// `claude-ocx2-` are disjoint from their plain siblings.
|
|
153
|
+
if (id.startsWith(CLAUDE_ALIAS_PREFIX_CURRENT_V2)) {
|
|
154
|
+
const parts = splitAlias(id, CLAUDE_ALIAS_PREFIX_CURRENT_V2);
|
|
155
|
+
if (!parts) return null;
|
|
156
|
+
const model = decodeEscapedModelId(parts.model);
|
|
157
|
+
if (!model) return null;
|
|
158
|
+
return parts.provider === NATIVE_PSEUDO_PROVIDER ? model : `${parts.provider}/${model}`;
|
|
159
|
+
}
|
|
114
160
|
if (id.startsWith(CLAUDE_ALIAS_PREFIX_V2)) {
|
|
115
161
|
const parts = splitAlias(id, CLAUDE_ALIAS_PREFIX_V2);
|
|
116
162
|
if (!parts) return null;
|
|
@@ -118,8 +164,9 @@ export function resolveAlias(id: string): string | null {
|
|
|
118
164
|
if (!model) return null;
|
|
119
165
|
return parts.provider === NATIVE_PSEUDO_PROVIDER ? model : `${parts.provider}/${model}`;
|
|
120
166
|
}
|
|
121
|
-
|
|
122
|
-
|
|
167
|
+
for (const prefix of [CLAUDE_ALIAS_PREFIX_CURRENT, CLAUDE_ALIAS_PREFIX_V1]) {
|
|
168
|
+
if (!id.startsWith(prefix)) continue;
|
|
169
|
+
const parts = splitAlias(id, prefix);
|
|
123
170
|
if (!parts) return null;
|
|
124
171
|
// Literal decode — preserves pre-escape aliases whose model id contained
|
|
125
172
|
// the two-char sequences ~s / ~t.
|
|
@@ -131,11 +178,11 @@ export function resolveAlias(id: string): string | null {
|
|
|
131
178
|
/**
|
|
132
179
|
* Claude Code (CLI) surface alias — devlog 050 + audit 051 #2.
|
|
133
180
|
*
|
|
134
|
-
* The readable `claude
|
|
181
|
+
* The readable `ocx-claude*` form when representable; otherwise the desktop-3p
|
|
135
182
|
* hash so the model still appears in discovery (collisions follow the same
|
|
136
183
|
* first-wins policy as the desktop registry — audit 051 #1). Real Anthropic
|
|
137
184
|
* models pass through unchanged (they must keep hitting the sk-ant passthrough).
|
|
138
|
-
*
|
|
185
|
+
* Old `claude-ocx*` ids keep decoding forever in resolveInboundModel, so ids persisted
|
|
139
186
|
* in Claude Code's settings.json never break when the surface style changes.
|
|
140
187
|
*/
|
|
141
188
|
export function claudeCodeAlias(provider: string, modelId: string): string {
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
* (devlog/260712_cli_context_cache/010 B2, audit R2#1/R3#1/R3#4/R4#3).
|
|
4
4
|
*
|
|
5
5
|
* The map registers EVERY selector form a Claude Code model slot might store —
|
|
6
|
-
* bare native slug, provider/id, desktop3p alias,
|
|
6
|
+
* bare native slug, provider/id, desktop3p alias, current ocx-claude-* alias and
|
|
7
|
+
* the legacy claude-ocx-* spelling a saved selector may still carry —
|
|
7
8
|
* with first-wins dedupe (mirrors the desktop3p registry collision policy).
|
|
8
9
|
* Values are authoritative context windows only (native override table /
|
|
9
10
|
* adapter-reported CatalogModel.contextWindow); nothing is guessed.
|
|
10
11
|
*/
|
|
11
|
-
import { aliasForNative, aliasForRoute } from "./alias";
|
|
12
|
+
import { aliasForNative, aliasForRoute, currentClaudeAliasSpelling, legacyAliasForNative, legacyAliasForRoute } from "./alias";
|
|
12
13
|
import { desktop3pAlias } from "./desktop-3p";
|
|
13
14
|
import { nativeOpenAiContextWindow, type CatalogModel, type NativeContextLimitsInput } from "../codex/catalog";
|
|
14
15
|
|
|
@@ -62,9 +63,12 @@ function inAutoCompactRange(value: number): boolean {
|
|
|
62
63
|
|
|
63
64
|
/**
|
|
64
65
|
* Resolve the auto-context mode from claudeCode config. Disabled when the user
|
|
65
|
-
* turned it off OR when
|
|
66
|
-
*
|
|
67
|
-
*
|
|
66
|
+
* turned it off OR when maxContextTokens is set. That override injects only
|
|
67
|
+
* CLAUDE_CODE_MAX_CONTEXT_TOKENS: compact stays enabled (no DISABLE_COMPACT) and
|
|
68
|
+
* Claude Code compacts against that window for ocx-claude-* ids, so no
|
|
69
|
+
* CLAUDE_CODE_AUTO_COMPACT_WINDOW is injected beside it and [1m] auto-marking stays
|
|
70
|
+
* off. maxContextTokens accepts values outside the compact variable's 100k–1M
|
|
71
|
+
* range, so deriving that variable from it could only produce ignored values.
|
|
68
72
|
*
|
|
69
73
|
* `envOverride` is the raw CLAUDE_CODE_AUTO_COMPACT_WINDOW the USER already
|
|
70
74
|
* exported (user-wins injection keeps it): a valid value drives the marking
|
|
@@ -139,6 +143,7 @@ export function buildClaudeContextWindows(
|
|
|
139
143
|
put(slug, window);
|
|
140
144
|
put(desktop3pAlias("native", slug), window);
|
|
141
145
|
put(aliasForNative(slug), window);
|
|
146
|
+
put(legacyAliasForNative(slug), window);
|
|
142
147
|
}
|
|
143
148
|
// Anthropic passthrough guard (audit 021 #3): canonical claude ids ride the
|
|
144
149
|
// subscription passthrough — marking a sub-1M one would strap [1m]/1M-beta onto
|
|
@@ -161,6 +166,7 @@ export function buildClaudeContextWindows(
|
|
|
161
166
|
put(`${m.provider}/${m.id}`, window);
|
|
162
167
|
put(desktop3pAlias(m.provider, m.id), window);
|
|
163
168
|
put(aliasForRoute(m.provider, m.id), window);
|
|
169
|
+
put(legacyAliasForRoute(m.provider, m.id), window);
|
|
164
170
|
if (bareCounts.get(m.id) === 1) put(m.id, window);
|
|
165
171
|
}
|
|
166
172
|
return out;
|
|
@@ -204,8 +210,11 @@ export function effectiveModelEnv(
|
|
|
204
210
|
): Record<string, string> {
|
|
205
211
|
const out: Record<string, string> = {};
|
|
206
212
|
const auto = autoOverride ?? resolveAutoContext(claudeCode);
|
|
213
|
+
// A slot still configured with a legacy claude-ocx selector is emitted in its current
|
|
214
|
+
// ocx-claude spelling. The route is identical, but Claude Code applies the context window
|
|
215
|
+
// (and keeps compact) only for ids that do not start with "claude-".
|
|
207
216
|
const set = (name: string, value: string | undefined) => {
|
|
208
|
-
const marked = withOneMillionMarker(value, windows, auto);
|
|
217
|
+
const marked = withOneMillionMarker(value === undefined ? undefined : currentClaudeAliasSpelling(value), windows, auto);
|
|
209
218
|
if (marked) out[name] = marked;
|
|
210
219
|
};
|
|
211
220
|
set("ANTHROPIC_MODEL", claudeCode?.model);
|
|
@@ -42,6 +42,8 @@ export interface Desktop3pMetadata {
|
|
|
42
42
|
[key: string]: unknown;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
export const DESKTOP_PICKER_ENTRY_NAME = "opencodex-picker";
|
|
46
|
+
|
|
45
47
|
export function parseMetadata(path: string): Desktop3pMetadata {
|
|
46
48
|
if (!existsSync(path)) return { entries: [] };
|
|
47
49
|
const parsed = JSON.parse(readFileSync(path, "utf8")) as Partial<Desktop3pMetadata>;
|
|
@@ -56,7 +58,7 @@ export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
export function isOwnedDesktopEntry(entry: Desktop3pMetadataEntry | undefined): boolean {
|
|
59
|
-
return entry?.name === "opencodex" || entry?.name === "opencodex-standard";
|
|
61
|
+
return entry?.name === "opencodex" || entry?.name === "opencodex-standard" || entry?.name === DESKTOP_PICKER_ENTRY_NAME;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
/** A gateway row is removable; the selected standard row must always remain. */
|
|
@@ -86,4 +88,3 @@ export function readDesktopProfileForeignKeys(path: string): Record<string, unkn
|
|
|
86
88
|
Object.entries(parsed).filter(([key]) => !OPENCODEX_DESKTOP_PROFILE_KEYS.has(key)),
|
|
87
89
|
);
|
|
88
90
|
}
|
|
89
|
-
|
package/src/claude/desktop-3p.ts
CHANGED
|
@@ -146,7 +146,7 @@ export function legacyDesktop3pAlias(provider: string, modelId: string): string
|
|
|
146
146
|
return `claude-opus-4-${deriveDesktop3pCode(`${provider}/${modelId}`)}`;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
function displayModelId(modelId: string): string {
|
|
149
|
+
export function displayModelId(modelId: string): string {
|
|
150
150
|
return modelId
|
|
151
151
|
// Capability markers like [1m] are not name text: strip the brackets so the label
|
|
152
152
|
// reads "K3 1M", never "K3[1m]".
|
|
@@ -3,21 +3,25 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Desktop has two ways to reach opencodex:
|
|
5
5
|
*
|
|
6
|
-
* - `
|
|
7
|
-
* and remote control. Only the Claude Code process it spawns for the Code tab (and that
|
|
8
|
-
* process's subagents) is redirected, through the `HTTPS_PROXY`/`NODE_EXTRA_CA_CERTS` env
|
|
9
|
-
* in `~/.claude/settings.json` (src/claude/intercept/settings.ts) and the server's intercept
|
|
10
|
-
* pair (src/claude/intercept/runtime.ts). Nothing is written under Desktop's config library.
|
|
11
|
-
* - `gateway`: the historical third-party deployment profile (src/claude/desktop-3p.ts). The
|
|
6
|
+
* - `gateway` (default): the third-party deployment profile (src/claude/desktop-3p.ts). The
|
|
12
7
|
* whole app is switched to a gateway build; picker entries are opencodex aliases.
|
|
8
|
+
* - `first-party`: the app keeps its ordinary claude.ai login, Chat tab, connectors and remote
|
|
9
|
+
* control. Only the Claude Code process it spawns for the Code tab (and that process's
|
|
10
|
+
* subagents) is redirected, through the `HTTPS_PROXY`/`NODE_EXTRA_CA_CERTS` env in
|
|
11
|
+
* `~/.claude/settings.json` (src/claude/intercept/settings.ts) and the server's intercept pair
|
|
12
|
+
* (src/claude/intercept/runtime.ts). It sends Claude subscription traffic through a local
|
|
13
|
+
* interception proxy, so every first-party surface carries the account-risk notice in
|
|
14
|
+
* src/claude/desktop-risk.ts.
|
|
13
15
|
*
|
|
14
16
|
* The two are mutually exclusive on disk: applying one removes the other. The mode is persisted
|
|
15
|
-
* in `claudeCode.desktopMode
|
|
16
|
-
* gateway
|
|
17
|
-
*
|
|
17
|
+
* in `claudeCode.desktopMode`. Installs that predate the field keep what they run: a selected
|
|
18
|
+
* gateway row or a gateway apply marker keeps `gateway`, and first-party env that opencodex wrote
|
|
19
|
+
* into Claude Code's settings keeps `first-party` (observeClaudeDesktopMode), so moving the default
|
|
20
|
+
* to gateway never flips a working Desktop under the operator.
|
|
18
21
|
*/
|
|
19
22
|
import { getConfigDir } from "../config/paths";
|
|
20
23
|
import type { OcxConfig } from "../types";
|
|
24
|
+
import { inspectDesktop3pConfigLibrary } from "./desktop-3p";
|
|
21
25
|
import { claudeInterceptCaCertPath, ensureLocalInterceptCa } from "./intercept/local-ca";
|
|
22
26
|
import { claudeInterceptEnabled, claudeInterceptProxyPort } from "./intercept/runtime";
|
|
23
27
|
import {
|
|
@@ -33,7 +37,7 @@ import {
|
|
|
33
37
|
|
|
34
38
|
export const CLAUDE_DESKTOP_MODES = ["first-party", "gateway"] as const;
|
|
35
39
|
export type ClaudeDesktopMode = typeof CLAUDE_DESKTOP_MODES[number];
|
|
36
|
-
export const DEFAULT_CLAUDE_DESKTOP_MODE: ClaudeDesktopMode = "
|
|
40
|
+
export const DEFAULT_CLAUDE_DESKTOP_MODE: ClaudeDesktopMode = "gateway";
|
|
37
41
|
|
|
38
42
|
export function isClaudeDesktopMode(value: unknown): value is ClaudeDesktopMode {
|
|
39
43
|
return typeof value === "string" && (CLAUDE_DESKTOP_MODES as readonly string[]).includes(value);
|
|
@@ -41,15 +45,30 @@ export function isClaudeDesktopMode(value: unknown): value is ClaudeDesktopMode
|
|
|
41
45
|
|
|
42
46
|
type DesktopModeConfig = Pick<OcxConfig, "claudeCode">;
|
|
43
47
|
|
|
48
|
+
/** What the resolver may learn from disk. Only rows and settings opencodex owns count. */
|
|
49
|
+
export interface ClaudeDesktopModeObservation {
|
|
50
|
+
/** Desktop's selected config-library row is our gateway (current or drifted). */
|
|
51
|
+
ownedGatewaySelected?: boolean;
|
|
52
|
+
/** Claude Code's settings carry first-party env that opencodex wrote (applied or stale). */
|
|
53
|
+
ownedFirstPartySettings?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
/**
|
|
45
|
-
* Effective Desktop mode. An explicit `claudeCode.desktopMode` wins
|
|
46
|
-
*
|
|
47
|
-
*
|
|
57
|
+
* Effective Desktop mode. An explicit `claudeCode.desktopMode` wins. Without one, what is on disk
|
|
58
|
+
* decides for installs that predate the field: a selected owned gateway row or a persisted gateway
|
|
59
|
+
* apply marker keeps `gateway`, owned first-party settings keep `first-party`. Everything else is
|
|
60
|
+
* the gateway default. Pure: callers that decide an apply or a write pass
|
|
61
|
+
* `observeClaudeDesktopMode(config)`.
|
|
48
62
|
*/
|
|
49
|
-
export function resolveClaudeDesktopMode(
|
|
63
|
+
export function resolveClaudeDesktopMode(
|
|
64
|
+
config: DesktopModeConfig,
|
|
65
|
+
observed: ClaudeDesktopModeObservation = {},
|
|
66
|
+
): ClaudeDesktopMode {
|
|
50
67
|
const explicit = config.claudeCode?.desktopMode;
|
|
51
68
|
if (isClaudeDesktopMode(explicit)) return explicit;
|
|
69
|
+
if (observed.ownedGatewaySelected) return "gateway";
|
|
52
70
|
if (config.claudeCode?.desktopProfile?.appliedFingerprint) return "gateway";
|
|
71
|
+
if (observed.ownedFirstPartySettings) return "first-party";
|
|
53
72
|
return DEFAULT_CLAUDE_DESKTOP_MODE;
|
|
54
73
|
}
|
|
55
74
|
|
|
@@ -78,18 +97,16 @@ export function recordClaudeDesktopMode(
|
|
|
78
97
|
}
|
|
79
98
|
|
|
80
99
|
/**
|
|
81
|
-
* Mode an *apply* without an explicit choice should use.
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* later with `intercept_disabled`, which names the fix).
|
|
100
|
+
* Mode an *apply* without an explicit choice should use. With gateway as the default, first-party
|
|
101
|
+
* only comes from an explicit choice or an observed first-party install; both are honoured even
|
|
102
|
+
* when the intercept is disabled, and the apply is then refused with `intercept_disabled`, which
|
|
103
|
+
* names the fix, instead of silently replacing the operator's mode.
|
|
86
104
|
*/
|
|
87
105
|
export function resolveClaudeDesktopApplyMode(
|
|
88
106
|
config: Pick<OcxConfig, "claudeCode" | "runtimeRole">,
|
|
107
|
+
observed: ClaudeDesktopModeObservation = {},
|
|
89
108
|
): ClaudeDesktopMode {
|
|
90
|
-
|
|
91
|
-
if (resolved === "gateway" || isClaudeDesktopMode(config.claudeCode?.desktopMode)) return resolved;
|
|
92
|
-
return claudeInterceptEnabled(config) ? "first-party" : "gateway";
|
|
109
|
+
return resolveClaudeDesktopMode(config, observed);
|
|
93
110
|
}
|
|
94
111
|
|
|
95
112
|
export interface DesktopFirstPartyTarget {
|
|
@@ -151,6 +168,30 @@ export function inspectDesktopFirstParty(
|
|
|
151
168
|
};
|
|
152
169
|
}
|
|
153
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Observe what Desktop runs today for the resolver. Never throws: an unreadable library or
|
|
173
|
+
* settings file is no evidence, and a foreign proxy env is never mistaken for ours.
|
|
174
|
+
*/
|
|
175
|
+
export function observeClaudeDesktopMode(
|
|
176
|
+
config: Pick<OcxConfig, "claudeCode" | "port" | "runtimeRole">,
|
|
177
|
+
options: DesktopFirstPartyOptions = {},
|
|
178
|
+
): ClaudeDesktopModeObservation {
|
|
179
|
+
const observed: ClaudeDesktopModeObservation = {};
|
|
180
|
+
try {
|
|
181
|
+
const library = inspectDesktop3pConfigLibrary({ appliedFingerprint: config.claudeCode?.desktopProfile?.appliedFingerprint ?? null });
|
|
182
|
+
observed.ownedGatewaySelected = library.kind === "gateway_ours" || library.kind === "gateway_drifted";
|
|
183
|
+
} catch { // no-excuse-ok: catch -- an unreadable library is no gateway evidence.
|
|
184
|
+
observed.ownedGatewaySelected = false;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const kind = inspectDesktopFirstParty(config, options).settings.kind;
|
|
188
|
+
observed.ownedFirstPartySettings = kind === "applied" || kind === "stale";
|
|
189
|
+
} catch { // no-excuse-ok: catch -- unreadable settings are no first-party evidence.
|
|
190
|
+
observed.ownedFirstPartySettings = false;
|
|
191
|
+
}
|
|
192
|
+
return observed;
|
|
193
|
+
}
|
|
194
|
+
|
|
154
195
|
export type DesktopFirstPartyApplyResult =
|
|
155
196
|
| { ok: true; changed: boolean; path: string; env: ClaudeInterceptEnv; proxyPort: number }
|
|
156
197
|
| { ok: false; reason: "intercept_disabled" | "ca_unavailable" | "unreadable" | "foreign_env"; path: string };
|