Skip to content
Merged
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
99 changes: 2 additions & 97 deletions src/server/responses/fetch-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,107 +5,12 @@ import {
shouldUseCodexWsUpstream,
type BunRuntimeGateInput,
} from "./ws-upstream";
import { bridgeToResponsesSSE, buildResponseJSON, formatErrorResponse, type ResponsesTerminalStatus } from "../../bridge";
import {
getConfigPath,
multiAgentGuidanceEnabled,
resolveEnvValue,
} from "../../config";
import { parseRequest } from "../../responses/parser";
import { buildCompactV1Output, COMPACT_PROMPT, decodeCompactionSummary, extractCompactUserMessages } from "../../responses/compaction";
import { FORWARD_HEADERS, sanitizeReasoningInputContent } from "../../adapters/openai-responses";
import { expandPreviousResponseInput, previousResponseProviderState, rememberResponseState } from "../../responses/state";
import { routeModel } from "../../router";
import {
advanceComboAfterFailure,
comboDefaultEffort,
comboFailureDecision,
comboIdFromRawBody,
concreteComboRequestBody,
getCombo,
isComboTargetInCooldown,
NoAvailableComboTargetsError,
noteComboSuccess,
parseRetryAfterMs,
pickComboTarget,
targetKey,
} from "../../combos";
import { isInjectionDebugEnabled } from "../../lib/debug-settings";
import { injectionDebugLog } from "../../lib/injection-debug-log";
import { modelInList, namespacedToolName } from "../../types";
import type { AdapterEvent, OcxConfig, OcxParsedRequest, OcxProviderConfig, OcxProviderContinuationState, OcxUsage } from "../../types";
import {
forceRefreshOAuthAccessSnapshot,
getOAuthCredentialApiBaseUrl,
getOAuthCredentialProjectId,
getValidAccessTokenSnapshot,
type OAuthAccessSnapshot,
UnsupportedOAuthProviderError,
} from "../../oauth";
import { buildWebSearchTool, planWebSearch, runWithWebSearch, shouldResolveOpenAiWebSearchSidecar } from "../../web-search";
import { describeImagesInPlace, planVisionSidecar, shouldResolveOpenAiVisionSidecar, stripImagesInPlace } from "../../vision";
import { createAdapterEventQueue, preflightAdapterEvents } from "../../adapters/run-turn-queue";
import {
applyCodexAuthContextToProvider,
CodexAccountCooldownError,
CodexAuthContextError,
CodexDirectAuthenticationError,
CodexPoolAuthenticationError,
CodexThreadAffinityExpiredError,
headersForCodexAuthContext,
isCodexAuthContextUsable,
resolveCodexAuthContext,
type CodexAuthContext,
} from "../../codex/auth-context";
import {
formatCodexProviderForLog,
recordCodexUpstreamOutcome,
type CodexUpstreamOutcome,
} from "../../codex/routing";
import { fetchWithResetRetry, fetchWithTransientRetry, applyUpstreamRecoveryInit } from "../../lib/upstream-retry";
import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential } from "../auth-cors";
import { listOpenAiForwardSidecarCandidates, resolveFirstUsableOpenAiSidecar, type ResolvedOpenAiForwardSidecar } from "../../providers/openai-sidecar";
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
import { slugsEquivalent } from "../../providers/slug-codec";
import { applyOpenAiVirtualModel, resolveOpenAiCompactModel } from "../../providers/openai-virtual-models";
import { isUsageDebugEnabled } from "../../usage/debug";
import { readJsonRequestBody, DecompressedBodyTooLargeError, UnsupportedContentEncodingError } from "../request-decompress";
import { resolveAdapter, resolveWireProtocolOverride } from "../adapter-resolve";
import { hasKeyPoolFailover, rotateProviderTransportOn429 } from "../../providers/key-failover";
import { shouldAttemptImageTierRetry } from "../image-retry";
import { resolveProviderTransport } from "../../providers/xai-transport";
import type { OcxProviderConfig } from "../../types";
import type { WsData } from "../ws-bridge";
import { registerTurn, trackStreamLifetime, unregisterTurn } from "../lifecycle";
import { redactSecretString } from "../../lib/redact";
import { readBoundedResponseBody } from "../../lib/bounded-body";
import { supportedLadderFor } from "../effort-policy";
import {
beginRequestAttempt,
catalogModelSupportsServiceTier,
finishRequestAttempt,
inspectResponseLogJson,
noteAttemptSend,
readConfiguredCodexServiceTier,
requestLogSpeedLabel,
sealRequestAttemptIdentity,
usageFromResponsesPayload,
type RequestLogContext,
} from "../request-log";
import type { AttemptRecoveryKind } from "../../usage/log";
import {
consumeForInspection,
consumeForResponseLogMetadata,
markNativePassthroughSseResponse,
relaySseWithFailedTail,
relayWithAbort,
sanitizePassthroughHeaders,
} from "../relay";
import { hasResponsesItemIdRepair, relaySseWithResponsesItemIdRepair } from "../responses-item-id-repair";
import type { EffectiveSubagentRoster, SpawnAgentSurface } from "../../codex/catalog";
import { waitForProviderRequestSlot } from "../../providers/request-pacing";
import { withUpstreamHttpVersion } from "../../lib/upstream-http-version";

