@bitkyc08/opencodex 2.55.0 → 2.57.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/bin/ocx.mjs +10 -0
- package/gui/dist/assets/{index-BBOZWGB6.css → index-C5-RdDmD.css} +1 -1
- package/gui/dist/assets/{index-VuoiWj9J.js → index-Cz7CLdif.js} +21 -21
- package/gui/dist/index.html +2 -2
- package/package.json +4 -3
- package/src/adapters/base.ts +21 -0
- package/src/adapters/codebuddy/adapter.ts +2 -1
- package/src/adapters/codebuddy/scaffold-guard.ts +248 -0
- package/src/adapters/command-code.ts +1 -1
- package/src/adapters/cursor/envelope-echo.ts +8 -2
- package/src/adapters/cursor/transport-retry.ts +46 -1
- package/src/adapters/cursor.ts +4 -0
- package/src/adapters/google.ts +7 -7
- package/src/adapters/kiro/adapter.ts +42 -1
- package/src/adapters/kiro/payload.ts +17 -3
- package/src/adapters/kiro/reasoning.ts +70 -7
- package/src/adapters/kiro/stream.ts +8 -2
- package/src/adapters/kiro/wire.ts +2 -1
- package/src/adapters/kiro-events.ts +21 -13
- package/src/adapters/kiro-retry.ts +23 -4
- package/src/adapters/openai-chat/errors.ts +116 -0
- package/src/adapters/openai-chat/messages.ts +346 -0
- package/src/adapters/openai-chat/passthrough.ts +146 -0
- package/src/adapters/openai-chat/response-events.ts +117 -0
- package/src/adapters/openai-chat/tool-call-validation.ts +200 -0
- package/src/adapters/openai-chat/tool-name-registry.ts +166 -0
- package/src/adapters/openai-chat/tool-schema.ts +495 -0
- package/src/adapters/openai-chat/wire.ts +50 -0
- package/src/adapters/openai-chat.ts +40 -1452
- package/src/adapters/openai-responses/canonical-forward.ts +202 -0
- package/src/adapters/openai-responses/image-gen.ts +406 -0
- package/src/adapters/openai-responses/internal.ts +3 -0
- package/src/adapters/openai-responses/passthrough.ts +642 -0
- package/src/adapters/openai-responses/prompt-cache.ts +83 -0
- package/src/adapters/openai-responses/reasoning.ts +220 -0
- package/src/adapters/openai-responses/request-strips.ts +185 -0
- package/src/adapters/openai-responses/tool-output-recovery.ts +509 -0
- package/src/adapters/openai-responses/tool-schema.ts +293 -0
- package/src/adapters/openai-responses/web-search.ts +156 -0
- package/src/adapters/openai-responses.ts +4 -2625
- package/src/bridge/errors.ts +58 -0
- package/src/bridge/internal.ts +174 -0
- package/src/bridge/response-json.ts +630 -0
- package/src/bridge/sse.ts +1462 -0
- package/src/bridge.ts +5 -2204
- package/src/chat/inbound.ts +12 -1
- package/src/claude/desktop-profile.ts +66 -9
- package/src/claude/outbound.ts +18 -0
- package/src/cli/account-main.ts +1 -1
- package/src/cli/capabilities.ts +2 -2
- package/src/cli/combo.ts +10 -1
- package/src/cli/index.ts +48 -5
- package/src/cli/registry.ts +2 -1
- package/src/cli/system-command.ts +4 -4
- package/src/clients/config-export.ts +7 -3
- package/src/codex/account-label.ts +14 -3
- package/src/codex/account-lifecycle.ts +3 -0
- package/src/codex/account-store.ts +184 -35
- package/src/codex/account-usability.ts +21 -0
- package/src/codex/auth-api/account-list.ts +507 -0
- package/src/codex/auth-api/http.ts +32 -0
- package/src/codex/auth-api/login-flow.ts +566 -0
- package/src/codex/auth-api/login-state.ts +64 -0
- package/src/codex/auth-api/main-account-probe.ts +331 -0
- package/src/codex/auth-api/pool-mode-gate.ts +274 -0
- package/src/codex/auth-api/pool-quota-probe.ts +512 -0
- package/src/codex/auth-api/reset-credit-service.ts +431 -0
- package/src/codex/auth-api/routes.ts +425 -0
- package/src/codex/auth-api/runtime-config.ts +48 -0
- package/src/codex/auth-api.ts +27 -3118
- package/src/codex/auth-context.ts +252 -35
- package/src/codex/catalog/aggregation.ts +80 -1
- package/src/codex/catalog/auto-review.ts +507 -0
- package/src/codex/catalog/build-entries.ts +981 -0
- package/src/codex/catalog/combo-member.ts +375 -0
- package/src/codex/catalog/derive-entry.ts +229 -0
- package/src/codex/catalog/effort.ts +0 -1
- package/src/codex/catalog/gated-native-warn.ts +63 -0
- package/src/codex/catalog/gather-capture.ts +533 -0
- package/src/codex/catalog/model-hints.ts +691 -0
- package/src/codex/catalog/model-visibility.ts +305 -0
- package/src/codex/catalog/provider-fetch.ts +52 -2942
- package/src/codex/catalog/provider-models.ts +685 -0
- package/src/codex/catalog/remote.ts +30 -0
- package/src/codex/catalog/restore.ts +132 -0
- package/src/codex/catalog/retained-sync.ts +714 -0
- package/src/codex/catalog/routed-gather.ts +895 -0
- package/src/codex/catalog/subagent-roster.ts +176 -0
- package/src/codex/catalog/sync.ts +52 -2698
- package/src/codex/cli-install-provenance.ts +7 -1
- package/src/codex/convergence.ts +7 -2
- package/src/codex/desktop-app/types.ts +11 -2
- package/src/codex/desktop-app/windows.ts +5 -5
- package/src/codex/inject/config-toml.ts +563 -0
- package/src/codex/inject/remove.ts +192 -0
- package/src/codex/inject/restore.ts +567 -0
- package/src/codex/inject/routing-classify.ts +109 -0
- package/src/codex/inject/routing-target.ts +125 -0
- package/src/codex/inject.ts +89 -1444
- package/src/codex/lineage.ts +458 -0
- package/src/codex/model-entitlements.ts +152 -15
- package/src/codex/pool-refresh-backoff.ts +161 -0
- package/src/codex/quota-rejection.ts +104 -15
- package/src/codex/routing/active-account.ts +194 -0
- package/src/codex/routing/cache-affinity.ts +70 -0
- package/src/codex/routing/cooldown-math.ts +285 -0
- package/src/codex/routing/health-store.ts +402 -0
- package/src/codex/routing/probe-lease.ts +358 -0
- package/src/codex/routing/selection.ts +780 -0
- package/src/codex/routing/thread-affinity.ts +586 -0
- package/src/codex/routing/transient-hold-dispatch.ts +141 -0
- package/src/codex/routing.ts +370 -2271
- package/src/codex/shim-fingerprint.ts +223 -0
- package/src/codex/shim-inspect.ts +175 -0
- package/src/codex/shim-probe.ts +367 -0
- package/src/codex/shim-restore-lock.ts +169 -0
- package/src/codex/shim-state-file.ts +151 -0
- package/src/codex/shim-templates.ts +265 -0
- package/src/codex/shim.ts +48 -1268
- package/src/codex/warmup.ts +1 -1
- package/src/combos/failover.ts +85 -0
- package/src/combos/request.ts +17 -10
- package/src/combos/types.ts +23 -2
- package/src/config/diagnostics.ts +705 -0
- package/src/config/feature-flags.ts +55 -0
- package/src/config/live-reconcile.ts +403 -0
- package/src/config/load-degrade.ts +880 -0
- package/src/config/mutation-lock.ts +244 -0
- package/src/config/openai-tier-backup.ts +268 -0
- package/src/config/pending-teardown.ts +31 -0
- package/src/config/persist-unlocked.ts +92 -0
- package/src/config/proxy-env.ts +188 -0
- package/src/config/salvage.ts +244 -0
- package/src/config/schema/config-schema.ts +640 -0
- package/src/config/schema/leaf-validators.ts +855 -0
- package/src/config/warn-memo.ts +28 -0
- package/src/config.ts +234 -4481
- package/src/generated/compatibility-version.json +649 -121
- package/src/images/loop.ts +1 -1
- package/src/lib/errors.ts +17 -0
- package/src/lib/request-execution-budget.ts +198 -23
- package/src/lib/spend-reservation-ledger.ts +958 -0
- package/src/lib/state-store-registrations.ts +6 -2
- package/src/lib/test-home-guard.ts +85 -1
- package/src/lib/upstream-retry.ts +132 -21
- package/src/lib/windows-elevation.ts +76 -14
- package/src/lib/workflow-budget.ts +553 -30
- package/src/oauth/index.ts +2 -2
- package/src/oauth/key-providers.ts +2 -2
- package/src/providers/kiro-models.ts +4 -3
- package/src/providers/label.ts +19 -1
- package/src/providers/model-discovery.ts +16 -0
- package/src/providers/quota/account-cache.ts +441 -0
- package/src/providers/quota/antigravity.ts +295 -0
- package/src/providers/quota/report-cache.ts +320 -0
- package/src/providers/quota/vendor-probes-key.ts +1243 -0
- package/src/providers/quota/vendor-probes-oauth.ts +590 -0
- package/src/providers/quota.ts +324 -3079
- package/src/providers/registry/entries-core.ts +1228 -0
- package/src/providers/registry/entries-extended.ts +1213 -0
- package/src/providers/registry/model-seeds.ts +912 -0
- package/src/providers/registry/types.ts +352 -0
- package/src/providers/registry.ts +24 -3536
- package/src/responses/continuation-ownership.ts +29 -0
- package/src/responses/reasoning-envelope.ts +6 -3
- package/src/responses/state/replay-fingerprint.ts +80 -0
- package/src/responses/state/snapshot-codec.ts +104 -0
- package/src/responses/state/spill-failure.ts +118 -0
- package/src/responses/state/spill-queue.ts +665 -0
- package/src/responses/state/temp-recovery.ts +257 -0
- package/src/responses/state.ts +82 -1143
- package/src/routing/identity-domains.ts +456 -0
- package/src/routing/probe-lease.ts +613 -0
- package/src/server/chat-completions.ts +3 -1
- package/src/server/chat-native.ts +37 -9
- package/src/server/index/bounded-request.ts +88 -0
- package/src/server/index/live-sideband.ts +601 -0
- package/src/server/index/serve-options.ts +1766 -0
- package/src/server/index/startup-warnings.ts +213 -0
- package/src/server/index/websocket-handler.ts +339 -0
- package/src/server/index.ts +45 -2552
- package/src/server/inspection-tee.ts +107 -0
- package/src/server/live.ts +46 -1
- package/src/server/management/combo-routes.ts +10 -1
- package/src/server/management/route-registry.ts +26 -23
- package/src/server/management/shared.ts +8 -5
- package/src/server/management/workflow-budget-routes.ts +133 -0
- package/src/server/management-api.ts +12 -0
- package/src/server/relay-eager.ts +2 -0
- package/src/server/relay.ts +14 -19
- package/src/server/request-log-conversation.ts +9 -7
- package/src/server/request-log.ts +372 -4
- package/src/server/response-log-body.ts +153 -0
- package/src/server/responses/account-change-state.ts +307 -0
- package/src/server/responses/adapter-continuation.ts +540 -0
- package/src/server/responses/adapter-delivery.ts +208 -0
- package/src/server/responses/adapter-dispatch.ts +1042 -0
- package/src/server/responses/codex-ws-wire.ts +5 -0
- package/src/server/responses/collaboration.ts +74 -4
- package/src/server/responses/combo-session-recall.ts +68 -8
- package/src/server/responses/compact.ts +113 -17
- package/src/server/responses/completion-policy.ts +33 -0
- package/src/server/responses/core-auth.ts +529 -0
- package/src/server/responses/core-codex-account.ts +907 -0
- package/src/server/responses/core-combo-failure.ts +210 -0
- package/src/server/responses/core-combo.ts +787 -0
- package/src/server/responses/core-errors.ts +170 -0
- package/src/server/responses/core-lifetime.ts +95 -0
- package/src/server/responses/core-normalize.ts +350 -0
- package/src/server/responses/core-opaque-recovery.ts +380 -0
- package/src/server/responses/core-options.ts +159 -0
- package/src/server/responses/core-replay.ts +298 -0
- package/src/server/responses/core.ts +192 -8893
- package/src/server/responses/encrypted-payload.ts +0 -1
- package/src/server/responses/input-admission.ts +126 -6
- package/src/server/responses/passthrough-delivery.ts +869 -0
- package/src/server/responses/passthrough-dispatch.ts +1494 -0
- package/src/server/responses/passthrough-error.ts +38 -2
- package/src/server/responses/passthrough-execution.ts +54 -0
- package/src/server/responses/request-prepare.ts +1080 -0
- package/src/server/responses/request-send-budget.ts +259 -0
- package/src/server/responses/request-sidecar-auth.ts +149 -0
- package/src/server/responses/request-spend.ts +147 -0
- package/src/server/responses/request-transport.ts +803 -0
- package/src/server/responses/response-effects.ts +157 -0
- package/src/server/responses/run-turn-execution.ts +476 -0
- package/src/server/responses/sidecar-execution.ts +463 -0
- package/src/server/responses/terminal-guard.ts +65 -4
- package/src/server/responses-image-gen-repair.ts +1 -1
- package/src/server/responses-undeclared-tool-guard.ts +9 -5
- package/src/server/workflow-refusal.ts +84 -0
- package/src/service/windows-ops.ts +210 -16
- package/src/service/windows-scheduler.ts +28 -21
- package/src/service.ts +1 -1
- package/src/types/config.ts +34 -1
- package/src/types/request.ts +8 -5
- package/src/types/tools.ts +24 -0
- package/src/types.ts +2 -0
- package/src/update/index.ts +10 -0
- package/src/update/stop-contract.d.mts +1 -0
- package/src/update/stop-contract.mjs +19 -0
- package/src/update/stop-decision.d.mts +1 -1
- package/src/update/stop-decision.mjs +12 -3
- package/src/usage/log.ts +147 -1
- package/src/usage/summary.ts +171 -21
- package/src/vision/anthropic-describe.ts +1 -1
- package/src/vision/describe.ts +5 -5
- package/src/web-search/anthropic-executor.ts +1 -1
- package/src/web-search/exa-executor.ts +1 -1
- package/src/web-search/executor.ts +1 -1
- package/src/web-search/gemini-executor.ts +1 -1
- package/src/web-search/loop.ts +1 -1
- package/src/web-search/ollama-executor.ts +1 -1
- package/src/web-search/parse.ts +67 -14
- package/src/web-search/passthrough-bridge.ts +64 -31
- package/src/web-search/xai-executor.ts +1 -1
package/src/chat/inbound.ts
CHANGED
|
@@ -313,7 +313,18 @@ export function chatCompletionsToResponsesBody(raw: unknown): Rec {
|
|
|
313
313
|
// here keeps that adjacency intact.
|
|
314
314
|
const reasoningText = assistantReasoningText(msg);
|
|
315
315
|
if (reasoningText !== undefined) {
|
|
316
|
-
|
|
316
|
+
// `summary` is required on a reasoning input item by the OpenAI Responses API, and our
|
|
317
|
+
// own responsesRequestSchema marks it optional, so a summary-less item validated locally
|
|
318
|
+
// and was refused upstream with `Missing required parameter: 'input[N].summary'`. It also
|
|
319
|
+
// has to carry the text, not just satisfy the field: sanitizeReasoningInputContent blanks
|
|
320
|
+
// `content` for every destination except a `preserveResponsesReasoningContent` provider,
|
|
321
|
+
// so summary is the only channel that survives to a native backend. This mirrors the
|
|
322
|
+
// Claude ingress (src/claude/inbound.ts), which has always minted both.
|
|
323
|
+
input.push({
|
|
324
|
+
type: "reasoning",
|
|
325
|
+
summary: [{ type: "summary_text", text: reasoningText }],
|
|
326
|
+
content: [{ type: "reasoning_text", text: reasoningText }],
|
|
327
|
+
});
|
|
317
328
|
}
|
|
318
329
|
const blocks = assistantContentToBlocks(msg.content);
|
|
319
330
|
if (blocks.length > 0) input.push({ type: "message", role: "assistant", content: blocks });
|
|
@@ -22,8 +22,34 @@ export interface RenderedDesktopModel extends DesktopProfileModel {
|
|
|
22
22
|
supports1m: boolean;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
// Managed-namespace date aliases run 2026-2035. The original 2026-only
|
|
26
|
+
// (365 slots) design failed with "all 365 encoded date slots are occupied"
|
|
27
|
+
// once a catalog exceeded 365 routes (stale assignments are retained by
|
|
28
|
+
// design, so the set only grows). Years before 2026 stay rejected: dated
|
|
29
|
+
// ids like `claude-opus-4-8-20250201` are real model snapshot ids, not
|
|
30
|
+
// managed aliases, and the inbound decoder relies on that distinction.
|
|
31
|
+
// Every emitted suffix stays 8 digits so modelMap date-stripping keeps
|
|
32
|
+
// working.
|
|
33
|
+
const DATE_ALIAS = /^claude-opus-4-8-(202[6-9]\d{4}|203[0-5]\d{4})$/;
|
|
34
|
+
const LEGACY_YEAR = 2026;
|
|
35
|
+
const LEGACY_DAY_COUNT = 365;
|
|
36
|
+
const ALIAS_FIRST_YEAR = 2026;
|
|
37
|
+
const ALIAS_LAST_YEAR = 2035;
|
|
38
|
+
|
|
39
|
+
function isLeapYear(year: number): boolean {
|
|
40
|
+
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function daysInAliasYear(year: number): number {
|
|
44
|
+
return isLeapYear(year) ? 366 : 365;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const TOTAL_ALIAS_SLOTS = (() => {
|
|
48
|
+
let total = 0;
|
|
49
|
+
for (let year = ALIAS_FIRST_YEAR; year <= ALIAS_LAST_YEAR; year += 1) total += daysInAliasYear(year);
|
|
50
|
+
return total;
|
|
51
|
+
})();
|
|
52
|
+
export const ALIAS_YEAR_RANGE = { first: ALIAS_FIRST_YEAR, last: ALIAS_LAST_YEAR } as const;
|
|
27
53
|
|
|
28
54
|
export class DesktopProfileError extends Error {
|
|
29
55
|
constructor(message: string, readonly path = "profile") {
|
|
@@ -119,7 +145,7 @@ export function parseDesktopProfile(value: unknown): DesktopProfile {
|
|
|
119
145
|
if (isRealAnthropicRoute(route)) {
|
|
120
146
|
if (raw.alias !== routeModelId(route)) throw new DesktopProfileError("real Anthropic routes must keep their exact model id", `profile.assignments.${route}.alias`);
|
|
121
147
|
} else if (!validDateAlias(raw.alias)) {
|
|
122
|
-
throw new DesktopProfileError("must be a valid claude-opus-4-8-
|
|
148
|
+
throw new DesktopProfileError("must be a valid claude-opus-4-8-YYYYMMDD alias", `profile.assignments.${route}.alias`);
|
|
123
149
|
}
|
|
124
150
|
if (aliases.has(raw.alias)) throw new DesktopProfileError(`duplicate alias "${raw.alias}"`, `profile.assignments.${route}.alias`);
|
|
125
151
|
aliases.add(raw.alias);
|
|
@@ -144,26 +170,57 @@ export function parseDesktopProfile(value: unknown): DesktopProfile {
|
|
|
144
170
|
return { version: 1, assignments, defaults, ...appliedMarkers(value) };
|
|
145
171
|
}
|
|
146
172
|
|
|
147
|
-
function
|
|
148
|
-
const date = new Date(Date.UTC(
|
|
173
|
+
function formatSlotDate(year: number, dayOfYear: number): string {
|
|
174
|
+
const date = new Date(Date.UTC(year, 0, dayOfYear));
|
|
149
175
|
const y = date.getUTCFullYear();
|
|
150
176
|
const m = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
151
177
|
const d = String(date.getUTCDate()).padStart(2, "0");
|
|
152
178
|
return `claude-opus-4-8-${y}${m}${d}`;
|
|
153
179
|
}
|
|
154
180
|
|
|
181
|
+
// Legacy 2026 ring, byte-identical to the original allocator: the same route
|
|
182
|
+
// must keep resolving to the same 2026 alias it always had, and a probe over
|
|
183
|
+
// a nearly-full 2026 set must land on the same free date as before.
|
|
184
|
+
function legacyDayAlias(dayIndex: number): string {
|
|
185
|
+
return formatSlotDate(LEGACY_YEAR, dayIndex + 1);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Overflow ring for catalogs past 365 routes (2027-2035). Probed only after
|
|
189
|
+
// every legacy slot is taken, so existing profiles never shift into it.
|
|
190
|
+
const OVERFLOW_FIRST_YEAR = LEGACY_YEAR + 1;
|
|
191
|
+
const OVERFLOW_SLOT_COUNT = TOTAL_ALIAS_SLOTS - LEGACY_DAY_COUNT;
|
|
192
|
+
|
|
193
|
+
function overflowSlotAlias(slotIndex: number): string {
|
|
194
|
+
let remaining = ((slotIndex % OVERFLOW_SLOT_COUNT) + OVERFLOW_SLOT_COUNT) % OVERFLOW_SLOT_COUNT;
|
|
195
|
+
for (let year = OVERFLOW_FIRST_YEAR; year <= ALIAS_LAST_YEAR; year += 1) {
|
|
196
|
+
const days = daysInAliasYear(year);
|
|
197
|
+
if (remaining < days) return formatSlotDate(year, remaining + 1);
|
|
198
|
+
remaining -= days;
|
|
199
|
+
}
|
|
200
|
+
throw new DesktopProfileError("slot index out of range", "profile.assignments");
|
|
201
|
+
}
|
|
202
|
+
|
|
155
203
|
function routeStartDay(route: string): number {
|
|
156
|
-
return createHash("sha256").update(route).digest().readUInt32BE(0) %
|
|
204
|
+
return createHash("sha256").update(route).digest().readUInt32BE(0) % LEGACY_DAY_COUNT;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function routeOverflowStart(route: string): number {
|
|
208
|
+
return createHash("sha256").update(route).digest().readUInt32BE(4) % OVERFLOW_SLOT_COUNT;
|
|
157
209
|
}
|
|
158
210
|
|
|
159
211
|
function allocateAlias(route: string, used: Set<string>): string {
|
|
160
212
|
if (isRealAnthropicRoute(route)) return routeModelId(route);
|
|
161
213
|
const start = routeStartDay(route);
|
|
162
|
-
for (let offset = 0; offset <
|
|
163
|
-
const alias =
|
|
214
|
+
for (let offset = 0; offset < LEGACY_DAY_COUNT; offset += 1) {
|
|
215
|
+
const alias = legacyDayAlias((start + offset) % LEGACY_DAY_COUNT);
|
|
216
|
+
if (!used.has(alias)) return alias;
|
|
217
|
+
}
|
|
218
|
+
const overflowStart = routeOverflowStart(route);
|
|
219
|
+
for (let offset = 0; offset < OVERFLOW_SLOT_COUNT; offset += 1) {
|
|
220
|
+
const alias = overflowSlotAlias((overflowStart + offset) % OVERFLOW_SLOT_COUNT);
|
|
164
221
|
if (!used.has(alias)) return alias;
|
|
165
222
|
}
|
|
166
|
-
throw new DesktopProfileError(
|
|
223
|
+
throw new DesktopProfileError(`all ${TOTAL_ALIAS_SLOTS} encoded date slots are occupied`, `profile.assignments.${route}.alias`);
|
|
167
224
|
}
|
|
168
225
|
|
|
169
226
|
export function reconcileDesktopProfile(
|
package/src/claude/outbound.ts
CHANGED
|
@@ -212,6 +212,8 @@ interface OpenBlock {
|
|
|
212
212
|
argsBuf?: string;
|
|
213
213
|
argsBufBytes?: number;
|
|
214
214
|
webSearchArgsEmitted?: boolean;
|
|
215
|
+
/** True once ordinary function-call arguments were emitted to Anthropic SSE. */
|
|
216
|
+
toolArgsEmitted?: boolean;
|
|
215
217
|
callId?: string;
|
|
216
218
|
/** Last fixed-size reasoning identity (item + summary/content index) seen by this block. */
|
|
217
219
|
reasoningPartKey?: string;
|
|
@@ -488,6 +490,7 @@ export function responsesSseToAnthropicSse(
|
|
|
488
490
|
argsBuf: "",
|
|
489
491
|
argsBufBytes: 0,
|
|
490
492
|
webSearchArgsEmitted: false,
|
|
493
|
+
toolArgsEmitted: false,
|
|
491
494
|
};
|
|
492
495
|
break;
|
|
493
496
|
}
|
|
@@ -518,6 +521,14 @@ export function responsesSseToAnthropicSse(
|
|
|
518
521
|
type: "content_block_delta", index: open.index,
|
|
519
522
|
delta: { type: "input_json_delta", partial_json: data.delta },
|
|
520
523
|
});
|
|
524
|
+
open.toolArgsEmitted = true;
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
case "response.function_call_arguments.done": {
|
|
528
|
+
if (!open || open.kind !== "tool_use" || open.bufferWebSearchArgs || open.toolArgsEmitted) break;
|
|
529
|
+
if (typeof data.arguments !== "string" || data.arguments.length === 0) break;
|
|
530
|
+
emit("content_block_delta", { type: "content_block_delta", index: open.index, delta: { type: "input_json_delta", partial_json: data.arguments } });
|
|
531
|
+
open.toolArgsEmitted = true;
|
|
521
532
|
break;
|
|
522
533
|
}
|
|
523
534
|
case "response.output_item.done": {
|
|
@@ -565,6 +576,13 @@ export function responsesSseToAnthropicSse(
|
|
|
565
576
|
delta: { type: "input_json_delta", partial_json: JSON.stringify(sanitizeWebSearchInput(parsed)) },
|
|
566
577
|
});
|
|
567
578
|
open.webSearchArgsEmitted = true;
|
|
579
|
+
} else if (!open.bufferWebSearchArgs && !open.toolArgsEmitted
|
|
580
|
+
&& typeof item.arguments === "string" && item.arguments.length > 0) {
|
|
581
|
+
emit("content_block_delta", {
|
|
582
|
+
type: "content_block_delta", index: open.index,
|
|
583
|
+
delta: { type: "input_json_delta", partial_json: item.arguments },
|
|
584
|
+
});
|
|
585
|
+
open.toolArgsEmitted = true;
|
|
568
586
|
}
|
|
569
587
|
closeOpenBlock();
|
|
570
588
|
}
|
package/src/cli/account-main.ts
CHANGED
|
@@ -245,7 +245,7 @@ export async function cmdNativeMainAccount(args: string[], deps: AccountDeps): P
|
|
|
245
245
|
}
|
|
246
246
|
if (noWait) {
|
|
247
247
|
printStatus({ flowId: startFlowId, ...pending });
|
|
248
|
-
console.log("follow up: ocx account main reauth status --flow " + startFlowId);
|
|
248
|
+
if (!wantsJson) console.log("follow up: ocx account main reauth status --flow " + startFlowId);
|
|
249
249
|
return 0;
|
|
250
250
|
}
|
|
251
251
|
// Blocking wait bounded by the service flow expiry (15-minute grant + margin).
|
package/src/cli/capabilities.ts
CHANGED
|
@@ -710,10 +710,10 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
710
710
|
},
|
|
711
711
|
{
|
|
712
712
|
command: ["system", "codex-restart"],
|
|
713
|
-
summary: "Restart the Codex app-
|
|
713
|
+
summary: "Restart the Codex desktop app and app-servers.",
|
|
714
714
|
routes: [{ method: "POST", path: "/api/system/codex-restart" }],
|
|
715
715
|
flags: [
|
|
716
|
-
{ name: "--yes", value: "boolean", summary: "Required:
|
|
716
|
+
{ name: "--yes", value: "boolean", summary: "Required: fully quits and relaunches the operator's Codex desktop app and restarts its app-servers." },
|
|
717
717
|
{ name: "--json", value: "boolean", summary: "Emit the restart result as JSON." },
|
|
718
718
|
],
|
|
719
719
|
mutates: true,
|
package/src/cli/combo.ts
CHANGED
|
@@ -15,7 +15,8 @@ const USAGE = `Usage:
|
|
|
15
15
|
ocx combo show <id> [--json]
|
|
16
16
|
ocx combo set <id> --targets <provider/model[:weight],...>
|
|
17
17
|
[--strategy <failover|round-robin|random|least-used|reset-window>] [--sticky <1-100>]
|
|
18
|
-
[--effort <low|medium|high|xhigh|max|ultra|->] [--
|
|
18
|
+
[--effort <low|medium|high|xhigh|max|ultra|->] [--effort-mode <fallback|force>]
|
|
19
|
+
(force overrides valid client effort and can increase cost/latency) [--alias <name|->]
|
|
19
20
|
[--native-alias] [--display-name <label|->]
|
|
20
21
|
[--rename-from <id>] [--json]
|
|
21
22
|
ocx combo remove <id> --yes [--json]`;
|
|
@@ -80,6 +81,10 @@ async function set(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
80
81
|
if (strategy !== "round-robin") throw new CliUsageError("--sticky applies only to round-robin", USAGE);
|
|
81
82
|
}
|
|
82
83
|
const effort = takeOption(args, "--effort");
|
|
84
|
+
const effortMode = takeOption(args, "--effort-mode");
|
|
85
|
+
if (effortMode !== undefined && effortMode !== "fallback" && effortMode !== "force") {
|
|
86
|
+
throw new CliUsageError("--effort-mode must be fallback or force", USAGE);
|
|
87
|
+
}
|
|
83
88
|
const alias = takeOption(args, "--alias");
|
|
84
89
|
const nativeAlias = takeFlag(args, "--native-alias");
|
|
85
90
|
const displayName = takeOption(args, "--display-name");
|
|
@@ -91,12 +96,16 @@ async function set(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
91
96
|
targets: parseTargets(targetsRaw),
|
|
92
97
|
};
|
|
93
98
|
if (effort !== undefined) combo.defaultEffort = effort === "-" ? null : effort;
|
|
99
|
+
if (effortMode !== undefined) combo.defaultEffortMode = effortMode;
|
|
94
100
|
if (alias !== undefined) combo.alias = alias === "-" ? "" : alias;
|
|
95
101
|
if (nativeAlias) combo.nativeAlias = true;
|
|
96
102
|
if (displayName !== undefined) combo.displayName = displayName === "-" ? "" : displayName;
|
|
97
103
|
const current = await runtimeRequest<{ combos?: ComboRow[] }>("/api/combos", {}, deps);
|
|
98
104
|
const existing = (current.combos ?? []).find(row => row.id === (renameFrom ?? id));
|
|
99
105
|
if (existing?.imageInput === "disabled") combo.imageInput = "disabled";
|
|
106
|
+
if (effortMode === undefined && existing?.defaultEffortMode === "force") {
|
|
107
|
+
combo.defaultEffortMode = effort === "-" ? "fallback" : "force";
|
|
108
|
+
}
|
|
100
109
|
const result = await runtimeRequest("/api/combos", {
|
|
101
110
|
method: "PUT",
|
|
102
111
|
body: JSON.stringify({ id, combo, ...(renameFrom ? { renameFrom } : {}) }),
|
package/src/cli/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ try {
|
|
|
15
15
|
}
|
|
16
16
|
import { currentExternalCodexModelProvider, restoreNativeCodex, restoreNativeCodexAsync, shouldInjectApiAuthHeader } from "../codex/inject";
|
|
17
17
|
import { stripGrokConfig } from "../grok/inject";
|
|
18
|
-
import { STOP_HISTORY_INCOMPLETE_EXIT_CODE } from "../update/stop-contract.mjs";
|
|
18
|
+
import { STOP_HISTORY_DEFERRED_EXIT_CODE, STOP_HISTORY_INCOMPLETE_EXIT_CODE } from "../update/stop-contract.mjs";
|
|
19
19
|
import {
|
|
20
20
|
describeHistoryJobFailure,
|
|
21
21
|
resolveCodexHistoryJobTarget,
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
isPendingTeardownAbandoned,
|
|
47
47
|
listPendingTeardowns,
|
|
48
48
|
pendingTeardownPathFor,
|
|
49
|
+
pendingTeardownsAreExactly,
|
|
49
50
|
quarantinePendingTeardown,
|
|
50
51
|
} from "../config/pending-teardown";
|
|
51
52
|
import { collectStatus, hubStatusLines, remoteHubBannerLine, remoteHubStatusLines, unusedProxyWarningLines } from "./status";
|
|
@@ -785,9 +786,16 @@ async function handleRestartStartWhenStopped(): Promise<boolean | "skipped"> {
|
|
|
785
786
|
*
|
|
786
787
|
* The distinction exists because `ocx update` must proceed for the first and abort for the
|
|
787
788
|
* second, and it can only see an exit code (#3008).
|
|
789
|
+
*
|
|
790
|
+
* `historyDeferred` is the third kind (#4718). The Codex history preflight refuses BEFORE
|
|
791
|
+
* the config half runs, so nothing was restored at all: config, catalog, history and
|
|
792
|
+
* provenance are untouched and the client is still routed at the proxy that just stopped.
|
|
793
|
+
* Like `historyOnly` the proxy is genuinely down, so an update may replace package files.
|
|
794
|
+
* Unlike `historyOnly` the obligation was not performed, so the receipt must survive.
|
|
788
795
|
*/
|
|
789
|
-
async function restoreSharedClientStateAfterStop(): Promise<{ historyOnly: boolean; other: boolean }> {
|
|
796
|
+
async function restoreSharedClientStateAfterStop(): Promise<{ historyOnly: boolean; historyDeferred: boolean; other: boolean }> {
|
|
790
797
|
let historyOnly = false;
|
|
798
|
+
let historyDeferred = false;
|
|
791
799
|
let other = false;
|
|
792
800
|
try {
|
|
793
801
|
const result = await restoreNativeCodexAsync();
|
|
@@ -798,7 +806,16 @@ async function restoreSharedClientStateAfterStop(): Promise<{ historyOnly: boole
|
|
|
798
806
|
// not — a client reads those, so their failure is a real teardown failure.
|
|
799
807
|
const artifacts = result.artifacts;
|
|
800
808
|
const configOrCatalogFailed = artifacts.config.state === "failed" || artifacts.catalog.state === "failed";
|
|
801
|
-
|
|
809
|
+
// A preflight refusal reports every artifact as `skipped` because none of them were
|
|
810
|
+
// attempted. Reading the states alone cannot tell that apart from an ownership
|
|
811
|
+
// refusal, so the structured reason carries it and the states are still required to
|
|
812
|
+
// agree — a refusal that somehow reports a failed artifact is not this case.
|
|
813
|
+
const preflightRefused = result.historyPreflightRefusal !== undefined
|
|
814
|
+
&& artifacts.config.state === "skipped"
|
|
815
|
+
&& artifacts.catalog.state === "skipped"
|
|
816
|
+
&& artifacts.history.state === "skipped";
|
|
817
|
+
if (preflightRefused) historyDeferred = true;
|
|
818
|
+
else if (!configOrCatalogFailed && artifacts.history.state === "failed") historyOnly = true;
|
|
802
819
|
else other = true;
|
|
803
820
|
console.error(`⚠️ ${result.message}`);
|
|
804
821
|
}
|
|
@@ -816,7 +833,7 @@ async function restoreSharedClientStateAfterStop(): Promise<{ historyOnly: boole
|
|
|
816
833
|
other = true;
|
|
817
834
|
console.error(`⚠️ Grok config restore failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
818
835
|
}
|
|
819
|
-
return { historyOnly, other };
|
|
836
|
+
return { historyOnly, historyDeferred, other };
|
|
820
837
|
}
|
|
821
838
|
|
|
822
839
|
async function handleStop() {
|
|
@@ -860,6 +877,11 @@ async function handleStop() {
|
|
|
860
877
|
};
|
|
861
878
|
let stopFailed = false;
|
|
862
879
|
let historyOnlyFailure = false;
|
|
880
|
+
/**
|
|
881
|
+
* Obligations this run deliberately kept because the Codex history preflight refused
|
|
882
|
+
* before restoring anything (#4718). Non-null selects the deferred exit code.
|
|
883
|
+
*/
|
|
884
|
+
let historyDeferredNonces: string[] | null = null;
|
|
863
885
|
// Only Task Scheduler respawns after a successful stop (#764), so only it earns the
|
|
864
886
|
// restart-window wait; launchd, systemd and WinSW are down when they say so.
|
|
865
887
|
let schedulerCanRespawn = false;
|
|
@@ -1138,6 +1160,7 @@ async function handleStop() {
|
|
|
1138
1160
|
}
|
|
1139
1161
|
const restore = await restoreSharedClientStateAfterStop();
|
|
1140
1162
|
if (restore.other) stopFailed = true;
|
|
1163
|
+
else if (restore.historyDeferred) historyDeferredNonces = teardownNonce ? [teardownNonce, ...recoveredNonces] : recoveredNonces;
|
|
1141
1164
|
else if (restore.historyOnly) historyOnlyFailure = true;
|
|
1142
1165
|
// The obligation is discharged whether or not history metadata finalized: config and
|
|
1143
1166
|
// catalog are what a client reads, and `restore.other` already fails the stop.
|
|
@@ -1145,7 +1168,16 @@ async function handleStop() {
|
|
|
1145
1168
|
// Each nonce names its own file, so a clear can only ever remove the obligation it
|
|
1146
1169
|
// names — never one a concurrent stop wrote. Both this run's claim and every inherited
|
|
1147
1170
|
// receipt it proved discharged are released together.
|
|
1148
|
-
|
|
1171
|
+
//
|
|
1172
|
+
// A history-preflight refusal is the exception: it restored nothing, so there is
|
|
1173
|
+
// nothing to discharge. Clearing here would drop a real obligation on the floor and
|
|
1174
|
+
// leave the client config pointing at a proxy that is gone, with nothing on disk
|
|
1175
|
+
// saying so — which is the whole failure the receipt exists to prevent (#4718).
|
|
1176
|
+
if (restore.historyDeferred) {
|
|
1177
|
+
console.error(" The shared teardown was refused before it changed anything, so it is still owed.");
|
|
1178
|
+
console.error(" Its receipt is preserved; run 'ocx stop' again once Codex is closed to retry the restore.");
|
|
1179
|
+
}
|
|
1180
|
+
if (!restore.other && !restore.historyDeferred) {
|
|
1149
1181
|
const discharged = teardownNonce ? [teardownNonce, ...recoveredNonces] : recoveredNonces;
|
|
1150
1182
|
for (const nonce of discharged) {
|
|
1151
1183
|
// A receipt that survives its discharge re-triggers recovery forever, so a failed
|
|
@@ -1185,6 +1217,17 @@ async function handleStop() {
|
|
|
1185
1217
|
// still wins: it is the stronger signal.
|
|
1186
1218
|
if (stopFailed) process.exitCode = 1;
|
|
1187
1219
|
else if (historyOnlyFailure) process.exitCode = STOP_HISTORY_INCOMPLETE_EXIT_CODE;
|
|
1220
|
+
// The deferred code says "the only obligations left are the ones I just decided to
|
|
1221
|
+
// keep". It is read across a process boundary by an updater that will replace package
|
|
1222
|
+
// files on the strength of it, so this run has to be able to prove the claim: if any
|
|
1223
|
+
// other obligation is sitting in the home — quarantined, or a concurrent stop's — the
|
|
1224
|
+
// claim is false and the ordinary failure code is the honest answer. That is also the
|
|
1225
|
+
// behaviour before #4718, so the fallback loses nothing that used to work.
|
|
1226
|
+
else if (historyDeferredNonces) {
|
|
1227
|
+
process.exitCode = pendingTeardownsAreExactly(historyDeferredNonces)
|
|
1228
|
+
? STOP_HISTORY_DEFERRED_EXIT_CODE
|
|
1229
|
+
: 1;
|
|
1230
|
+
}
|
|
1188
1231
|
return !stopFailed;
|
|
1189
1232
|
}
|
|
1190
1233
|
|
package/src/cli/registry.ts
CHANGED
|
@@ -407,7 +407,8 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
407
407
|
"Ensures the proxy is running, then execs `claude` with ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN,",
|
|
408
408
|
"CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 and model slots from config.claudeCode.",
|
|
409
409
|
"When Claude routing is explicitly disabled, it launches natively after removing proven OpenCodex-owned proxy state.",
|
|
410
|
-
"Routed models appear in the native /model picker with stable claude-opus-4-8-
|
|
410
|
+
"Routed models appear in the native /model picker with stable claude-opus-4-8-YYYYMMDD slot aliases,",
|
|
411
|
+
"where the year runs 2026-2035 and 2026 slots are allocated first (Claude Code >= 2.1.129).",
|
|
411
412
|
"Older versions: pick models via ANTHROPIC_MODEL or /model <id> directly (any string passes through).",
|
|
412
413
|
"User-exported ANTHROPIC_* variables take precedence for routed launches; native fallback removes only proven OpenCodex-owned proxy values.",
|
|
413
414
|
"",
|
|
@@ -130,14 +130,14 @@ export async function handleSystemCommand(argv: string[], deps: RuntimeApiDeps =
|
|
|
130
130
|
const args = [...rest]; const wantsJson = takeFlag(args, "--json"); rejectArgs(args, USAGE);
|
|
131
131
|
printData(await runtimeRequest("/api/system/codex-app-server", {}, deps), wantsJson);
|
|
132
132
|
} else if (sub === "codex-restart") {
|
|
133
|
-
// --yes required: this
|
|
134
|
-
//
|
|
133
|
+
// --yes required: this fully quits and relaunches the user's Codex desktop app as well as
|
|
134
|
+
// restarting app-servers; an agent guessing a subcommand must not interrupt that session.
|
|
135
135
|
const args = [...rest];
|
|
136
136
|
const wantsJson = takeFlag(args, "--json");
|
|
137
137
|
const yes = takeFlag(args, "--yes");
|
|
138
|
-
if (!yes) throw new CliUsageError("system codex-restart requires --yes", USAGE);
|
|
138
|
+
if (!yes) throw new CliUsageError("system codex-restart requires --yes: this fully quits and relaunches the Codex desktop app and restarts its app-servers", USAGE);
|
|
139
139
|
rejectArgs(args, USAGE);
|
|
140
|
-
printData(await runtimeRequest("/api/system/codex-restart", { method: "POST" }, deps), wantsJson, ["Codex app-server restart requested."]);
|
|
140
|
+
printData(await runtimeRequest("/api/system/codex-restart", { method: "POST" }, deps), wantsJson, ["Codex desktop app and app-server restart requested."]);
|
|
141
141
|
} else if (sub === "update") await update(rest, deps);
|
|
142
142
|
else throw new CliUsageError(`unknown system command ${sub}`, USAGE);
|
|
143
143
|
});
|
|
@@ -812,6 +812,7 @@ export interface OpenclawModelEntry {
|
|
|
812
812
|
id: string;
|
|
813
813
|
name: string;
|
|
814
814
|
contextWindow?: number;
|
|
815
|
+
input?: string[];
|
|
815
816
|
}
|
|
816
817
|
|
|
817
818
|
export interface OpenclawProviderBlock {
|
|
@@ -839,15 +840,15 @@ export interface KimiProviderBlock {
|
|
|
839
840
|
/**
|
|
840
841
|
* `max_context_size` is mandatory and must be positive, so a model with no
|
|
841
842
|
* authoritative context window is omitted from the document entirely rather
|
|
842
|
-
* than guessed at.
|
|
843
|
-
*
|
|
844
|
-
* that a routed selector will not match.
|
|
843
|
+
* than guessed at. Catalog image input becomes `image_in`; other capabilities
|
|
844
|
+
* are not inferred from routed model names.
|
|
845
845
|
*/
|
|
846
846
|
export interface KimiModelBlock {
|
|
847
847
|
provider: string;
|
|
848
848
|
model: string;
|
|
849
849
|
max_context_size: number;
|
|
850
850
|
display_name?: string;
|
|
851
|
+
capabilities?: ["image_in"];
|
|
851
852
|
}
|
|
852
853
|
|
|
853
854
|
export interface KimiGeneratedConfig {
|
|
@@ -974,10 +975,12 @@ function buildHermesClientConfig(ctx: ExportContext): HermesGeneratedConfig {
|
|
|
974
975
|
function buildOpenclawClientConfig(ctx: ExportContext): OpenclawGeneratedConfig {
|
|
975
976
|
const models: OpenclawModelEntry[] = normalizeExportModels(ctx.models).map(model => {
|
|
976
977
|
const context = authoritativeContextWindow(model.contextWindow);
|
|
978
|
+
const input = [...new Set(model.inputModalities?.filter(value => ["text", "image", "video", "audio"].includes(value)))];
|
|
977
979
|
return {
|
|
978
980
|
id: model.namespaced,
|
|
979
981
|
name: exportModelLabel(model),
|
|
980
982
|
...(context !== undefined ? { contextWindow: context } : {}),
|
|
983
|
+
...(input.length > 0 ? { input } : {}),
|
|
981
984
|
};
|
|
982
985
|
});
|
|
983
986
|
const headers = proxyAdmissionHeaders(ctx.config, OPENCLAW_API_KEY_ENV_REF);
|
|
@@ -1015,6 +1018,7 @@ function buildKimiClientConfig(ctx: ExportContext): KimiGeneratedConfig {
|
|
|
1015
1018
|
model: model.namespaced,
|
|
1016
1019
|
max_context_size: context,
|
|
1017
1020
|
...(model.displayName ? { display_name: model.displayName } : {}),
|
|
1021
|
+
...(model.inputModalities?.includes("image") ? { capabilities: ["image_in"] as ["image_in"] } : {}),
|
|
1018
1022
|
};
|
|
1019
1023
|
}
|
|
1020
1024
|
return {
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import type { CodexAccount, OcxConfig } from "../types";
|
|
3
3
|
import type { CodexAuthContext } from "./auth-context";
|
|
4
|
+
import type { ProviderApiKeySelection } from "../types/provider";
|
|
4
5
|
import { MAIN_CODEX_ACCOUNT_ID } from "./main-account";
|
|
5
6
|
|
|
6
7
|
export const CODEX_ACCOUNT_LOG_LABEL_RE = /^p[a-f0-9]{6}$/;
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
|
-
* Account log labels come in
|
|
10
|
+
* Account log labels come in three families:
|
|
10
11
|
*
|
|
11
12
|
* - `p<hex6>` (plus the literal `main`) — a Codex pool account.
|
|
12
13
|
* - `o<hex6>` — a non-Codex OAuth provider account (xai, cursor, and siblings).
|
|
14
|
+
* - `k<hex32>` — a request-owned API-key selection, scoped to provider and reference.
|
|
13
15
|
*
|
|
14
|
-
*
|
|
16
|
+
* Labels never contain an email, raw key/reference, or raw provider account id. That is
|
|
15
17
|
* a privacy requirement, not a formatting preference: these labels are written to the usage log
|
|
16
18
|
* and served over the management API.
|
|
17
19
|
*
|
|
@@ -20,7 +22,16 @@ export const CODEX_ACCOUNT_LOG_LABEL_RE = /^p[a-f0-9]{6}$/;
|
|
|
20
22
|
* accepted cost of keeping the existing `p` format byte-compatible.
|
|
21
23
|
*/
|
|
22
24
|
export const OAUTH_ACCOUNT_LOG_LABEL_RE = /^o[a-f0-9]{6}$/;
|
|
23
|
-
export const
|
|
25
|
+
export const KEY_ACCOUNT_LOG_LABEL_RE = /^k[a-f0-9]{32}$/;
|
|
26
|
+
export const ACCOUNT_LOG_LABEL_RE = /^(?:main|[po][a-f0-9]{6}|k[a-f0-9]{32})$/;
|
|
27
|
+
|
|
28
|
+
/** Digest the request-owned configured selection, never serialize its key/reference. */
|
|
29
|
+
export function apiKeyAccountLogLabel(provider: string, selection: ProviderApiKeySelection | undefined): `k${string}` | undefined {
|
|
30
|
+
if (!selection || typeof selection.reference !== "string" || !selection.reference.length) return undefined;
|
|
31
|
+
return `k${createHash("sha256").update(JSON.stringify([
|
|
32
|
+
"ocx-key-account-v1", provider, selection.entryId ?? null, selection.reference,
|
|
33
|
+
])).digest("hex").slice(0, 32)}`;
|
|
34
|
+
}
|
|
24
35
|
|
|
25
36
|
export function oauthAccountLogLabel(accountId: string, provider = ""): string {
|
|
26
37
|
return `o${createHash("sha256").update(`${provider}\0${accountId}`).digest("hex").slice(0, 6)}`;
|
|
@@ -11,6 +11,8 @@ import { getMainChatgptAccountId, readCodexTokensResult } from "./auth-collision
|
|
|
11
11
|
import { MAIN_CODEX_ACCOUNT_ID, setMainAccountPlan } from "./main-account";
|
|
12
12
|
import { clearAccountQuota } from "./quota";
|
|
13
13
|
import { clearCodexUpstreamHealthForAccount, clearThreadAccountMapForAccount } from "./routing";
|
|
14
|
+
|
|
15
|
+
import { clearCodexPoolRefreshFailure } from "./pool-refresh-backoff";
|
|
14
16
|
import { invalidateCodexWebSocketsForAccount } from "./websocket-registry";
|
|
15
17
|
import { clearMainAccountCredentialPresence, clearMainAccountInfoCache, observeMainQuotaCredential, observeMainQuotaIdentity } from "./main-account-cache";
|
|
16
18
|
import { extractAccountIdClaims } from "../oauth/chatgpt";
|
|
@@ -41,6 +43,7 @@ export function purgeCodexAccountRuntimeState(accountId: string): void {
|
|
|
41
43
|
clearAccountQuota(accountId);
|
|
42
44
|
clearThreadAccountMapForAccount(accountId);
|
|
43
45
|
clearCodexUpstreamHealthForAccount(accountId);
|
|
46
|
+
clearCodexPoolRefreshFailure(accountId);
|
|
44
47
|
if (accountId === MAIN_CODEX_ACCOUNT_ID) {
|
|
45
48
|
clearMainAccountInfoCache();
|
|
46
49
|
clearMainAccountCredentialPresence();
|