Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs-site/src/content/docs/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,17 @@ See [The parser and bridge](/reference/architecture/#the-parser) for the explici
There is no provider-level setting that can add a missing `tool_search` declaration; ordinary
code-mode discovery remains a separate path.

### Cache accounting evidence

Cache summaries distinguish upstream-reported values from client-compatible defaults. An explicit
upstream zero is measured; an omitted cache counter is unknown. Cache-read and cache-creation
counts remain separate, and a missing round or sent attempt cannot become observed merely because
other rounds reported usage. Unsent candidates without usage do not dilute the aggregate.
Only complete observed cache reads contribute to the hit-rate numerator and its matching input
denominator. Partial totals can remain visible without implying a measured hit rate; historical
zero-only records without source evidence remain unknown. These are accounting corrections, not
a measured performance improvement.

### Cache-read diagnostics

Set `OPENCODEX_CACHE_DEBUG=1` before starting the proxy to write one diagnostic record per
Expand Down
2 changes: 1 addition & 1 deletion scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"version": 1,
"root": "tests",
"explicitShards": ["layout-clients.json", "layout-codex-collision.json"],
"explicit": { "combo-description.test.ts": "routing", "combo-description-catalog.test.ts": "codex-integration", "codex-provider-routing.test.ts": "codex-integration", "config-native-reasoning-retention.test.ts": "config", "server-native-reasoning-retention-merge.test.ts": "server", "server-native-reasoning-retention.test.ts": "server", "responses-retained-reasoning-stream-recovery.test.ts": "responses", "openai-native-hosted-search-history.test.ts": "adapters/openai", "responses-native-compact-search-history.test.ts": "responses", "config-hosted-search-projection.test.ts": "config", "gcp-credential-boundary.test.ts": "adapters/google", "gcp-sa-marker.test.ts": "adapters/google", "gcp-sa-adapter.test.ts": "adapters/google", "management-gcp-credential-input.test.ts": "server", "codex-preserve-signed-in.test.ts": "codex-integration", "cli-account-preservation.test.ts": "cli", "service-state-isolation.test.ts": "service", "compaction-history-framing.test.ts": "responses", "catalog-native-max-context.test.ts": "codex-integration", "protocol-argon-thought-boundary.test.ts": "responses", "service-log.test.ts": "service", "messages-native-empty-inline.test.ts": "claude-integration", "catalog-auto-review-override.test.ts": "codex-integration", "management-auto-review-settings.test.ts": "server", "management-auto-review-auth.test.ts": "server", "prefer-model-alias-ids.test.ts": "server", "responses-mimo-envelope.test.ts": "responses", "responses-call-id-linkage.test.ts": "responses", "bridge-terminal-refusal.test.ts": "adapters", "loop-terminal-refusal.test.ts": "images", "web-search-terminal-refusal.test.ts": "web-search", "combo-terminal-refusal.test.ts": "routing", "server-terminal-refusal-wiring.test.ts": "server", "incomplete-reason-normalization.test.ts": "responses", "advisor-retention.test.ts": "advisor", "responses-native-key-cooldown.test.ts": "responses", "responses-run-turn-key-readmission.test.ts": "responses", "qoder-spawn-readmission.test.ts": "adapters", "api-key-selection-cooldown.test.ts": "providers", "api-key-auto-pause-distribution.test.ts": "providers", "explicit-key-pause-oauth-boundary.test.ts": "providers", "server-chat-native-key-selection.test.ts": "server", "compact-key-selection.test.ts": "responses", "messages-native-key-selection.test.ts": "claude-integration",
"explicit": { "cache-telemetry-native-seams.test.ts": "responses", "anthropic-cache-evidence.test.ts": "adapters/anthropic", "advisor-cache-evidence-retention.test.ts": "advisor", "cache-telemetry-flow.test.ts": "responses", "cache-telemetry-guard-mergers.test.ts": "responses", "cache-telemetry-http.test.ts": "responses", "cache-telemetry-attempts.test.ts": "usage", "cache-telemetry-evidence.test.ts": "usage", "web-search-loop-cache-evidence.test.ts": "web-search", "combo-description.test.ts": "routing", "combo-description-catalog.test.ts": "codex-integration", "codex-provider-routing.test.ts": "codex-integration", "config-native-reasoning-retention.test.ts": "config", "server-native-reasoning-retention-merge.test.ts": "server", "server-native-reasoning-retention.test.ts": "server", "responses-retained-reasoning-stream-recovery.test.ts": "responses", "openai-native-hosted-search-history.test.ts": "adapters/openai", "responses-native-compact-search-history.test.ts": "responses", "config-hosted-search-projection.test.ts": "config", "gcp-credential-boundary.test.ts": "adapters/google", "gcp-sa-marker.test.ts": "adapters/google", "gcp-sa-adapter.test.ts": "adapters/google", "management-gcp-credential-input.test.ts": "server", "codex-preserve-signed-in.test.ts": "codex-integration", "cli-account-preservation.test.ts": "cli", "service-state-isolation.test.ts": "service", "compaction-history-framing.test.ts": "responses", "catalog-native-max-context.test.ts": "codex-integration", "protocol-argon-thought-boundary.test.ts": "responses", "service-log.test.ts": "service", "messages-native-empty-inline.test.ts": "claude-integration", "catalog-auto-review-override.test.ts": "codex-integration", "management-auto-review-settings.test.ts": "server", "management-auto-review-auth.test.ts": "server", "prefer-model-alias-ids.test.ts": "server", "responses-mimo-envelope.test.ts": "responses", "responses-call-id-linkage.test.ts": "responses", "bridge-terminal-refusal.test.ts": "adapters", "loop-terminal-refusal.test.ts": "images", "web-search-terminal-refusal.test.ts": "web-search", "combo-terminal-refusal.test.ts": "routing", "server-terminal-refusal-wiring.test.ts": "server", "incomplete-reason-normalization.test.ts": "responses", "advisor-retention.test.ts": "advisor", "responses-native-key-cooldown.test.ts": "responses", "responses-run-turn-key-readmission.test.ts": "responses", "qoder-spawn-readmission.test.ts": "adapters", "api-key-selection-cooldown.test.ts": "providers", "api-key-auto-pause-distribution.test.ts": "providers", "explicit-key-pause-oauth-boundary.test.ts": "providers", "server-chat-native-key-selection.test.ts": "server", "compact-key-selection.test.ts": "responses", "messages-native-key-selection.test.ts": "claude-integration",
"openai-chat-standalone-bare-block.test.ts": "adapters/openai",
"owner-registry-isolation.test.ts": "config",
"responses-auto-pause-replay-owner.test.ts": "responses", "responses-account-auto-pause.test.ts": "responses", "oauth-account-auto-pause.test.ts": "oauth", "responses-account-auto-pause-wire-owner.test.ts": "responses", "oauth-upsert-paused-api-key.test.ts": "oauth", "management-account-auto-pause.test.ts": "server", "cli-account-key-pause.test.ts": "cli", "oauth-account-auto-pause-revision.test.ts": "oauth", "config-account-auto-pause-revisions.test.ts": "config", "codex-history-pending-wal.test.ts": "codex-integration", "responses-anthropic-billing-send-budget.test.ts": "responses", "anthropic-billing-ownership.test.ts": "adapters/anthropic", "server-anthropic-billing.test.ts": "server", "server-anthropic-billing-boundaries.test.ts": "server", "router-anthropic-billing-failover.test.ts": "routing", "anthropic-billing-refusal.test.ts": "adapters/anthropic", "messages-request-id-headers.test.ts": "claude-integration", "bounded-file-read.test.ts": "lib", "responses-undeclared-tool-guard-inbound.test.ts": "responses", "explicit-empty-tool-catalog.test.ts": "responses", "explicit-empty-tool-catalog-history.test.ts": "responses",
Expand Down
2 changes: 2 additions & 0 deletions src/adapters/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createAdapterTierMetadata, type AdapterTierMetadata } from "../provider
import { ANTHROPIC_FAST_MODE_BETA, mergeAnthropicBetaHeader } from "../providers/anthropic-fast";
import { createToolCallIdAllocator, type ToolCallIdAllocator } from "./tool-call-id";
import { debugDroppedFrame } from "../lib/debug";
import { rawCacheTelemetry } from "../usage/cache-telemetry";
import type {
AdapterEvent,
OcxAssistantMessage,
Expand Down Expand Up @@ -652,6 +653,7 @@ function usageFromAnthropic(usage: unknown): OcxUsage | undefined {
return {
inputTokens,
outputTokens: output,
cacheTelemetry: rawCacheTelemetry(usage),
...(reasoningOutputTokens !== undefined ? { reasoningOutputTokens } : {}),
...(hasCache ? {
cachedInputTokens: read,
Expand Down
6 changes: 5 additions & 1 deletion src/adapters/openai-responses/passthrough.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { truncationReasonFor } from "../../responses/truncated-stop-reason";
import { normalizeRoutedAgentMessages } from "../routed-agent-messages";
import { frameCompactionHistory } from "../../responses/compaction-history-framing";
import { rawCacheTelemetry } from "../../usage/cache-telemetry";
import { nameRoutedIdentity, repairIdentityInResponsesBody, stripRoutedIdentity } from "../identity";
import { stripBracketedModelSuffix } from "../openai-chat";
import { normalizeOpenCodeGoAdditionalTools } from "../opencode-go-additional-tools";
Expand Down Expand Up @@ -140,12 +141,15 @@ function usageFromResponsesPayload(payload: unknown): OcxUsage | undefined {
&& key !== "cache_write_1h_tokens"))
|| (isPlainObject(usage.output_tokens_details)
&& Object.keys(usage.output_tokens_details).some(key => key !== "reasoning_tokens"));
if (inputTokens === 0 && outputTokens === 0 && !hasExtras) return undefined;
if (inputTokens === 0 && outputTokens === 0 && !hasExtras
&& typeof (usage.input_tokens_details as Record<string, unknown> | undefined)?.cached_tokens !== "number"
&& typeof (usage.input_tokens_details as Record<string, unknown> | undefined)?.cache_write_tokens !== "number") return undefined;
const inputDetails = isPlainObject(usage.input_tokens_details) ? usage.input_tokens_details : undefined;
const outputDetails = isPlainObject(usage.output_tokens_details) ? usage.output_tokens_details : undefined;
return {
inputTokens,
outputTokens,
cacheTelemetry: rawCacheTelemetry(usage),
...(typeof usage.total_tokens === "number" ? { totalTokens: usage.total_tokens } : {}),
...(typeof inputDetails?.cached_tokens === "number" ? { cachedInputTokens: inputDetails.cached_tokens } : {}),
...(typeof inputDetails?.cache_write_tokens === "number" ? { cacheCreationInputTokens: inputDetails.cache_write_tokens } : {}),
Expand Down
7 changes: 6 additions & 1 deletion src/server/relay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,8 @@ export type SseInspector = {
};

export type SseInspectorHandlers = {
/** True only at the upstream observation seam, before client encoders/defaults. */
upstreamUsage?: boolean;
onTerminal?: (status: ResponsesTerminalStatus, httpStatusOverride?: number) => void;
logCtx?: RequestLogContext;
onCompletedResponse?: (response: { id?: unknown; output?: unknown; status?: unknown }) => void;
Expand Down Expand Up @@ -1326,7 +1328,7 @@ export function createSseInspector(handlers: SseInspectorHandlers): SseInspector
}
}
if (!reported && handlers.logCtx) {
inspectResponseLogSsePayloadParsed(handlers.logCtx, payload, parsed);
inspectResponseLogSsePayloadParsed(handlers.logCtx, payload, parsed, handlers.upstreamUsage ? "upstream" : "client");
}
// Before any terminal handling: a consumer deciding on the whole turn must observe this
// payload even when the terminal snapshot that follows no longer mentions it.
Expand Down Expand Up @@ -1536,6 +1538,7 @@ export function createSseInspector(handlers: SseInspectorHandlers): SseInspector
export type InspectionDrainBounds = { ms: number; bytes: number };

export type InspectionConsumerOptions = {
upstreamUsage?: boolean;
clientGoneSignal?: AbortSignal;
drainBounds?: Partial<InspectionDrainBounds>;
upstream?: AbortController;
Expand Down Expand Up @@ -1714,6 +1717,7 @@ export function consumeForInspection(
onTerminal,
logCtx,
onCompletedResponse,
upstreamUsage: options?.upstreamUsage,
onParsedPayload: payload => {
const message = boundedBareUpstreamErrorMessage(payload);
if (message !== undefined) bareUpstreamError = message;
Expand Down Expand Up @@ -1774,6 +1778,7 @@ export function consumeForResponseLogMetadata(
logCtx,
onCompletedResponse,
onParsedPayload: options?.onParsedPayload,
upstreamUsage: options?.upstreamUsage,
onFirstOutput,
pinCompletedResponseIdToFirstSeen: options?.pinCompletedResponseIdToFirstSeen,
});
Expand Down
Loading
Loading