export { withUpstreamHttpVersion } from "../../lib/upstream-http-version";
export { withUpstreamHttpVersion };

export function disableResponsesRequestTimeout(req: Request, server: Pick<Server<WsData>, "timeout"> | undefined): boolean {
if (!server) return false;
Expand Down
16 changes: 16 additions & 0 deletions structure/04_transports-and-sidecars.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ executor contract. Main-request migration must not treat that branch as fixed-tr
provider, lets the selected adapter speak the upstream protocol, then bridges adapter events back to
Responses-compatible streaming output.

### Fetch-helper import boundary

`src/server/responses/fetch-helpers.ts` is a transport leaf shared by Responses, compact, and native
Chat. Its runtime imports are limited to the Codex WebSocket transport, provider request pacing, and
the upstream HTTP-version helper. Server, provider, and WebSocket data types remain type-only edges.
It must not import routing, combos, OAuth, adapters, sidecars, response parsing, logging, or relay
modules merely because those imports existed in the pre-split `responses.ts` monolith.

[Decision Log]
- 목적과 의도: Keep transport helpers reusable without making every consumer evaluate the full routed Responses and sidecar graph at module load.
- 기존 구현 및 제약 조건: The original `responses.ts` split copied the monolith import header into `fetch-helpers.ts`; seven helper exports therefore retained 39 distinct runtime import specifiers and reached 326 modules even though the implementations used only three runtime dependencies.
- 검토한 주요 대안: Leave the imports because current modules have limited top-level side effects; move the helpers again; prune the copied imports and lock the direct runtime boundary.
- 선택한 방식: Preserve the file and all public exports, remove unused runtime edges, and enforce an explicit three-specifier allowlist with a source-level regression that also proves type-only imports are ignored.
- 다른 대안 대신 이 방식을 선택한 이유: Relying on unrelated modules to remain side-effect-free makes startup ownership accidental, while another move adds churn without changing the responsibility boundary.
- 장점, 단점 및 영향: Ordinary native Chat and compact consumers no longer load unrelated routing, combo, OAuth, web-search, vision, and relay modules through this leaf. The allowlist is intentionally strict, so a future helper that needs a new runtime dependency must make that ownership decision explicit in code, tests, and this document.

[Decision Log]
- 목적과 의도: Prevent routed models from turning invented or neighboring-agent tool names into client-executable Responses calls.
- 기존 구현 및 제약 조건: The request catalog already controlled custom-tool restoration and the non-OpenAI prompt nudge, but an undeclared upstream name still fell through as an ordinary `function_call`; Codex then reduced the mismatch to a bare `aborted` result.
Expand Down
78 changes: 78 additions & 0 deletions tests/responses-fetch-helpers-boundary.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { createScanner, LanguageVariant, SyntaxKind } from "typescript/unstable/ast";

const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const helperPath = resolve(repoRoot, "src/server/responses/fetch-helpers.ts");

interface RuntimeImportScan {
specifiers: string[];
nonLiteralDynamicImports: string[];
}

const importTranspiler = new Bun.Transpiler({ loader: "ts" });

function nonLiteralDynamicImports(source: string): string[] {
const scanner = createScanner(true, LanguageVariant.Standard, source);
const imports: string[] = [];
for (;;) {
const token = scanner.scan();
if (token === SyntaxKind.EndOfFile) return imports;
if (token !== SyntaxKind.ImportKeyword) continue;
if (scanner.scan() !== SyntaxKind.OpenParenToken) continue;
const argument = scanner.scan();
if (argument !== SyntaxKind.StringLiteral) imports.push(scanner.getTokenText());
}
}

function runtimeImports(source: string): RuntimeImportScan {
return {
specifiers: [...new Set(importTranspiler.scanImports(source).map(item => item.path))].sort(),
nonLiteralDynamicImports: nonLiteralDynamicImports(source),
};
}

function expectRuntimeImportBoundary(source: string): string[] {
const scan = runtimeImports(source);
expect(scan.nonLiteralDynamicImports).toEqual([]);
return scan.specifiers;
}

describe("Responses fetch-helper import boundary", () => {
test("loads only transport-owned runtime dependencies", () => {
expect(expectRuntimeImportBoundary(readFileSync(helperPath, "utf8"))).toEqual([
"../../lib/upstream-http-version",
"../../providers/request-pacing",
"./ws-upstream",
]);
});

test("the guard recognizes runtime edges and ignores type-only imports", () => {
const scan = runtimeImports([
'import type { T } from "./types";',
'import { type T2 } from "./more-types";',
'export type { U } from "./other-types";',
'export { type U2 } from "./more-other-types";',
'import { a } from "./static";',
'import "./side-effect";',
'export { b } from "./re-export";',
'const c = import("./dynamic");',
'const moduleName = "./hidden";',
'const d = import(moduleName);',
'const e = import(`./template`);',
].join("\n"));
expect(scan.specifiers).toEqual([
"./dynamic",
"./re-export",
"./side-effect",
"./static",
"./template",
]);
expect(scan.nonLiteralDynamicImports).toEqual([
"moduleName",
"`./template`",
]);
});
});
Loading