@bitkyc08/opencodex 2.76.0 → 2.78.0-preview.20261005
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 +11 -10
- package/gui/dist/assets/App-CppMz-6w.js +54 -0
- package/gui/dist/assets/App-lCIslZ1L.css +1 -0
- package/gui/dist/assets/{Tray-DjNwwrWS.js → Tray-CJWQqtVv.js} +1 -1
- package/gui/dist/assets/index-DQt9srNi.js +94 -0
- package/gui/dist/assets/index-DmQB8D_R.css +1 -0
- package/gui/dist/assets/quota-summary-BMsyoZH2.js +1 -0
- package/gui/dist/index.html +4 -3
- package/package.json +1 -1
- package/src/adapters/anthropic/beta-allowlist.ts +19 -2
- package/src/adapters/anthropic/client-identity.ts +13 -4
- package/src/adapters/anthropic/native-client-preamble.ts +31 -0
- package/src/adapters/anthropic/passthrough.ts +103 -25
- package/src/adapters/antigravity-refusal.ts +28 -0
- package/src/adapters/cursor/protobuf-request.ts +6 -0
- package/src/adapters/cursor/request-builder.ts +3 -0
- package/src/adapters/cursor/structured-output.ts +13 -0
- package/src/adapters/cursor/types.ts +2 -0
- package/src/adapters/devin/cloud-direct/chat.ts +7 -2
- package/src/adapters/devin/trajectory.ts +49 -0
- package/src/adapters/devin.ts +13 -0
- package/src/adapters/ollama-native.ts +62 -19
- package/src/adapters/openai-responses/passthrough.ts +133 -103
- package/src/adapters/openai-responses/request-strips.ts +10 -4
- package/src/adapters/zed.ts +26 -4
- package/src/bridge/response-json.ts +14 -14
- package/src/bridge/sse.ts +15 -5
- package/src/chatgpt/app-server-shim/bundle-trust.ts +40 -10
- package/src/chatgpt/app-server-shim/gate-rewrite.ts +3 -0
- package/src/claude/agents-inject.ts +4 -43
- package/src/claude/gateway-cache.ts +16 -13
- package/src/claude/inbound-model-options.ts +9 -2
- package/src/claude/intercept/cli-catalog.ts +27 -16
- package/src/claude/intercept/client-hello.ts +103 -0
- package/src/claude/intercept/model-bindings.ts +3 -2
- package/src/claude/intercept/picker-bootstrap.ts +37 -16
- package/src/claude/intercept/picker-budget.ts +41 -0
- package/src/claude/intercept/picker-listener.ts +279 -66
- package/src/claude/outbound.ts +2 -0
- package/src/claude/subagent-force-status.ts +53 -0
- package/src/claude/subagent-model.ts +93 -0
- package/src/cli/access-audio-input.ts +84 -0
- package/src/cli/access-audio-live.ts +99 -0
- package/src/cli/access-audio.ts +123 -0
- package/src/cli/access-data-client.ts +127 -0
- package/src/cli/access-data-plane.ts +104 -0
- package/src/cli/access-data-response.ts +76 -0
- package/src/cli/access.ts +65 -2
- package/src/cli/account-api.ts +33 -6
- package/src/cli/account-auth.ts +111 -42
- package/src/cli/account-key-quota.ts +118 -0
- package/src/cli/account-main.ts +11 -4
- package/src/cli/account-next-actions.ts +63 -0
- package/src/cli/account-policy-dto.ts +68 -0
- package/src/cli/account-policy.ts +167 -0
- package/src/cli/account-target.ts +8 -0
- package/src/cli/account.ts +37 -9
- package/src/cli/agent-runtime-settings.ts +130 -0
- package/src/cli/agent-settings.ts +101 -0
- package/src/cli/agent.ts +29 -2
- package/src/cli/alias.ts +7 -2
- package/src/cli/aside-profiles.ts +2 -2
- package/src/cli/capabilities-access-remote.ts +147 -0
- package/src/cli/capabilities-accounts.ts +195 -0
- package/src/cli/capabilities-agents-routing.ts +414 -0
- package/src/cli/capabilities-base.ts +783 -0
- package/src/cli/capabilities-command.ts +1 -0
- package/src/cli/capabilities-integrations.ts +264 -0
- package/src/cli/capabilities-lab.ts +214 -0
- package/src/cli/capabilities-observe-system.ts +662 -0
- package/src/cli/capabilities-provider-models.ts +302 -0
- package/src/cli/capabilities.ts +30 -1190
- package/src/cli/capability-types.ts +54 -0
- package/src/cli/catalog-command-result.ts +57 -0
- package/src/cli/chatgpt-command.ts +35 -9
- package/src/cli/claude-agent-startup-sync.ts +17 -0
- package/src/cli/claude-desktop-profile.ts +100 -0
- package/src/cli/claude-desktop.ts +39 -6
- package/src/cli/claude.ts +22 -6
- package/src/cli/codex-shim-autorestore.ts +2 -1
- package/src/cli/codex-shim-readiness.ts +109 -0
- package/src/cli/combo-input.ts +88 -0
- package/src/cli/combo-result.ts +52 -0
- package/src/cli/combo-stats.ts +123 -0
- package/src/cli/combo.ts +37 -14
- package/src/cli/companion-timeline.ts +142 -0
- package/src/cli/companion-usage.ts +146 -0
- package/src/cli/companion.ts +12 -3
- package/src/cli/config-command.ts +24 -8
- package/src/cli/connect.ts +19 -0
- package/src/cli/dispatch.ts +55 -82
- package/src/cli/doctor.ts +22 -4
- package/src/cli/export-command.ts +20 -9
- package/src/cli/gui.ts +8 -3
- package/src/cli/help-catalog.ts +101 -0
- package/src/cli/help-models-context.ts +17 -0
- package/src/cli/help-navigation.ts +50 -0
- package/src/cli/help-recovery.ts +66 -0
- package/src/cli/help.ts +69 -11
- package/src/cli/index.ts +38 -27
- package/src/cli/injection-follow.ts +35 -0
- package/src/cli/inspect.ts +5 -0
- package/src/cli/integration-aside-sync.ts +69 -0
- package/src/cli/integration-cursor.ts +106 -0
- package/src/cli/integration-input.ts +60 -0
- package/src/cli/integration-journal.ts +49 -0
- package/src/cli/integration-plan-dto.ts +156 -0
- package/src/cli/integration-preview.ts +215 -0
- package/src/cli/integrations.ts +24 -18
- package/src/cli/json-input.ts +134 -0
- package/src/cli/link.ts +32 -4
- package/src/cli/local-sync-result.ts +41 -0
- package/src/cli/log-follow.ts +84 -0
- package/src/cli/log-view-filter.ts +146 -0
- package/src/cli/logout-command.ts +44 -0
- package/src/cli/model-picker-ordering.ts +137 -0
- package/src/cli/models-custom-input.ts +107 -0
- package/src/cli/models-custom-runtime.ts +112 -0
- package/src/cli/models-order.ts +194 -0
- package/src/cli/models-runtime-subcommands.ts +2 -0
- package/src/cli/models-runtime.ts +9 -1
- package/src/cli/models.ts +66 -81
- package/src/cli/observe-stream.ts +142 -0
- package/src/cli/observe.ts +75 -34
- package/src/cli/opencode.ts +54 -10
- package/src/cli/provider-batch.ts +77 -0
- package/src/cli/provider-lifecycle-runtime.ts +170 -0
- package/src/cli/provider-result.ts +124 -0
- package/src/cli/provider-runtime.ts +15 -12
- package/src/cli/provider-settings.ts +143 -0
- package/src/cli/provider.ts +116 -64
- package/src/cli/registry.ts +90 -24
- package/src/cli/remote-workspace-hub.ts +83 -0
- package/src/cli/remote-workspace.ts +6 -1
- package/src/cli/restart-failure.ts +27 -0
- package/src/cli/root.ts +42 -9
- package/src/cli/route-policy-write.ts +137 -0
- package/src/cli/route-policy.ts +8 -0
- package/src/cli/runtime-api.ts +57 -80
- package/src/cli/secret-args.ts +149 -0
- package/src/cli/settings-result.ts +30 -0
- package/src/cli/storage.ts +77 -7
- package/src/cli/system-command.ts +60 -2
- package/src/cli/system-restart-client.ts +20 -3
- package/src/cli/system-settings-parity.ts +83 -0
- package/src/cli/tray-proxy.ts +3 -2
- package/src/cli/update-restart-candidate.ts +21 -0
- package/src/cli/update-restart-child.ts +81 -0
- package/src/cli/update-restart-home.ts +38 -0
- package/src/cli/update-restart-transport.ts +193 -0
- package/src/cli/update-restart.ts +260 -0
- package/src/cli/usage-model-search.ts +58 -0
- package/src/cli/usage-report.ts +13 -5
- package/src/cli/v2-input.ts +66 -0
- package/src/cli/v2-local-output.ts +37 -0
- package/src/cli/v2-runtime.ts +90 -0
- package/src/cli/v2.ts +102 -144
- package/src/clients/config-export/contracts.ts +100 -0
- package/src/clients/config-export/droid.ts +54 -2
- package/src/clients/config-export/dsh.ts +21 -0
- package/src/clients/config-export/kilo.ts +12 -2
- package/src/clients/config-export/model-metadata.ts +10 -4
- package/src/clients/config-export/reasoning-metadata.ts +47 -0
- package/src/clients/config-export.ts +53 -67
- package/src/clients/omo-role-models.ts +17 -3
- package/src/codex/account-credit-use.ts +4 -2
- package/src/codex/account-runtime-state.ts +21 -0
- package/src/codex/account-store.ts +11 -47
- package/src/codex/account-usability.ts +11 -8
- package/src/codex/auth-api/account-list.ts +73 -63
- package/src/codex/auth-api/main-account-probe.ts +25 -12
- package/src/codex/auth-api/pool-mode-gate.ts +6 -9
- package/src/codex/auth-api/pool-quota-probe.ts +2 -4
- package/src/codex/auth-api/reset-credit-service.ts +3 -2
- package/src/codex/auth-context.ts +30 -11
- package/src/codex/catalog/aggregation.ts +3 -0
- package/src/codex/catalog/build-entries.ts +1 -1
- package/src/codex/catalog/bundled.ts +9 -2
- package/src/codex/catalog/heal-observation.ts +66 -0
- package/src/codex/catalog/model-hints.ts +2 -1
- package/src/codex/catalog/model-visibility.ts +6 -3
- package/src/codex/catalog/parsing.ts +2 -0
- package/src/codex/catalog/provider-models.ts +13 -4
- package/src/codex/catalog/publication-observer.ts +19 -0
- package/src/codex/catalog/remote.ts +13 -15
- package/src/codex/catalog/restore.ts +19 -6
- package/src/codex/catalog/retained-sync.ts +60 -11
- package/src/codex/catalog/routed-removal.ts +116 -0
- package/src/codex/catalog/write-audit-contract.ts +21 -0
- package/src/codex/catalog/write-audit.ts +171 -0
- package/src/codex/catalog-self-heal.ts +284 -0
- package/src/codex/catalog-write-serialization.ts +71 -7
- package/src/codex/chatgpt-refresh-failure.ts +56 -0
- package/src/codex/codex-home-owner.ts +124 -0
- package/src/codex/convergence.ts +57 -16
- package/src/codex/features.ts +8 -1
- package/src/codex/inject/restore.ts +46 -4
- package/src/codex/inject.ts +11 -1
- package/src/codex/internal/catalog-writer.ts +120 -13
- package/src/codex/journal.ts +49 -13
- package/src/codex/main-account.ts +144 -11
- package/src/codex/management-convergence.ts +3 -0
- package/src/codex/quota-refresh-outcome.ts +17 -2
- package/src/codex/quota-types.ts +36 -1
- package/src/codex/quota.ts +67 -16
- package/src/codex/refresh.ts +20 -9
- package/src/codex/routing/cooldown-math.ts +10 -4
- package/src/codex/routing/selection.ts +11 -4
- package/src/codex/routing.ts +9 -9
- package/src/codex/runtime.ts +7 -1
- package/src/codex/shim-diagnostics.ts +80 -0
- package/src/codex/shim-fingerprint.ts +7 -2
- package/src/codex/shim-migration.ts +153 -0
- package/src/codex/shim-overlay.ts +509 -0
- package/src/codex/shim-path-resolution.ts +145 -0
- package/src/codex/shim-probe.ts +4 -3
- package/src/codex/shim-state-file.ts +93 -7
- package/src/codex/shim.ts +54 -107
- package/src/codex/subagent-model-fallback.ts +2 -1
- package/src/codex/sync.ts +24 -4
- package/src/combos/failover.ts +101 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/jev.ts +13 -3
- package/src/combos/resolve.ts +12 -0
- package/src/config/atomic-write.ts +13 -8
- package/src/config/diagnostics.ts +23 -1
- package/src/config/load-degrade.ts +7 -0
- package/src/config/provider-validation.ts +3 -0
- package/src/config/schema/chatgpt-desktop.ts +22 -0
- package/src/config/schema/compaction-triggers.ts +8 -0
- package/src/config/schema/config-schema.ts +43 -14
- package/src/config/schema/leaf-validators.ts +8 -3
- package/src/config/subagent-models.ts +12 -0
- package/src/generated/compatibility-version.json +645 -237
- package/src/grok/session-identity.ts +33 -0
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/merge.ts +13 -5
- package/src/integrations/mutation-plan.ts +50 -5
- package/src/integrations/omp-yaml-source.ts +181 -1
- package/src/integrations/ownership-policy.ts +66 -0
- package/src/integrations/registry.ts +50 -1
- package/src/integrations/state.ts +106 -19
- package/src/integrations/target.ts +25 -2
- package/src/integrations/writer.ts +107 -35
- package/src/lib/gui-pair-capability.ts +17 -0
- package/src/lib/package-tree-integrity.ts +42 -1
- package/src/lib/provider-client-headers.ts +62 -0
- package/src/lib/proxy-env.ts +3 -0
- package/src/lib/test-home-guard.ts +2 -1
- package/src/oauth/anthropic-account-refusal.ts +19 -8
- package/src/oauth/anthropic-continuity.ts +65 -0
- package/src/oauth/anthropic-identity.ts +75 -0
- package/src/oauth/anthropic-routing.ts +20 -15
- package/src/oauth/anthropic.ts +34 -3
- package/src/oauth/generic-account-failover.ts +21 -0
- package/src/oauth/health.ts +32 -5
- package/src/oauth/index.ts +42 -35
- package/src/oauth/local-token-detect.ts +7 -0
- package/src/oauth/pool-settings-capability.ts +9 -2
- package/src/oauth/refresh-policy.ts +2 -0
- package/src/oauth/store.ts +55 -6
- package/src/oauth/types.ts +9 -0
- package/src/protocols/encoders/messages.ts +4 -3
- package/src/protocols/plan-snapshot.ts +1 -1
- package/src/protocols/settings.ts +28 -4
- package/src/providers/antigravity-effort-families.ts +75 -0
- package/src/providers/antigravity-models.ts +54 -16
- package/src/providers/antigravity-wire-snapshot.ts +66 -0
- package/src/providers/derive.ts +2 -0
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/new-model-policy.ts +48 -10
- package/src/providers/registry/entries-extended.ts +3 -0
- package/src/providers/registry/model-ids.ts +2 -0
- package/src/providers/registry/model-seeds.ts +5 -0
- package/src/providers/registry/types.ts +4 -0
- package/src/responses/compaction.ts +19 -0
- package/src/router.ts +6 -0
- package/src/server/auth-cors.ts +9 -0
- package/src/server/caller-session-identity.ts +32 -0
- package/src/server/chat-completions.ts +10 -0
- package/src/server/claude-messages.ts +29 -15
- package/src/server/droid-reasoning-default.ts +50 -0
- package/src/server/github-copilot-responses-repair.ts +3 -6
- package/src/server/grok-responses-control-frame.ts +2 -2
- package/src/server/grok-responses-snapshot-repair.ts +4 -5
- package/src/server/gui-session.ts +33 -11
- package/src/server/index/claude-intercept-lifecycle.ts +4 -0
- package/src/server/index/serve-options.ts +13 -7
- package/src/server/index.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +12 -134
- package/src/server/management/config-routes.ts +1 -1
- package/src/server/management/context.ts +8 -1
- package/src/server/management/cursor-integration-routes.ts +3 -3
- package/src/server/management/integration-routes.ts +69 -11
- package/src/server/management/link-routes.ts +20 -13
- package/src/server/management/model-routes.ts +3 -3
- package/src/server/management/model-row-export-metadata.ts +150 -0
- package/src/server/management/model-rows.ts +148 -9
- package/src/server/management/native-integration-routes.ts +2 -2
- package/src/server/management/oauth-account-routes.ts +96 -30
- package/src/server/management/route-registry.ts +29 -27
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/subagent-model-routes.ts +214 -0
- package/src/server/messages-native-eligibility.ts +3 -6
- package/src/server/messages-native-oauth.ts +82 -35
- package/src/server/messages-native.ts +78 -16
- package/src/server/relay.ts +14 -44
- package/src/server/request-log-generation-window.ts +38 -0
- package/src/server/request-log.ts +13 -0
- package/src/server/responses/adapter-dispatch.ts +152 -28
- package/src/server/responses/codex-auth-error.ts +10 -2
- package/src/server/responses/combo-stream-preflight.ts +29 -3
- package/src/server/responses/compaction-routing.ts +10 -1
- package/src/server/responses/core-auth.ts +67 -30
- package/src/server/responses/core-codex-account.ts +6 -3
- package/src/server/responses/core-combo-failure.ts +5 -1
- package/src/server/responses/core-combo-native.ts +7 -1
- package/src/server/responses/core-combo.ts +64 -7
- package/src/server/responses/core-normalize.ts +7 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/fetch-helpers.ts +9 -0
- package/src/server/responses/passthrough-delivery.ts +1 -1
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/server/responses/request-prepare.ts +5 -0
- package/src/server/responses/request-spend.ts +30 -30
- package/src/server/responses-custom-tool-repair.ts +2 -2
- package/src/server/responses-hosted-image-display.ts +70 -9
- package/src/server/responses-terminal-repair.ts +15 -2
- package/src/server/sse-frame-buffer.ts +38 -36
- package/src/server/sse-payload-rewrite.ts +78 -49
- package/src/server/ws-bridge.ts +1 -11
- package/src/service/cli.ts +6 -1
- package/src/service/ownership-mutation-lease.d.mts +26 -0
- package/src/service/ownership-mutation-lease.mjs +76 -1
- package/src/service/windows-taskxml.ts +1 -2
- package/src/service-manager-probe.ts +36 -2
- package/src/types/config.ts +6 -0
- package/src/types/provider.ts +28 -10
- package/src/update/index.ts +2 -1
- package/src/update/npm-invocation.mjs +35 -3
- package/src/update/proxy-liveness-probe.mjs +38 -8
- package/src/update/update-failure-guidance.d.mts +6 -0
- package/src/update/update-failure-guidance.mjs +30 -5
- package/src/usage/expected-prices.ts +9 -0
- package/src/usage/log.ts +7 -0
- package/src/usage/summary.ts +82 -1
- package/src/web-search/loop.ts +11 -8
- package/src/web-search/run-turn-loop.ts +10 -7
- package/gui/dist/assets/App-CddorHdA.js +0 -51
- package/gui/dist/assets/App-DMudtISW.css +0 -1
- package/gui/dist/assets/index-Btze1I3T.css +0 -1
- package/gui/dist/assets/index-CvnY7E6i.js +0 -86
- package/gui/dist/assets/quota-summary-B_AWpHfP.js +0 -1
|
@@ -61,6 +61,7 @@ import { resolveWireProtocolOverride } from "./adapter-resolve";
|
|
|
61
61
|
import type { OcxConfig } from "../types";
|
|
62
62
|
import { readJsonRequestBody, resolveInboundBodyLimitBytes } from "./request-decompress";
|
|
63
63
|
import { addFinalRequestLog, httpStatusForRequestLogTerminal, recordFirstOutput, type RequestLogContext } from "./request-log";
|
|
64
|
+
import { recordGenerationEvent } from "./request-log-generation-window";
|
|
64
65
|
import { createFinalRequestLog } from "./inference/final-log";
|
|
65
66
|
import {
|
|
66
67
|
conversationIdFromClaudeMetadata,
|
|
@@ -79,7 +80,7 @@ import { upstreamWireForAdapter } from "../protocols/contract";
|
|
|
79
80
|
import { createProtocolEnvelope, type ProtocolEnvelope } from "../protocols/envelope";
|
|
80
81
|
import { featuresFromMessagesBody, type ProtocolFeature } from "../protocols/features";
|
|
81
82
|
import { credentialDomainFor, messagesBodyHasOpaqueState } from "../protocols/opaque-state";
|
|
82
|
-
import { hasAnthropicFailoverQuorum } from "../oauth/anthropic-routing";
|
|
83
|
+
import { hasAnthropicFailoverQuorum, anthropicSessionKeyFromParts } from "../oauth/anthropic-routing";
|
|
83
84
|
import { checkRepresentable, unrepresentableMessage } from "../protocols/guard";
|
|
84
85
|
import { requestPathForLane } from "../protocols/path";
|
|
85
86
|
import { resolveApiSurfaceSettings, resolveProtocolSettings } from "../protocols/settings";
|
|
@@ -143,10 +144,10 @@ function decodeClaudeFastSelector(raw: string, cc?: OcxConfig["claudeCode"]): st
|
|
|
143
144
|
return decodedBase === bare ? exact : `${decodedBase}--fast`;
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
/** Restore
|
|
147
|
-
function
|
|
147
|
+
/** Restore reversible native Claude picker aliases before Anthropic passthrough checks. */
|
|
148
|
+
function decodeNativeClaudePickerAlias(raw: string, cc?: OcxConfig["claudeCode"]): string {
|
|
148
149
|
const decoded = resolveInboundModel(raw, cc);
|
|
149
|
-
if (!decoded.startsWith("claude-
|
|
150
|
+
if (!decoded.startsWith("claude-")) return raw;
|
|
150
151
|
// A picker value saved before the ocx-claude spelling keeps the native passthrough too.
|
|
151
152
|
return claudeCodeNativeAlias(decoded) === raw || legacyAliasForNative(decoded) === raw ? decoded : raw;
|
|
152
153
|
}
|
|
@@ -334,6 +335,7 @@ export function tapAnthropicSseForLog(
|
|
|
334
335
|
let data: unknown;
|
|
335
336
|
try { data = JSON.parse(dataLine); } catch { return; }
|
|
336
337
|
if (!isRec(data)) return;
|
|
338
|
+
recordGenerationEvent(logCtx, data.type);
|
|
337
339
|
if (data.type === "message_start" && isRec(data.message) && isRec(data.message.usage)) {
|
|
338
340
|
usageAcc = { ...usageAcc, ...data.message.usage };
|
|
339
341
|
} else if (data.type === "message_delta" && isRec(data.usage)) {
|
|
@@ -894,7 +896,7 @@ async function handleClaudeMessagesWithBudget(
|
|
|
894
896
|
}
|
|
895
897
|
}
|
|
896
898
|
if (isRec(anthropicBody) && typeof anthropicBody.model === "string") {
|
|
897
|
-
anthropicBody.model =
|
|
899
|
+
anthropicBody.model = decodeNativeClaudePickerAlias(anthropicBody.model, cc);
|
|
898
900
|
}
|
|
899
901
|
if (isRec(anthropicBody) && typeof anthropicBody.model === "string") {
|
|
900
902
|
requestedModel = anthropicBody.model;
|
|
@@ -1206,6 +1208,13 @@ async function handleClaudeMessagesWithBudget(
|
|
|
1206
1208
|
// Compatibility identity is an opaque request-local handle, separate from credentials.
|
|
1207
1209
|
clientIdentity: captureAnthropicClientIdentity(req.headers),
|
|
1208
1210
|
callerAnthropicBeta: req.headers.get("anthropic-beta"),
|
|
1211
|
+
sessionKey: anthropicSessionKeyFromParts({
|
|
1212
|
+
sessionIdHeader: req.headers.get("session_id")?.trim() || req.headers.get("x-claude-code-session-id"),
|
|
1213
|
+
threadIdHeader: req.headers.get("thread_id"),
|
|
1214
|
+
clientThreadId: conversationIdFromClaudeMetadata(isRec(nativeBody.metadata) ? nativeBody.metadata : undefined),
|
|
1215
|
+
promptCacheKey: typeof internalBody.prompt_cache_key === "string" ? internalBody.prompt_cache_key : null,
|
|
1216
|
+
promptCacheKeyIsSharedCohort: cacheKeySource === "system",
|
|
1217
|
+
}),
|
|
1209
1218
|
});
|
|
1210
1219
|
}
|
|
1211
1220
|
|
|
@@ -1313,10 +1322,12 @@ async function handleClaudeMessagesWithBudget(
|
|
|
1313
1322
|
const replayRefusal = isReplayRefusalResponse(response);
|
|
1314
1323
|
// Re-shape the OpenAI-style error envelope into the Anthropic one, preserving status.
|
|
1315
1324
|
let message = `upstream error (${response.status})`;
|
|
1325
|
+
let contextError = false;
|
|
1316
1326
|
try {
|
|
1317
1327
|
const text = await response.text();
|
|
1318
1328
|
try {
|
|
1319
|
-
const parsed = JSON.parse(text) as { error?: { message?: string; type?: string } | string; message?: string };
|
|
1329
|
+
const parsed = JSON.parse(text) as { error?: { message?: string; type?: string; code?: unknown } | string; message?: string };
|
|
1330
|
+
contextError = typeof parsed?.error === "object" && parsed.error?.code === "context_length_exceeded";
|
|
1320
1331
|
const nested = typeof parsed?.error === "object" && parsed.error ? parsed.error.message : undefined;
|
|
1321
1332
|
const flat = typeof parsed?.error === "string" ? parsed.error : parsed?.message;
|
|
1322
1333
|
message = nested || flat || (text ? `upstream error (${response.status}): ${text.slice(0, 400)}` : message);
|
|
@@ -1325,7 +1336,7 @@ async function handleClaudeMessagesWithBudget(
|
|
|
1325
1336
|
}
|
|
1326
1337
|
} catch { /* keep fallback message */ }
|
|
1327
1338
|
const upstreamRetryAfter = response.headers.get("retry-after");
|
|
1328
|
-
const retryAfter = replayRefusal
|
|
1339
|
+
const retryAfter = replayRefusal || contextError
|
|
1329
1340
|
? undefined
|
|
1330
1341
|
: resolveClientRetryAfter({
|
|
1331
1342
|
status: response.status,
|
|
@@ -1345,10 +1356,10 @@ async function handleClaudeMessagesWithBudget(
|
|
|
1345
1356
|
const nativeMainFence = response.status === 503
|
|
1346
1357
|
&& upstreamRetryAfter?.trim() === "1"
|
|
1347
1358
|
&& message === CODEX_MAIN_PROFILE_MAINTENANCE_MESSAGE;
|
|
1348
|
-
const transient = !replayRefusal && !nativeMainFence && isTransientUpstreamStatus(response.status);
|
|
1359
|
+
const transient = !replayRefusal && !nativeMainFence && !contextError && isTransientUpstreamStatus(response.status);
|
|
1349
1360
|
const outStatus = replayRefusal
|
|
1350
1361
|
? REPLAY_REFUSED_STATUS
|
|
1351
|
-
: nativeMainFence ? 503 : transient ? 529 : response.status;
|
|
1362
|
+
: nativeMainFence ? 503 : contextError ? 400 : transient ? 529 : response.status;
|
|
1352
1363
|
const outHeaders = new Headers({ "Content-Type": "application/json" });
|
|
1353
1364
|
if (retryAfter) outHeaders.set("Retry-After", retryAfter);
|
|
1354
1365
|
else if (transient) outHeaders.set("Retry-After", "2");
|
|
@@ -1357,7 +1368,7 @@ async function handleClaudeMessagesWithBudget(
|
|
|
1357
1368
|
outStatus,
|
|
1358
1369
|
message,
|
|
1359
1370
|
undefined,
|
|
1360
|
-
replayRefusal ? UPSTREAM_RESET_REPLAY_REFUSED_CODE : undefined,
|
|
1371
|
+
replayRefusal ? UPSTREAM_RESET_REPLAY_REFUSED_CODE : contextError ? "context_length_exceeded" : undefined,
|
|
1361
1372
|
)), {
|
|
1362
1373
|
status: outStatus,
|
|
1363
1374
|
headers: outHeaders,
|
|
@@ -1408,7 +1419,7 @@ async function handleClaudeMessagesWithBudget(
|
|
|
1408
1419
|
);
|
|
1409
1420
|
}
|
|
1410
1421
|
return new Response(JSON.stringify(message), {
|
|
1411
|
-
status: isError ? 502 : 200,
|
|
1422
|
+
status: isError ? (translatedError?.code === "context_length_exceeded" ? 400 : 502) : 200,
|
|
1412
1423
|
headers: { "Content-Type": "application/json" },
|
|
1413
1424
|
});
|
|
1414
1425
|
}
|
|
@@ -1431,6 +1442,9 @@ async function handleClaudeMessagesWithBudget(
|
|
|
1431
1442
|
"translation_buffer_limit",
|
|
1432
1443
|
);
|
|
1433
1444
|
}
|
|
1445
|
+
if (error?.code === "context_length_exceeded") {
|
|
1446
|
+
return anthropicErrorResponse(400, error.message ?? "upstream context limit exceeded", "invalid_request_error", error.code);
|
|
1447
|
+
}
|
|
1434
1448
|
return anthropicErrorResponse(502, error?.message ?? "upstream request failed", "api_error");
|
|
1435
1449
|
}
|
|
1436
1450
|
let message: Rec;
|
|
@@ -1659,7 +1673,7 @@ export async function handleClaudeCountTokens(
|
|
|
1659
1673
|
model = stripOneMillionMarker(countRoute);
|
|
1660
1674
|
raw.model = model;
|
|
1661
1675
|
}
|
|
1662
|
-
model =
|
|
1676
|
+
model = decodeNativeClaudePickerAlias(model, cc);
|
|
1663
1677
|
raw.model = model;
|
|
1664
1678
|
// Fast-only: count_tokens never parsed an effort row, so it must not start. It returns a
|
|
1665
1679
|
// token estimate and sends no tier, so only the IDENTITY is corrected - without this the
|
|
@@ -1678,9 +1692,9 @@ export async function handleClaudeCountTokens(
|
|
|
1678
1692
|
// A thread delta would undercount; refuse it exactly as the translated Messages path does.
|
|
1679
1693
|
if (carriesMessageThread(raw)) return messageThreadUnsupportedResponse();
|
|
1680
1694
|
// PF-08: an eligible managed-key route counts the body the native lane would send.
|
|
1681
|
-
const nativeCountBody =
|
|
1682
|
-
|
|
1683
|
-
|
|
1695
|
+
const nativeCountBody = (await import("./messages-native")).nativeMessagesCountBody(
|
|
1696
|
+
config, cc, raw, { fastRow: countFastRow !== null }, captureAnthropicClientIdentity(req.headers),
|
|
1697
|
+
);
|
|
1684
1698
|
// A count answers for the prompt a real turn from this model would forward, so it projects
|
|
1685
1699
|
// the same unserialized content that turn's `message_start` floor does. Counting the raw
|
|
1686
1700
|
// caller body instead reported replayed thinking this route never sends (#4857 family).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { CODEX_REASONING_LEVELS, configuredReasoningEfforts, isDeclaredReasoningEffort } from "../reasoning-effort";
|
|
2
|
+
import { isPlainObject } from "../lib/plain-data";
|
|
3
|
+
import type { OcxProviderConfig } from "../types";
|
|
4
|
+
|
|
5
|
+
export { DROID_DEFAULT_EFFORT_HEADER } from "../clients/config-export/contracts";
|
|
6
|
+
|
|
7
|
+
type ChatRequestBody = Record<string, unknown>;
|
|
8
|
+
type DroidReasoningTarget = { provider: OcxProviderConfig; modelId: string };
|
|
9
|
+
|
|
10
|
+
function hasExplicitEffort(body: ChatRequestBody): boolean {
|
|
11
|
+
return Object.hasOwn(body, "reasoning_effort")
|
|
12
|
+
|| (isPlainObject(body.reasoning) && Object.hasOwn(body.reasoning, "effort"));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Read before the Chat body is bridged: the bridge drops null and unknown efforts, which still count as explicit. */
|
|
16
|
+
export function droidReasoningDefault(header: string | null, chatBody: ChatRequestBody): string | undefined {
|
|
17
|
+
return header && isDeclaredReasoningEffort(header) && !hasExplicitEffort(chatBody) ? header : undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function pendingDefault(
|
|
21
|
+
body: ChatRequestBody,
|
|
22
|
+
defaultEffort: string | undefined,
|
|
23
|
+
target: DroidReasoningTarget,
|
|
24
|
+
): string | undefined {
|
|
25
|
+
if (!defaultEffort || hasExplicitEffort(body)) return undefined;
|
|
26
|
+
const supportedEfforts = configuredReasoningEfforts(target.provider, target.modelId)
|
|
27
|
+
?? CODEX_REASONING_LEVELS.map(level => level.effort);
|
|
28
|
+
return supportedEfforts.includes(defaultEffort) ? defaultEffort : undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function applyDroidReasoningDefault(
|
|
32
|
+
body: ChatRequestBody,
|
|
33
|
+
defaultEffort: string | undefined,
|
|
34
|
+
target: DroidReasoningTarget,
|
|
35
|
+
): void {
|
|
36
|
+
const effort = pendingDefault(body, defaultEffort, target);
|
|
37
|
+
if (effort) body.reasoning_effort = effort;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function applyDroidResponsesReasoningDefault(
|
|
41
|
+
body: unknown,
|
|
42
|
+
defaultEffort: string | undefined,
|
|
43
|
+
target: DroidReasoningTarget,
|
|
44
|
+
): boolean {
|
|
45
|
+
if (!isPlainObject(body)) return false;
|
|
46
|
+
const effort = pendingDefault(body, defaultEffort, target);
|
|
47
|
+
if (!effort) return false;
|
|
48
|
+
body.reasoning = { ...(isPlainObject(body.reasoning) ? body.reasoning : {}), effort };
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
} from "../lib/translator-budget";
|
|
15
15
|
import {
|
|
16
16
|
replaceSseDataPayload,
|
|
17
|
+
splitSseBlock,
|
|
18
|
+
sseLineEnding as blockNewline,
|
|
17
19
|
sseDataPayload,
|
|
18
20
|
type SseBlockRewrite,
|
|
19
21
|
} from "./sse-payload-rewrite";
|
|
@@ -46,14 +48,9 @@ function byteLength(value: unknown): number {
|
|
|
46
48
|
return Buffer.byteLength(JSON.stringify(value), "utf8");
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
function blockNewline(block: string): "\r\n" | "\n" {
|
|
50
|
-
return block.includes("\r\n") ? "\r\n" : "\n";
|
|
51
|
-
}
|
|
52
|
-
|
|
53
51
|
/** Keep a named SSE block's `event:` field aligned with its JSON `type`. */
|
|
54
52
|
function withEventName(block: string, eventName: string): string {
|
|
55
|
-
const newline =
|
|
56
|
-
const lines = block.split(/\r?\n/);
|
|
53
|
+
const { newline, lines } = splitSseBlock(block);
|
|
57
54
|
let replaced = false;
|
|
58
55
|
const rewritten = lines.flatMap((line) => {
|
|
59
56
|
if (!line.startsWith("event:")) return [line];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { replaceSseDataPayload, sseDataPayload, type SseBlockRewrite } from "./sse-payload-rewrite";
|
|
1
|
+
import { replaceSseDataPayload, splitSseBlock, sseDataPayload, type SseBlockRewrite } from "./sse-payload-rewrite";
|
|
2
2
|
|
|
3
3
|
const GROK_CONTROL_FRAME_TYPES: Record<string, true> = {
|
|
4
4
|
"codex.rate_limits": true,
|
|
@@ -144,7 +144,7 @@ export function createGrokResponsesControlFrameBlockRewrite(): SseBlockRewrite {
|
|
|
144
144
|
let eventName = "";
|
|
145
145
|
// SSE overwrites the event type on every event field, including empty resets.
|
|
146
146
|
// Like sseDataPayload, remove only one optional ASCII space after the colon.
|
|
147
|
-
for (const line of block.
|
|
147
|
+
for (const line of splitSseBlock(block).lines) {
|
|
148
148
|
if (line === "event") eventName = "";
|
|
149
149
|
else if (line.startsWith("event:")) {
|
|
150
150
|
const value = line.slice("event:".length);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Strict terminal reconstruction selected by the Grok compatibility marker. */
|
|
2
2
|
import type { TranslatorBudget } from "../lib/translator-budget";
|
|
3
3
|
import { MAX_COMPLETED_OUTPUT_ITEMS, MAX_COMPLETED_OUTPUT_ITEM_SOURCE_BYTES } from "./relay";
|
|
4
|
-
import { replaceSseDataPayload, sseDataPayload, type SseBlockRewrite } from "./sse-payload-rewrite";
|
|
4
|
+
import { replaceSseDataPayload, splitSseBlock, sseDataPayload, type SseBlockRewrite } from "./sse-payload-rewrite";
|
|
5
5
|
import { isPlainObject, jsonBlock, type RetainedOutputItem } from "./responses-snapshot-codec";
|
|
6
6
|
import {
|
|
7
7
|
requestToolScope,
|
|
@@ -215,14 +215,13 @@ function refusedTerminalBlock(
|
|
|
215
215
|
},
|
|
216
216
|
});
|
|
217
217
|
const rewritten = replaceSseDataPayload(block, payload);
|
|
218
|
-
const newline =
|
|
218
|
+
const { newline, lines } = splitSseBlock(rewritten);
|
|
219
219
|
let eventRewritten = false;
|
|
220
|
-
|
|
220
|
+
return lines.map(line => {
|
|
221
221
|
if (eventRewritten || !line.startsWith("event:")) return line;
|
|
222
222
|
eventRewritten = true;
|
|
223
223
|
return `event: ${GROK_REFUSED_TERMINAL_EVENT_TYPE}`;
|
|
224
|
-
});
|
|
225
|
-
return lines.join(newline);
|
|
224
|
+
}).join(newline);
|
|
226
225
|
}
|
|
227
226
|
|
|
228
227
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import type { OcxConfig } from "../types";
|
|
3
|
-
import { canonicalGuiBrowserOrigin } from "../lib/gui-pair-capability";
|
|
3
|
+
import { canonicalGuiBrowserOrigin, standaloneGuiPairingOrigin } from "../lib/gui-pair-capability";
|
|
4
4
|
import {
|
|
5
5
|
isAllowedManagementOrigin,
|
|
6
6
|
isApiAuthRequired,
|
|
@@ -20,6 +20,8 @@ export interface GuiSessionRecord {
|
|
|
20
20
|
csrfToken: string;
|
|
21
21
|
expiresAt: number;
|
|
22
22
|
issuance: GuiSessionIssuance;
|
|
23
|
+
/** Process-local marker; never accepted from a browser. */
|
|
24
|
+
localPairing?: true;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export interface GuiSessionBootstrap extends GuiSessionRecord {
|
|
@@ -31,6 +33,7 @@ export interface GuiPairingGrantRecord {
|
|
|
31
33
|
browserOrigin: string;
|
|
32
34
|
expiresAt: number;
|
|
33
35
|
failedAttempts?: number;
|
|
36
|
+
localPairing?: true;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
export interface PairingAttemptContext {
|
|
@@ -131,6 +134,7 @@ function mintSession(
|
|
|
131
134
|
issuance: GuiSessionIssuance,
|
|
132
135
|
state: GuiSessionState,
|
|
133
136
|
now: number,
|
|
137
|
+
localPairing = false,
|
|
134
138
|
): GuiSessionBootstrap {
|
|
135
139
|
pruneExpired(state, now);
|
|
136
140
|
evictOldestSession(state);
|
|
@@ -142,8 +146,9 @@ function mintSession(
|
|
|
142
146
|
serverOrigin,
|
|
143
147
|
browserOrigin,
|
|
144
148
|
csrfToken: randomBytes(32).toString("base64url"),
|
|
145
|
-
expiresAt: now + (issuance === "loopback" ? LOOPBACK_GUI_SESSION_TTL_MS : REMOTE_GUI_SESSION_TTL_MS),
|
|
149
|
+
expiresAt: now + (issuance === "loopback" || localPairing ? LOOPBACK_GUI_SESSION_TTL_MS : REMOTE_GUI_SESSION_TTL_MS),
|
|
146
150
|
issuance,
|
|
151
|
+
...(localPairing ? { localPairing: true as const } : {}),
|
|
147
152
|
};
|
|
148
153
|
state.sessions.set(token, session);
|
|
149
154
|
return {
|
|
@@ -258,13 +263,16 @@ export function createGuiPairingGrant(
|
|
|
258
263
|
now = Date.now(),
|
|
259
264
|
): { grant: string; browserOrigin: string; serverOrigin: string; expiresAt: number } {
|
|
260
265
|
const canonicalBrowserOrigin = canonicalGuiBrowserOrigin(browserOrigin);
|
|
261
|
-
const
|
|
266
|
+
const localOrigin = standaloneGuiPairingOrigin(config);
|
|
267
|
+
const localPairing = localOrigin !== null && canonicalBrowserOrigin === localOrigin;
|
|
268
|
+
const serverOrigin = config.runtimeRole === "hub"
|
|
269
|
+
? canonicalHttpOrigin(config.hub?.managementPublicOrigin) : localOrigin;
|
|
262
270
|
if (
|
|
263
|
-
|
|
264
|
-
|| !canonicalBrowserOrigin
|
|
271
|
+
!canonicalBrowserOrigin
|
|
265
272
|
|| canonicalBrowserOrigin !== browserOrigin
|
|
266
273
|
|| !serverOrigin
|
|
267
|
-
||
|
|
274
|
+
|| (config.runtimeRole === "hub"
|
|
275
|
+
? !isRemoteGuiBrowserOriginAllowed(canonicalBrowserOrigin, config) : !localPairing)
|
|
268
276
|
) throw new TypeError("remote GUI origin is not allowed");
|
|
269
277
|
pruneExpired(state, now);
|
|
270
278
|
consumeGrantRateSlot(state, now);
|
|
@@ -276,7 +284,8 @@ export function createGuiPairingGrant(
|
|
|
276
284
|
digest = pairingGrantDigest(grant);
|
|
277
285
|
} while (state.pairingGrants.has(digest));
|
|
278
286
|
const expiresAt = now + GUI_PAIRING_GRANT_TTL_MS;
|
|
279
|
-
state.pairingGrants.set(digest, { browserOrigin: canonicalBrowserOrigin, serverOrigin, expiresAt
|
|
287
|
+
state.pairingGrants.set(digest, { browserOrigin: canonicalBrowserOrigin, serverOrigin, expiresAt,
|
|
288
|
+
...(localPairing ? { localPairing: true as const } : {}) });
|
|
280
289
|
return { grant, browserOrigin: canonicalBrowserOrigin, serverOrigin, expiresAt };
|
|
281
290
|
}
|
|
282
291
|
|
|
@@ -316,7 +325,8 @@ export function consumeGuiPairingGrant(
|
|
|
316
325
|
now = Date.now(),
|
|
317
326
|
attemptContext?: PairingAttemptContext,
|
|
318
327
|
): GuiSessionBootstrap | PairingAttemptRefusal | null {
|
|
319
|
-
|
|
328
|
+
const localOrigin = standaloneGuiPairingOrigin(config);
|
|
329
|
+
if (req.method !== "POST" || hasAlternateCredential(req) || (config.runtimeRole !== "hub" && !localOrigin)) return null;
|
|
320
330
|
// Scheme check FIRST, before the grant is parsed or looked up.
|
|
321
331
|
//
|
|
322
332
|
// A grant is single-use, so consuming one and then refusing to mint would burn the
|
|
@@ -333,6 +343,11 @@ export function consumeGuiPairingGrant(
|
|
|
333
343
|
const grant = strictPairingGrantBody(body);
|
|
334
344
|
const browserOrigin = canonicalGuiBrowserOrigin(req.headers.get("Origin"));
|
|
335
345
|
if (!grant || !browserOrigin) return null;
|
|
346
|
+
// Unlike the hub path, standalone redemption never accepts a proxy-origin claim:
|
|
347
|
+
// both origins must be the configured loopback origin and the kernel peer must be local.
|
|
348
|
+
if (localOrigin && (destination !== localOrigin || browserOrigin !== localOrigin
|
|
349
|
+
|| attemptContext?.ingress !== "public"
|
|
350
|
+
|| !["127.0.0.1", "::1", "::ffff:127.0.0.1"].includes(attemptContext.peerAddress ?? ""))) return null;
|
|
336
351
|
const context = attemptContext ?? {
|
|
337
352
|
ingress: "public",
|
|
338
353
|
peerAddress: null,
|
|
@@ -344,11 +359,14 @@ export function consumeGuiPairingGrant(
|
|
|
344
359
|
const found = findPairingGrant(grant, state);
|
|
345
360
|
if (!found) {
|
|
346
361
|
// Cross-origin browser requests must not create limiter state as a side effect.
|
|
347
|
-
if (!isRemoteGuiBrowserOriginAllowed(browserOrigin, config)) return null;
|
|
362
|
+
if (localOrigin ? browserOrigin !== localOrigin : !isRemoteGuiBrowserOriginAllowed(browserOrigin, config)) return null;
|
|
348
363
|
const source = recordSourceFailure(state, context, now);
|
|
349
364
|
return attemptContext && !source.allowed ? source : null;
|
|
350
365
|
}
|
|
351
366
|
const [digest, record] = found;
|
|
367
|
+
// A live role/config change must not turn a hub grant into local operator consent, or vice versa.
|
|
368
|
+
if (record.localPairing ? !localOrigin || record.serverOrigin !== localOrigin
|
|
369
|
+
: config.runtimeRole !== "hub") return null;
|
|
352
370
|
if (record.expiresAt <= now) {
|
|
353
371
|
state.pairingGrants.delete(digest);
|
|
354
372
|
return null;
|
|
@@ -369,7 +387,7 @@ export function consumeGuiPairingGrant(
|
|
|
369
387
|
// the session is actually minted from.
|
|
370
388
|
if (!isPairingTransportPermitted(record.serverOrigin)) return null;
|
|
371
389
|
state.pairingGrants.delete(digest);
|
|
372
|
-
return mintSession(record.serverOrigin, record.browserOrigin, "pairing", state, now);
|
|
390
|
+
return mintSession(record.serverOrigin, record.browserOrigin, "pairing", state, now, record.localPairing === true);
|
|
373
391
|
}
|
|
374
392
|
|
|
375
393
|
/**
|
|
@@ -420,6 +438,10 @@ export function authorizeGuiSessionRequest(
|
|
|
420
438
|
state.sessions.delete(token);
|
|
421
439
|
return { ok: false, reason: "expired" };
|
|
422
440
|
}
|
|
441
|
+
if (session.localPairing && standaloneGuiPairingOrigin(config) !== session.serverOrigin) {
|
|
442
|
+
state.sessions.delete(token);
|
|
443
|
+
return { ok: false, reason: "server-origin" };
|
|
444
|
+
}
|
|
423
445
|
if (managementRequestOrigin(req, config) !== session.serverOrigin) {
|
|
424
446
|
return { ok: false, reason: "server-origin" };
|
|
425
447
|
}
|
|
@@ -435,6 +457,6 @@ export function authorizeGuiSessionRequest(
|
|
|
435
457
|
const csrf = req.headers.get("x-opencodex-csrf-token")?.trim();
|
|
436
458
|
if (!csrf || !equalSecret(csrf, session.csrfToken)) return { ok: false, reason: "csrf" };
|
|
437
459
|
}
|
|
438
|
-
if (session.issuance !== "loopback") session.expiresAt = now + REMOTE_GUI_SESSION_TTL_MS;
|
|
460
|
+
if (session.issuance !== "loopback" && !session.localPairing) session.expiresAt = now + REMOTE_GUI_SESSION_TTL_MS;
|
|
439
461
|
return { ok: true, principal: "gui-session", session };
|
|
440
462
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Server } from "bun";
|
|
2
2
|
import type { OcxConfig } from "../../types";
|
|
3
|
+
import { getConfigDir } from "../../config/paths";
|
|
4
|
+
import { discardPickerCaKey } from "../../claude/intercept/picker-ca";
|
|
3
5
|
import type { PickerRouteInput } from "../../claude/intercept/picker-models";
|
|
4
6
|
import { observeClaudeDesktopMode, type ClaudeDesktopModeObservation } from "../../claude/desktop-first-party";
|
|
5
7
|
import { firstPartyDesired, type ClaudeFirstPartyDesired } from "../../claude/first-party-settings";
|
|
@@ -79,6 +81,8 @@ export function createClaudeInterceptLifecycle<T>(): ClaudeInterceptLifecycle<T>
|
|
|
79
81
|
if (!options) return Promise.resolve(outcome = { ok: false, reason: "failed" });
|
|
80
82
|
const opts = options;
|
|
81
83
|
const run = async (): Promise<ClaudeInterceptOutcome> => {
|
|
84
|
+
// Legacy key cleanup is independent of listener eligibility, including on-demand retries.
|
|
85
|
+
try { discardPickerCaKey(opts.configDir ?? getConfigDir()); } catch { /* best-effort; retried on the next attempt */ }
|
|
82
86
|
if (opts.config.runtimeRole === "client") return { ok: false, reason: "client_role" };
|
|
83
87
|
if (!claudeInterceptEnabled(opts.config)) return { ok: false, reason: "disabled" };
|
|
84
88
|
if (opts.requestedPort === 0 && typeof opts.config.claudeCode?.intercept?.port !== "number")
|
|
@@ -202,6 +202,8 @@ import type { WorkflowRefusalLog } from "../workflow-refusal";
|
|
|
202
202
|
import { readyProtocolMetadata } from "../../remote/protocol";
|
|
203
203
|
import { modelCapabilityFields } from "../models-capabilities";
|
|
204
204
|
import { createWebsocketHandler } from "./websocket-handler";
|
|
205
|
+
import { withGrokSessionIdentity } from "../../grok/session-identity";
|
|
206
|
+
import { withCallerSessionIdentity } from "../caller-session-identity";
|
|
205
207
|
|
|
206
208
|
export type ServerIngress = "public" | "unauthenticated-loopback" | "hub-management" | "claude-intercept" | "hub-link";
|
|
207
209
|
|
|
@@ -297,9 +299,11 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
297
299
|
const requestMetrics = metricsExportEnabled(config)
|
|
298
300
|
? createRequestMetricsOwner(Date.now() / 1000, cachedKiroQuotaMetricRows) : undefined;
|
|
299
301
|
const requestMetricsLogContext = requestMetrics ? { requestMetricsRecorder: requestMetrics } : {};
|
|
300
|
-
const requestManagementApiDeps: ManagementApiDeps =
|
|
301
|
-
|
|
302
|
-
:
|
|
302
|
+
const requestManagementApiDeps: ManagementApiDeps = {
|
|
303
|
+
...managementApiDeps,
|
|
304
|
+
liveListenPort: () => ctx.boundPort ?? listenPort,
|
|
305
|
+
...(requestMetrics ? { requestMetrics: { snapshot: () => requestMetrics.snapshot() } } : {}),
|
|
306
|
+
};
|
|
303
307
|
const serveOptions = {
|
|
304
308
|
idleTimeout: 255,
|
|
305
309
|
// Bun rejects an oversized body before `fetch` runs, so the listener has to be raised
|
|
@@ -1473,10 +1477,11 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1473
1477
|
logged = true;
|
|
1474
1478
|
addFinalRequestLog(requestId, start, logCtx, status, meta);
|
|
1475
1479
|
};
|
|
1476
|
-
|
|
1480
|
+
const sessionReq = withCallerSessionIdentity(withGrokSessionIdentity(req), admission);
|
|
1481
|
+
return runAdmittedHttpTurn(sessionReq, policy, async turnAdmissionLease => {
|
|
1477
1482
|
let response: Response;
|
|
1478
1483
|
try {
|
|
1479
|
-
response = await handleResponses(
|
|
1484
|
+
response = await handleResponses(sessionReq, config, logCtx, {
|
|
1480
1485
|
turnAdmissionLease,
|
|
1481
1486
|
admission,
|
|
1482
1487
|
onRequestBodyRead: () => disableResponsesRequestTimeout(req, requestServer),
|
|
@@ -1553,8 +1558,9 @@ export function createServeOptions(ctx: ServeOptionsContext) {
|
|
|
1553
1558
|
// Logging is finalized inside handleClaudeMessages (Responses-vocab tap on the
|
|
1554
1559
|
// pre-translation stream + native passthrough callbacks) — do not re-wrap the
|
|
1555
1560
|
// translated Anthropic stream here.
|
|
1556
|
-
|
|
1557
|
-
|
|
1561
|
+
const sessionReq = withCallerSessionIdentity(req, admission);
|
|
1562
|
+
return runAdmittedHttpTurn(sessionReq, policy, async turnAdmissionLease => withCors(
|
|
1563
|
+
await handleClaudeMessages(sessionReq, config, logCtx, { requestId, start, turnAdmissionLease, admission }, policy, { claudeIntercept: ingress === "claude-intercept" }),
|
|
1558
1564
|
req,
|
|
1559
1565
|
policy,
|
|
1560
1566
|
), { requestId, start, logCtx });
|
package/src/server/index.ts
CHANGED
|
@@ -281,7 +281,7 @@ function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartSe
|
|
|
281
281
|
// with the later startup sync and warns ONCE about stale app-servers; warning
|
|
282
282
|
// here instead would read a catalog mtime the sync is about to move.
|
|
283
283
|
const outcome = withCatalogWriteSerialization(startupCodexHome, permit =>
|
|
284
|
-
invalidateCodexModelsCacheWithPermit(permit, startupCodexHome));
|
|
284
|
+
invalidateCodexModelsCacheWithPermit(permit, startupCodexHome), { intent: "cache", writer: "startup-cache" });
|
|
285
285
|
// A refused permit is not a write; only a completed run that returned true is.
|
|
286
286
|
setStartupCacheInvalidationWrite(outcome.kind === "completed" && outcome.value === true);
|
|
287
287
|
} catch { /* no readable Codex home: nothing to invalidate */ }
|