@iislee/opencodex 2.31.0 → 2.35.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/README.md +3 -3
- package/bin/ocx.mjs +99 -70
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/package.json +3 -1
- package/src/adapters/anthropic.ts +104 -15
- package/src/adapters/base.ts +29 -1
- package/src/adapters/command-code.ts +42 -6
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +15 -10
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +69 -18
- package/src/adapters/cursor/effort-map.ts +79 -1
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/live-models.ts +7 -2
- package/src/adapters/cursor/live-transport.ts +14 -0
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +2 -2
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -2
- package/src/adapters/cursor/native-exec.ts +20 -0
- package/src/adapters/cursor/protobuf-request.ts +68 -14
- package/src/adapters/cursor/request-builder.ts +40 -10
- package/src/adapters/cursor/tool-definitions.ts +14 -4
- package/src/adapters/cursor/tool-result-normalize.ts +28 -5
- package/src/adapters/cursor/types.ts +20 -0
- package/src/adapters/cursor.ts +209 -38
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +173 -25
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google.ts +136 -24
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-tools.ts +55 -16
- package/src/adapters/kiro.ts +182 -23
- package/src/adapters/openai-chat.ts +300 -242
- package/src/adapters/openai-responses.ts +353 -27
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +4 -3
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +17 -16
- package/src/bridge.ts +90 -37
- package/src/chat/inbound.ts +14 -1
- package/src/chat/outbound.ts +15 -7
- package/src/claude/agents-inject.ts +8 -1
- package/src/claude/context-windows.ts +16 -9
- package/src/claude/outbound.ts +24 -11
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +116 -19
- package/src/cli/account-extended.ts +262 -29
- package/src/cli/account-main.ts +12 -12
- package/src/cli/account.ts +69 -15
- package/src/cli/agent.ts +8 -1
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-desktop.ts +31 -11
- package/src/cli/claude.ts +26 -1
- package/src/cli/dispatch.ts +256 -28
- package/src/cli/doctor.ts +126 -14
- package/src/cli/help.ts +17 -6
- package/src/cli/index.ts +39 -9
- package/src/cli/init.ts +1 -0
- package/src/cli/inspect.ts +230 -0
- package/src/cli/models-runtime.ts +95 -0
- package/src/cli/models.ts +26 -10
- package/src/cli/observe.ts +31 -8
- package/src/cli/provider-runtime.ts +16 -2
- package/src/cli/provider.ts +2 -1
- package/src/cli/registry.ts +49 -10
- package/src/cli/runtime-api.ts +51 -7
- package/src/cli/status.ts +41 -1
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +1 -1
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +34 -10
- package/src/cli/version-skew.ts +46 -0
- package/src/clients/config-export.ts +85 -3
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +21 -0
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +3 -2
- package/src/codex/app-server-processes.ts +83 -7
- package/src/codex/auth-api.ts +50 -10
- package/src/codex/auth-context.ts +21 -18
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +18 -0
- package/src/codex/catalog/effort.ts +18 -3
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/native-models.ts +5 -2
- package/src/codex/catalog/parsing.ts +72 -27
- package/src/codex/catalog/provider-fetch.ts +187 -31
- package/src/codex/catalog/sync.ts +131 -5
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-write-lock.ts +3 -1
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +27 -2
- package/src/codex/desired-state.ts +2 -2
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/history-job.ts +32 -3
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +5 -5
- package/src/codex/history-provider.ts +825 -247
- package/src/codex/history-worker.ts +8 -5
- package/src/codex/inject-coordination.ts +83 -0
- package/src/codex/inject.ts +63 -22
- package/src/codex/injected-marker.ts +1 -1
- package/src/codex/internal/history-writer.ts +4 -3
- package/src/codex/log-guard/inspect.ts +22 -4
- package/src/codex/model-entitlements.ts +9 -2
- package/src/codex/native-profile-startup.ts +157 -27
- package/src/codex/native-residue.ts +26 -33
- package/src/codex/prompt-layers.ts +371 -25
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota.ts +91 -10
- package/src/codex/routing.ts +9 -0
- package/src/codex/shim.ts +56 -3
- package/src/codex/subagent-model-fallback.ts +104 -25
- package/src/codex/transition-state.ts +116 -8
- package/src/combos/failover.ts +27 -0
- package/src/combos/types.ts +10 -0
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +206 -824
- package/src/generated/compatibility-version.json +373 -213
- package/src/images/loop.ts +5 -4
- package/src/images/plan.ts +5 -4
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +46 -2
- package/src/integrations/registry.ts +7 -0
- package/src/integrations/state.ts +79 -7
- package/src/integrations/writer.ts +26 -2
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
- package/src/lab/fabric/producer-child.ts +1 -1
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/bounded-body.ts +14 -2
- package/src/lib/config-ownership.ts +20 -0
- package/src/lib/errors.ts +36 -3
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/process-control.ts +2 -1
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/tool-argument-integers.ts +56 -5
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/health.ts +1 -1
- package/src/oauth/index.ts +65 -6
- package/src/oauth/key-providers.ts +10 -1
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/kiro.ts +45 -0
- package/src/oauth/local-token-detect.ts +2 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +14 -5
- package/src/oauth/types.ts +15 -0
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/command-code-efforts.ts +38 -6
- package/src/providers/context-cap.ts +4 -3
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +28 -0
- package/src/providers/fastwire.ts +7 -1
- package/src/providers/label.ts +34 -1
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/provider-id-rewrite.ts +2 -1
- package/src/providers/quota.ts +65 -7
- package/src/providers/registry.ts +184 -69
- package/src/providers/slug-codec.ts +52 -0
- package/src/providers/xai-transport.ts +21 -0
- package/src/reasoning-effort.ts +19 -2
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/custom-tool-compat.ts +157 -39
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +7 -2
- package/src/responses/reasoning-replay-cache.ts +81 -3
- package/src/responses/schema.ts +5 -1
- package/src/responses/state.ts +94 -4
- package/src/responses/thought-signature-replay.ts +17 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +40 -2
- package/src/routing/analytics.ts +1 -0
- package/src/routing/quota.ts +10 -0
- package/src/server/auth-cors.ts +27 -7
- package/src/server/chat-completions.ts +24 -16
- package/src/server/chat-native-sse.ts +3 -3
- package/src/server/chat-native.ts +30 -11
- package/src/server/claude-messages.ts +1 -1
- package/src/server/index.ts +179 -32
- package/src/server/lifecycle.ts +52 -1
- package/src/server/local-management-read-client.ts +1 -1
- package/src/server/local-provider-reload-client.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +45 -23
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +2 -1
- package/src/server/management/config-routes.ts +59 -12
- package/src/server/management/context.ts +10 -1
- package/src/server/management/logs-usage-routes.ts +30 -24
- package/src/server/management/model-routes.ts +274 -4
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/oauth-account-routes.ts +10 -4
- package/src/server/management/provider-capability-config.ts +1 -1
- package/src/server/management/provider-routes.ts +81 -11
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-profile-routes.ts +2 -2
- package/src/server/management/system-restart.ts +1 -1
- package/src/server/management-api.ts +18 -16
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/proxy-liveness.ts +29 -5
- package/src/server/relay-eager.ts +147 -20
- package/src/server/relay.ts +251 -19
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +70 -16
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +37 -3
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +6 -21
- package/src/server/responses/core.ts +810 -208
- package/src/server/responses/empty-completion-guard.ts +63 -6
- package/src/server/responses/fetch-helpers.ts +17 -102
- package/src/server/responses/input-admission.ts +3 -1
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/responses-field-backfill.ts +105 -13
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-custom-tool-repair.ts +87 -6
- package/src/server/responses-terminal-repair.ts +25 -4
- package/src/server/responses-undeclared-tool-guard.ts +241 -18
- package/src/server/sse-frame-buffer.ts +31 -4
- package/src/server/ws-bridge.ts +14 -2
- package/src/service.ts +42 -4
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/types/config.ts +66 -0
- package/src/types/provider.ts +69 -0
- package/src/types/request.ts +17 -4
- package/src/types/tools.ts +116 -11
- package/src/types.ts +3 -1
- package/src/update/index.ts +5 -4
- package/src/update/job.ts +3 -1
- package/src/update/transactional-install.mjs +8 -1
- package/src/usage/expected-prices.ts +43 -24
- package/src/usage/log.ts +63 -12
- package/src/usage/summary.ts +211 -8
- package/src/vision/describe.ts +18 -13
- package/src/web-search/exa-executor.ts +40 -9
- package/src/web-search/executor.ts +10 -3
- package/src/web-search/loop.ts +5 -4
- package/gui/dist/assets/index-BF38heuV.js +0 -104
- package/gui/dist/assets/index-DMiI18Kv.css +0 -1
package/src/types/tools.ts
CHANGED
|
@@ -31,6 +31,35 @@ export function namespacedToolName(namespace: string | undefined, name: string):
|
|
|
31
31
|
return namespace ? `${namespace}__${name}` : name;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Codex unified-exec name normalization.
|
|
36
|
+
*
|
|
37
|
+
* Codex's code-mode shell tool is declared as `exec` (a freeform custom tool whose own
|
|
38
|
+
* description mentions the nested `await tools.exec_command(...)` helper). Routed models —
|
|
39
|
+
* DeepSeek in particular — sometimes echo that helper name as the tool-call name, emitting
|
|
40
|
+
* `exec_command` or `apply_patch` instead of the declared `exec`. Accept these nested helper
|
|
41
|
+
* names only when the request catalog actually declares `exec` and does not itself declare the
|
|
42
|
+
* emitted name (an MCP server may legitimately advertise one under its own namespace).
|
|
43
|
+
*/
|
|
44
|
+
const LEGACY_SHELL_BRIDGE_TOOL_NAMES = ["exec_command", "shell_command"] as const;
|
|
45
|
+
const CODE_MODE_HELPER_TOOL_NAMES = [...LEGACY_SHELL_BRIDGE_TOOL_NAMES, "apply_patch"] as const;
|
|
46
|
+
|
|
47
|
+
export function normalizeDeclaredToolName(
|
|
48
|
+
name: string,
|
|
49
|
+
declared: ReadonlySet<string> | undefined,
|
|
50
|
+
): string {
|
|
51
|
+
if (!declared || !declared.has("exec")) return name;
|
|
52
|
+
if (declared.has(name)) return name;
|
|
53
|
+
if (name === "apply_patch") return "exec";
|
|
54
|
+
// When the catalog explicitly declares any legacy shell bridge name, the environment
|
|
55
|
+
// genuinely exposes that tool — turn normalization off so a call is never mis-routed
|
|
56
|
+
// to `exec`.
|
|
57
|
+
if ((LEGACY_SHELL_BRIDGE_TOOL_NAMES as readonly string[]).some(legacy => declared.has(legacy))) {
|
|
58
|
+
return name;
|
|
59
|
+
}
|
|
60
|
+
return (CODE_MODE_HELPER_TOOL_NAMES as readonly string[]).includes(name) ? "exec" : name;
|
|
61
|
+
}
|
|
62
|
+
|
|
34
63
|
export function toolChoiceAliases(tool: Pick<OcxTool, "namespace" | "name">): string[] {
|
|
35
64
|
const wireName = namespacedToolName(tool.namespace, tool.name);
|
|
36
65
|
return tool.namespace ? [wireName, `${tool.namespace}.${tool.name}`] : [wireName];
|
|
@@ -43,6 +72,74 @@ function sameToolIdentity(
|
|
|
43
72
|
return left.namespace === right.namespace && left.name === right.name;
|
|
44
73
|
}
|
|
45
74
|
|
|
75
|
+
type ToolIdentity = Readonly<Pick<OcxTool, "namespace" | "name">>;
|
|
76
|
+
|
|
77
|
+
function snapshotToolIdentity(tool: Pick<OcxTool, "namespace" | "name">): ToolIdentity {
|
|
78
|
+
return Object.freeze({
|
|
79
|
+
name: tool.name,
|
|
80
|
+
...(tool.namespace === undefined ? {} : { namespace: tool.namespace }),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildToolChoiceCatalog(
|
|
85
|
+
tools: readonly ToolIdentity[],
|
|
86
|
+
): {
|
|
87
|
+
candidatesByName: ReadonlyMap<string, readonly ToolIdentity[]>;
|
|
88
|
+
sourceCandidatesByName: ReadonlyMap<string, readonly ToolIdentity[]>;
|
|
89
|
+
identitiesByTool: WeakMap<object, ToolIdentity>;
|
|
90
|
+
} {
|
|
91
|
+
const index = new Map<string, ToolIdentity[]>();
|
|
92
|
+
const sourceIndex = new Map<string, ToolIdentity[]>();
|
|
93
|
+
const identities = new Map<string, Set<string>>();
|
|
94
|
+
const identitiesByTool = new WeakMap<object, ToolIdentity>();
|
|
95
|
+
for (const tool of tools) {
|
|
96
|
+
const snapshot = snapshotToolIdentity(tool);
|
|
97
|
+
identitiesByTool.set(tool, snapshot);
|
|
98
|
+
const identity = JSON.stringify([snapshot.namespace ?? null, snapshot.name]);
|
|
99
|
+
for (const selector of [...toolChoiceAliases(snapshot), snapshot.name]) {
|
|
100
|
+
const candidates = index.get(selector);
|
|
101
|
+
if (!candidates) {
|
|
102
|
+
index.set(selector, [snapshot]);
|
|
103
|
+
sourceIndex.set(selector, [tool]);
|
|
104
|
+
identities.set(selector, new Set([identity]));
|
|
105
|
+
} else if (!identities.get(selector)!.has(identity)) {
|
|
106
|
+
candidates.push(snapshot);
|
|
107
|
+
sourceIndex.get(selector)!.push(tool);
|
|
108
|
+
identities.get(selector)!.add(identity);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return { candidatesByName: index, sourceCandidatesByName: sourceIndex, identitiesByTool };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Compile one immutable view of a request's tool catalog for repeated policy checks. */
|
|
116
|
+
export function createToolChoiceResolver(tools: readonly ToolIdentity[] | undefined) {
|
|
117
|
+
const compiled = tools ? buildToolChoiceCatalog(tools) : undefined;
|
|
118
|
+
const candidatesByName = compiled?.candidatesByName;
|
|
119
|
+
const snapshotFor = (tool: ToolIdentity): ToolIdentity | undefined => {
|
|
120
|
+
const snapshot = compiled?.identitiesByTool.get(tool);
|
|
121
|
+
return snapshot && sameToolIdentity(snapshot, tool) ? snapshot : undefined;
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
candidates(name: string): ToolIdentity[] {
|
|
125
|
+
return (candidatesByName?.get(name) ?? []).map(candidate => ({ ...candidate }));
|
|
126
|
+
},
|
|
127
|
+
candidateCount(name: string): number {
|
|
128
|
+
return candidatesByName?.get(name)?.length ?? 0;
|
|
129
|
+
},
|
|
130
|
+
allows(tool: ToolIdentity, allowedTools: ReadonlySet<string>): boolean {
|
|
131
|
+
if (!candidatesByName) return toolChoiceAliases(tool).some(name => allowedTools.has(name));
|
|
132
|
+
const snapshot = snapshotFor(tool);
|
|
133
|
+
return snapshot ? toolAllowedByChoiceFromIndex(snapshot, allowedTools, candidatesByName) : false;
|
|
134
|
+
},
|
|
135
|
+
selects(tool: ToolIdentity, name: string): boolean {
|
|
136
|
+
const snapshot = snapshotFor(tool);
|
|
137
|
+
const candidates = candidatesByName?.get(name);
|
|
138
|
+
return !!snapshot && candidates?.length === 1 && sameToolIdentity(candidates[0], snapshot);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
46
143
|
/**
|
|
47
144
|
* All tools that could be selected by one client-facing name. Bare logical names are included
|
|
48
145
|
* here because they are a compatibility selector for namespaced tools, while wire and dotted
|
|
@@ -53,12 +150,7 @@ export function toolChoiceCandidates(
|
|
|
53
150
|
name: string,
|
|
54
151
|
): Pick<OcxTool, "namespace" | "name">[] {
|
|
55
152
|
if (!tools) return [];
|
|
56
|
-
|
|
57
|
-
for (const tool of tools) {
|
|
58
|
-
if (tool.name !== name && !toolChoiceAliases(tool).includes(name)) continue;
|
|
59
|
-
if (!candidates.some(candidate => sameToolIdentity(candidate, tool))) candidates.push(tool);
|
|
60
|
-
}
|
|
61
|
-
return candidates;
|
|
153
|
+
return [...(buildToolChoiceCatalog(tools).sourceCandidatesByName.get(name) ?? [])];
|
|
62
154
|
}
|
|
63
155
|
|
|
64
156
|
/**
|
|
@@ -72,10 +164,22 @@ export function toolAllowedByChoice(
|
|
|
72
164
|
tools?: readonly Pick<OcxTool, "namespace" | "name">[],
|
|
73
165
|
): boolean {
|
|
74
166
|
if (!tools) return toolChoiceAliases(tool).some(name => allowedTools.has(name));
|
|
167
|
+
return toolAllowedByChoiceFromIndex(
|
|
168
|
+
snapshotToolIdentity(tool),
|
|
169
|
+
allowedTools,
|
|
170
|
+
buildToolChoiceCatalog(tools).candidatesByName,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function toolAllowedByChoiceFromIndex(
|
|
175
|
+
tool: ToolIdentity,
|
|
176
|
+
allowedTools: ReadonlySet<string>,
|
|
177
|
+
candidatesByName: ReadonlyMap<string, readonly ToolIdentity[]>,
|
|
178
|
+
): boolean {
|
|
75
179
|
for (const name of [...toolChoiceAliases(tool), tool.name]) {
|
|
76
180
|
if (!allowedTools.has(name)) continue;
|
|
77
|
-
const candidates =
|
|
78
|
-
if (candidates
|
|
181
|
+
const candidates = candidatesByName.get(name);
|
|
182
|
+
if (candidates?.length === 1 && sameToolIdentity(candidates[0], tool)) return true;
|
|
79
183
|
}
|
|
80
184
|
return false;
|
|
81
185
|
}
|
|
@@ -123,9 +227,10 @@ export function toolChoiceToolPredicate(
|
|
|
123
227
|
if (choice === "none") return () => false;
|
|
124
228
|
if (isAllowedToolChoice(choice)) {
|
|
125
229
|
const allowed = new Set(choice.allowedTools);
|
|
126
|
-
|
|
230
|
+
const resolver = createToolChoiceResolver(tools);
|
|
231
|
+
return tool => resolver.allows(tool, allowed);
|
|
127
232
|
}
|
|
128
233
|
if (!tools) return tool => toolChoiceAliases(tool).includes(choice.name);
|
|
129
|
-
const
|
|
130
|
-
return tool =>
|
|
234
|
+
const resolver = createToolChoiceResolver(tools);
|
|
235
|
+
return tool => resolver.selects(tool, choice.name);
|
|
131
236
|
}
|
package/src/types.ts
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
export type { OcxTool, OcxToolChoice } from "./types/tools";
|
|
5
5
|
export {
|
|
6
6
|
namespacedToolName,
|
|
7
|
+
normalizeDeclaredToolName,
|
|
7
8
|
toolChoiceAliases,
|
|
9
|
+
createToolChoiceResolver,
|
|
8
10
|
toolChoiceCandidates,
|
|
9
11
|
toolAllowedByChoice,
|
|
10
12
|
resolveToolChoiceWireName,
|
|
@@ -59,6 +61,7 @@ export type {
|
|
|
59
61
|
OcxCustomModel,
|
|
60
62
|
OcxApiKeyEntry,
|
|
61
63
|
OcxClientIntegrationsConfig,
|
|
64
|
+
OcxConfigRebaseProvenance,
|
|
62
65
|
OcxConfig,
|
|
63
66
|
OcxAccountPoolRotationStrategy,
|
|
64
67
|
OcxComboStrategy,
|
|
@@ -102,4 +105,3 @@ export type {
|
|
|
102
105
|
CodexAccountCredentials,
|
|
103
106
|
CodexAccountCredentialRecord,
|
|
104
107
|
} from "./types/accounts";
|
|
105
|
-
|
package/src/update/index.ts
CHANGED
|
@@ -2,7 +2,8 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
2
2
|
import { readFileSync, readdirSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
|
-
import { getConfigDir, loadConfig
|
|
5
|
+
import { getConfigDir, loadConfig } from "../config";
|
|
6
|
+
import { readPid, readRuntimePort } from "../config/process-state";
|
|
6
7
|
import { npmInvocation } from "./npm-invocation.mjs";
|
|
7
8
|
import {
|
|
8
9
|
npmCachePreflightFailureMessage,
|
|
@@ -284,9 +285,9 @@ export async function runUpdate(): Promise<void> {
|
|
|
284
285
|
}
|
|
285
286
|
if (historyRestoreIncomplete()) {
|
|
286
287
|
console.warn(
|
|
287
|
-
"⚠️ Codex resume
|
|
288
|
-
"
|
|
289
|
-
" After the update: close the Codex app, then run 'ocx stop' once to
|
|
288
|
+
"⚠️ Codex resume-history metadata restore is incomplete (a backup manifest remains).\n" +
|
|
289
|
+
" The DB may be busy or the manifest/target may need review; untracked routed history is intentionally unchanged.\n" +
|
|
290
|
+
" After the update: close the Codex app, run 'ocx doctor', then run 'ocx stop' once to retry.",
|
|
290
291
|
);
|
|
291
292
|
}
|
|
292
293
|
}
|
package/src/update/job.ts
CHANGED
|
@@ -6,12 +6,14 @@ import {
|
|
|
6
6
|
atomicWriteFile,
|
|
7
7
|
getConfigDir,
|
|
8
8
|
loadConfig,
|
|
9
|
+
} from "../config";
|
|
10
|
+
import {
|
|
9
11
|
readPid,
|
|
10
12
|
readRuntimePort,
|
|
11
13
|
removePid,
|
|
12
14
|
removeRuntimePort,
|
|
13
15
|
verifyPidIdentity,
|
|
14
|
-
} from "../config";
|
|
16
|
+
} from "../config/process-state";
|
|
15
17
|
import { isProcessAlive, killProxy } from "../lib/process-control";
|
|
16
18
|
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
17
19
|
import { killWindowsSchedulerWrappers } from "../lib/windows-service-wrappers";
|
|
@@ -176,7 +176,14 @@ export function transactionalNpmUpdate({
|
|
|
176
176
|
}
|
|
177
177
|
const spec = pkgName + "@" + (targetVersion || tag);
|
|
178
178
|
log("Staging " + spec + " into " + stageRoot);
|
|
179
|
-
|
|
179
|
+
// npm 12 blocks lifecycle scripts by default. Bun's postinstall copies the selected
|
|
180
|
+
// @oven/bun-* executable into bun/bin, so a successful npm exit without this narrow
|
|
181
|
+
// approval leaves the staged tree intentionally incomplete. Allow only the package
|
|
182
|
+
// whose executable the manifest verifies below; never broaden this to all scripts.
|
|
183
|
+
const install = runNpm([
|
|
184
|
+
"install", "-g", "--prefix", stageRoot,
|
|
185
|
+
"--allow-scripts=bun", "--no-audit", "--no-fund", spec,
|
|
186
|
+
]);
|
|
180
187
|
if (install.status !== 0) {
|
|
181
188
|
try { rmSync(stageRoot, { recursive: true, force: true }); } catch { /* best effort */ }
|
|
182
189
|
return { ok: false, phase: "stage", error: "npm staging install failed (" + (install.status ?? "?") + ")" };
|
|
@@ -39,7 +39,7 @@ export interface ExpectedPriceOverlay {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const GEMINI_31_PRO: Cost4 = { input: 2, output: 12, cacheRead: 0.2, cacheWrite: 0 };
|
|
42
|
-
const GPT56_SOL: Cost4 = { input:
|
|
42
|
+
const GPT56_SOL: Cost4 = { input: 4, output: 20, cacheRead: 0.4, cacheWrite: 5 };
|
|
43
43
|
/**
|
|
44
44
|
* Daybreak aliases. `daybreak-*-latest` never appears in the pricing table itself — only its
|
|
45
45
|
* current snapshot does — so these tuples are the snapshot's published rates and carry
|
|
@@ -126,6 +126,10 @@ export const EXPECTED_PRICE_OVERLAYS: readonly ExpectedPriceOverlay[] = [
|
|
|
126
126
|
// Daybreak aliases: priced as their current snapshots (red -> gpt-5.6-cyber,
|
|
127
127
|
// blue -> gpt-5.6-sol). The alias ids carry no rows of their own upstream, hence
|
|
128
128
|
// verified-derived. Blue deliberately reuses GPT56_SOL rather than duplicating the tuple.
|
|
129
|
+
// The `gpt-` selector is native to ChatGPT/Codex accounts. The bare aliases below belong to
|
|
130
|
+
// the separately billed API-key catalog; keep those namespaces disjoint so pricing metadata
|
|
131
|
+
// cannot make an unroutable provider/model pair look supported.
|
|
132
|
+
{ provider: "openai", modelId: "gpt-daybreak-blue-latest", cost4: GPT56_SOL, source: `alias of gpt-5.6-sol ${OPENAI_GPT56_PRICING}`, verifiedAt: "2026-08-11", status: "verified-derived" },
|
|
129
133
|
{ provider: "openai-apikey", modelId: "daybreak-red-latest", cost4: DAYBREAK_RED, source: `alias of gpt-5.6-cyber ${OPENAI_GPT56_PRICING}`, verifiedAt: "2026-08-11", status: "verified-derived" },
|
|
130
134
|
{ provider: "openai-apikey", modelId: "daybreak-blue-latest", cost4: GPT56_SOL, source: `alias of gpt-5.6-sol ${OPENAI_GPT56_PRICING}`, verifiedAt: "2026-08-11", status: "verified-derived" },
|
|
131
135
|
{ provider: "google-antigravity", modelId: "gemini-3.1-pro-low", cost4: GEMINI_31_PRO, source: `derived: gemini-3.1-pro (<=200k tier) ${GEMINI_PRICING}`, verifiedAt: "2026-07-20", status: "verified-derived" },
|
|
@@ -223,6 +227,8 @@ export function findExpectedPriceOverlay(
|
|
|
223
227
|
/** OpenAI Fast price multipliers retained as a compatibility export. */
|
|
224
228
|
export const PRIORITY_MULTIPLIERS: Readonly<Record<string, number>> = {
|
|
225
229
|
"gpt-5.6-sol": 2,
|
|
230
|
+
"gpt-daybreak-blue-latest": 2,
|
|
231
|
+
"daybreak-blue-latest": 2,
|
|
226
232
|
// Post-price-cut Fast tables (https://openai.com/api-fast-mode/, 2026-08-05):
|
|
227
233
|
// Terra 4/24/0.40/5 and Luna 0.40/2.40/0.04/0.50 are both 2× the corrected
|
|
228
234
|
// standard tuples; the stale bases made these look like 1.6/0.4 (#907).
|
|
@@ -257,13 +263,19 @@ const XAI_PRIORITY_PRICING = "https://docs.x.ai/developers/advanced-api-usage/pr
|
|
|
257
263
|
*/
|
|
258
264
|
export const PRIORITY_PRICING_RULES: readonly PriorityPricingRule[] = [
|
|
259
265
|
...["openai", "openai-apikey"].flatMap(provider =>
|
|
260
|
-
Object.entries(PRIORITY_MULTIPLIERS)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
266
|
+
Object.entries(PRIORITY_MULTIPLIERS)
|
|
267
|
+
// Daybreak's `gpt-` selector belongs to ChatGPT/Codex accounts; its bare selector belongs
|
|
268
|
+
// to the separately billed API-key catalog. All ordinary GPT ids remain valid on both.
|
|
269
|
+
.filter(([modelId]) => provider === "openai"
|
|
270
|
+
? modelId !== "daybreak-blue-latest"
|
|
271
|
+
: modelId !== "gpt-daybreak-blue-latest")
|
|
272
|
+
.map(([modelId, multiplier]): PriorityPricingRule => ({
|
|
273
|
+
provider,
|
|
274
|
+
modelId,
|
|
275
|
+
multiplier,
|
|
276
|
+
source: OPENAI_FAST_PRICING,
|
|
277
|
+
verifiedAt: "2026-08-05",
|
|
278
|
+
})),
|
|
267
279
|
),
|
|
268
280
|
...["grok-4.5", "grok-4.6"].map((modelId): PriorityPricingRule => ({
|
|
269
281
|
provider: "xai",
|
|
@@ -349,6 +361,29 @@ export const CONTEXT_TIERS: readonly ContextTier[] = [
|
|
|
349
361
|
verifiedAt: "2026-08-03",
|
|
350
362
|
})),
|
|
351
363
|
),
|
|
364
|
+
{
|
|
365
|
+
// The native Codex selector aliases gpt-5.6-sol and shares its 272k long-context tier.
|
|
366
|
+
provider: "openai",
|
|
367
|
+
modelId: "gpt-daybreak-blue-latest",
|
|
368
|
+
thresholdInputTokens: 272_000,
|
|
369
|
+
inclusive: false,
|
|
370
|
+
multiplier: OPENAI_LONG_CONTEXT,
|
|
371
|
+
confirmedPriorityRelation: "exclusive",
|
|
372
|
+
source: OPENAI_PRICING_DOC,
|
|
373
|
+
verifiedAt: "2026-08-11",
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
// The bare selector is the separately billed API-key alias. Daybreak Red has no tier row:
|
|
377
|
+
// the cyber snapshot's four long-context cells are all "-" on the pricing page.
|
|
378
|
+
provider: "openai-apikey",
|
|
379
|
+
modelId: "daybreak-blue-latest",
|
|
380
|
+
thresholdInputTokens: 272_000,
|
|
381
|
+
inclusive: false,
|
|
382
|
+
multiplier: OPENAI_LONG_CONTEXT,
|
|
383
|
+
confirmedPriorityRelation: "exclusive",
|
|
384
|
+
source: OPENAI_PRICING_DOC,
|
|
385
|
+
verifiedAt: "2026-08-11",
|
|
386
|
+
},
|
|
352
387
|
{
|
|
353
388
|
provider: "xai",
|
|
354
389
|
modelId: "grok-4.5",
|
|
@@ -371,22 +406,6 @@ export const CONTEXT_TIERS: readonly ContextTier[] = [
|
|
|
371
406
|
source: "https://docs.x.ai/developers/pricing",
|
|
372
407
|
verifiedAt: "2026-08-18",
|
|
373
408
|
},
|
|
374
|
-
{
|
|
375
|
-
// daybreak-blue-latest aliases gpt-5.6-sol, which publishes the full long-context row
|
|
376
|
-
// ($10 / $1 / $12.50 / $45). Scoped to openai-apikey ON PURPOSE: Daybreak is not
|
|
377
|
-
// routable on the Codex-login `openai` provider, so adding the alias to the shared
|
|
378
|
-
// OPENAI_GPT56_CONTEXT_MODELS list (expanded across both providers above) would mint a
|
|
379
|
-
// tier for a provider/model pair that cannot exist.
|
|
380
|
-
// daybreak-red-latest has NO tier row: the cyber snapshot's four long-context cells are
|
|
381
|
-
// all "-" on the pricing page (verified 2026-08-11).
|
|
382
|
-
provider: "openai-apikey",
|
|
383
|
-
modelId: "daybreak-blue-latest",
|
|
384
|
-
thresholdInputTokens: 272_000,
|
|
385
|
-
inclusive: false,
|
|
386
|
-
multiplier: OPENAI_LONG_CONTEXT,
|
|
387
|
-
source: OPENAI_PRICING_DOC,
|
|
388
|
-
verifiedAt: "2026-08-11",
|
|
389
|
-
},
|
|
390
409
|
...["minimax", "minimax-cn"].map((provider): ContextTier => ({
|
|
391
410
|
provider,
|
|
392
411
|
modelId: "MiniMax-M3",
|
package/src/usage/log.ts
CHANGED
|
@@ -8,12 +8,33 @@ import { sanitizeLogMetadataString } from "../lib/redact";
|
|
|
8
8
|
import { usageDisplayTotalTokens } from "./totals";
|
|
9
9
|
import type { AttemptTierOutcome, OcxUsage } from "../types";
|
|
10
10
|
import { normalizeRouteDecisionTrace, type RouteDecisionTraceV1 } from "../routing/trace";
|
|
11
|
-
import { CODEX_ACCOUNT_LOG_LABEL_RE } from "../codex/account-label";
|
|
11
|
+
import { ACCOUNT_LOG_LABEL_RE, CODEX_ACCOUNT_LOG_LABEL_RE } from "../codex/account-label";
|
|
12
12
|
|
|
13
13
|
export type UsageStatus = "reported" | "unreported" | "unsupported" | "estimated";
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* A persisted account label: a Codex pool account (`main`/`p<hex6>`) or a non-Codex OAuth
|
|
16
|
+
* provider account (`o<hex6>`, #2699).
|
|
17
|
+
*
|
|
18
|
+
* The old name `CodexUsageAccountLogLabel` is kept as an alias because it is exported and used
|
|
19
|
+
* across modules; the two predicates below are what callers should choose between.
|
|
20
|
+
*/
|
|
21
|
+
export type UsageAccountLogLabel = "main" | `p${string}` | `o${string}`;
|
|
22
|
+
export type CodexUsageAccountLogLabel = UsageAccountLogLabel;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Accepts EITHER label family. This is the predicate the persistence writers use, so widening
|
|
26
|
+
* it here is what stops six separate call sites from silently dropping an `o`-label -- including
|
|
27
|
+
* two in the live request path (`request-log.ts:972` and `:1187`).
|
|
28
|
+
*
|
|
29
|
+
* The name is unchanged deliberately: renaming it would touch every call site for no behavior,
|
|
30
|
+
* and the widened contract is what every one of those sites wanted.
|
|
31
|
+
*/
|
|
32
|
+
export function isCodexUsageAccountLogLabel(value: unknown): value is UsageAccountLogLabel {
|
|
33
|
+
return value === "main" || (typeof value === "string" && ACCOUNT_LOG_LABEL_RE.test(value));
|
|
34
|
+
}
|
|
15
35
|
|
|
16
|
-
|
|
36
|
+
/** Strictly a Codex pool label. Use when the Codex-only distinction actually matters. */
|
|
37
|
+
export function isCodexPoolAccountLogLabel(value: unknown): value is "main" | `p${string}` {
|
|
17
38
|
return value === "main" || (typeof value === "string" && CODEX_ACCOUNT_LOG_LABEL_RE.test(value));
|
|
18
39
|
}
|
|
19
40
|
|
|
@@ -28,6 +49,7 @@ export type AttemptRecoveryKind =
|
|
|
28
49
|
| "key-429"
|
|
29
50
|
| "rate-limit-429"
|
|
30
51
|
| "anthropic-oauth-429"
|
|
52
|
+
| "oauth-account-429"
|
|
31
53
|
| "image-413"
|
|
32
54
|
| "opaque-blob-rejection"
|
|
33
55
|
| "empty-completion";
|
|
@@ -50,6 +72,13 @@ export interface PersistedUsageAttempt {
|
|
|
50
72
|
sendCount: number;
|
|
51
73
|
recoveryKinds: AttemptRecoveryKind[];
|
|
52
74
|
usageStatus: UsageStatus;
|
|
75
|
+
/**
|
|
76
|
+
* True when the proxy answered this turn locally and issued no upstream request. It travels on
|
|
77
|
+
* the attempt itself rather than as a `finishRequestAttempt` argument because that function is
|
|
78
|
+
* called from six places, and a new parameter would silently default to the wrong answer at any
|
|
79
|
+
* one of them that was missed. Absent on ordinary attempts so old rows keep their exact shape.
|
|
80
|
+
*/
|
|
81
|
+
locallyAnswered?: boolean;
|
|
53
82
|
/** Stable non-PII identity for the Codex pool account that served this attempt. */
|
|
54
83
|
accountLogLabel?: CodexUsageAccountLogLabel;
|
|
55
84
|
inputTokenEstimate?: number;
|
|
@@ -68,6 +97,7 @@ export interface PersistedUsageAttempt {
|
|
|
68
97
|
}
|
|
69
98
|
|
|
70
99
|
export interface PersistedUsageEntry {
|
|
100
|
+
requestedAlias?: string;
|
|
71
101
|
requestId: string;
|
|
72
102
|
timestamp: number;
|
|
73
103
|
provider: string;
|
|
@@ -179,8 +209,20 @@ function isEstimatedUsageProvider(providerOrAdapter: string): boolean {
|
|
|
179
209
|
|| providerOrAdapter === "cursor" || providerOrAdapter.startsWith("cursor-");
|
|
180
210
|
}
|
|
181
211
|
|
|
182
|
-
export function usageForFinalLog(
|
|
212
|
+
export function usageForFinalLog(
|
|
213
|
+
provider: string,
|
|
214
|
+
usage: OcxUsage | undefined,
|
|
215
|
+
/**
|
|
216
|
+
* True when the proxy answered this turn locally and issued no upstream request. Such a turn's
|
|
217
|
+
* zero counts are EXACT, so the provider-wide estimated marking must not apply: Kiro and Cursor
|
|
218
|
+
* are marked estimated because their adapters can only guess a real inference's usage, and a
|
|
219
|
+
* turn with no inference has nothing to guess. Without this, a no-send turn is indistinguishable
|
|
220
|
+
* from a real one whose usage frame never arrived.
|
|
221
|
+
*/
|
|
222
|
+
locallyAnswered = false,
|
|
223
|
+
): OcxUsage | undefined {
|
|
183
224
|
if (!usage) return undefined;
|
|
225
|
+
if (locallyAnswered) return usage;
|
|
184
226
|
if (usage.estimated || isEstimatedUsageProvider(provider)) return { ...usage, estimated: true };
|
|
185
227
|
return usage;
|
|
186
228
|
}
|
|
@@ -218,6 +260,7 @@ const ATTEMPT_RECOVERY_KINDS = new Set<AttemptRecoveryKind>([
|
|
|
218
260
|
"key-429",
|
|
219
261
|
"rate-limit-429",
|
|
220
262
|
"anthropic-oauth-429",
|
|
263
|
+
"oauth-account-429",
|
|
221
264
|
"image-413",
|
|
222
265
|
"opaque-blob-rejection",
|
|
223
266
|
"empty-completion",
|
|
@@ -927,6 +970,10 @@ async function readUsageEntriesIncrementally(
|
|
|
927
970
|
// A shrink means truncation or replacement-in-place; the retained rows may no
|
|
928
971
|
// longer correspond to file contents, so refuse to extend them.
|
|
929
972
|
if (size < retained.coveredThroughBytes) return null;
|
|
973
|
+
// Retained-state reuse is only an optimization. A burst larger than the configured
|
|
974
|
+
// window must re-anchor through the bounded full-tail reader instead of reading and
|
|
975
|
+
// parsing every byte appended since the previous poll.
|
|
976
|
+
if (size - retained.coveredThroughBytes > maxReadBytes) return null;
|
|
930
977
|
// Verify the retained REGION is unchanged before anything is reused. Identity keeps
|
|
931
978
|
// dev/ino/birthtime, and an append and an in-place rewrite both move mtime/ctime
|
|
932
979
|
// forward, so only the bytes themselves settle it.
|
|
@@ -1138,6 +1185,14 @@ export async function readUsageEntriesForManagement(): Promise<PersistedUsageEnt
|
|
|
1138
1185
|
return (await readUsageSnapshotForManagement()).entries;
|
|
1139
1186
|
}
|
|
1140
1187
|
|
|
1188
|
+
/** Keep legacy optional fields permissive, but reject rows that cannot be safely attributed. */
|
|
1189
|
+
function normalizePersistedUsageRow(value: unknown): PersistedUsageEntry | undefined {
|
|
1190
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
1191
|
+
const row = value as Record<string, unknown>;
|
|
1192
|
+
if (typeof row.requestId !== "string" || typeof row.provider !== "string") return undefined;
|
|
1193
|
+
return normalizeUsageEntry(row as unknown as PersistedUsageEntry);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1141
1196
|
export function readUsageEntries(): PersistedUsageEntry[] {
|
|
1142
1197
|
const path = usageLogPath();
|
|
1143
1198
|
if (!existsSync(path)) return [];
|
|
@@ -1146,10 +1201,8 @@ export function readUsageEntries(): PersistedUsageEntry[] {
|
|
|
1146
1201
|
for (const line of lines) {
|
|
1147
1202
|
if (!line.trim()) continue;
|
|
1148
1203
|
try {
|
|
1149
|
-
const parsed = JSON.parse(line)
|
|
1150
|
-
if (parsed
|
|
1151
|
-
entries.push(normalizeUsageEntry(parsed));
|
|
1152
|
-
}
|
|
1204
|
+
const parsed = normalizePersistedUsageRow(JSON.parse(line));
|
|
1205
|
+
if (parsed) entries.push(parsed);
|
|
1153
1206
|
} catch {
|
|
1154
1207
|
/* keep reading after a partially written or hand-edited line */
|
|
1155
1208
|
}
|
|
@@ -1162,10 +1215,8 @@ function parseUsageLines(lines: string[]): PersistedUsageEntry[] {
|
|
|
1162
1215
|
for (const line of lines) {
|
|
1163
1216
|
if (!line.trim()) continue;
|
|
1164
1217
|
try {
|
|
1165
|
-
const parsed = JSON.parse(line)
|
|
1166
|
-
if (parsed
|
|
1167
|
-
entries.push(normalizeUsageEntry(parsed));
|
|
1168
|
-
}
|
|
1218
|
+
const parsed = normalizePersistedUsageRow(JSON.parse(line));
|
|
1219
|
+
if (parsed) entries.push(parsed);
|
|
1169
1220
|
} catch {
|
|
1170
1221
|
/* skip partial / hand-edited lines */
|
|
1171
1222
|
}
|