Skip to content
48 changes: 48 additions & 0 deletions devlog/_plan/260915_godfile_round5/070_core_outcome.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 070 core.ts 편입 (계획 변경 기록)

000_plan.md 은 `src/server/responses/core.ts` 를 라운드5 범위 밖으로 선언했다. 별도 워크트리에서
다른 에이전트가 담당하고 있었기 때문이다. 그 작업이 완료돼 이 라운드로 편입했으므로 그 선언을 정정한다.

## 무엇이 들어왔는가

`src/server/responses/core.ts` 9,386 -> 210줄. 리프 24개가 `src/server/responses/` 하위에 생겼다.
가장 큰 리프는 `passthrough-dispatch.ts` 1,476줄이고 전부 2,000줄 아래다.

앞선 네 단위와 결정적으로 다른 점이 하나 있다. 나머지는 순수 이동이었지만 이것은 아니다.
`handleResponsesInner` 는 약 5,600줄짜리 단일 함수였고, 그 본문을 13개 처리 구간으로 나눴다.
계정 교체나 재시도 후에도 같은 상태를 보아야 하는 값들은 복사하지 않고 원래 지역 변수에 연결된
getter/setter 로 넘긴다: 전송 예산, adapter, 인증 snapshot, 도구 별칭, 취소 상태, continuation 재시도 횟수.

이 결정이 라운드5 의 `serveOptions` 추출과 같은 성질이다. 거기서도 가변 캡처 3개를 구조 분해하면
스냅샷이 되어 조용히 깨졌다. 여기서는 그 대상이 6종이고, 대상이 하나라도 값 복사로 새면 계정 교체
직후의 재시도가 이전 계정의 예산과 adapter 를 들고 돌아간다.

## 오라클 처리 방식이 더 낫다

라운드5 는 오라클을 손으로 재지정했고 두 번 놓쳤다. bridge 에서는 경로가 조립돼 있어서 리터럴 검색이
못 봤고(CI 에서 `Received value does not have a length property: null`), server/index 에서는 같은 파일
안 세 번째 describe 를 시뮬레이션이 빠뜨렸다.

core.ts 쪽은 `tests/helpers/responses-core-source.ts` 에 모듈 목록을 상수로 두고
`readResponsesCoreSource()` 가 그 전부를 이어 읽는다. 그리고 `tests/responses/core-modules.test.ts` 가
그 목록이 실제 소스 import 그래프와 일치하는지 단언한다. 리프를 추가하고 목록에 넣지 않으면 그 테스트가
실패하므로, 오라클이 조용히 vacuous 해지는 경로가 닫힌다. 다음 라운드는 이 방식을 먼저 쓴다.

## 이 라운드의 최종 상태

| 파일 | 이전 | 이후 |
| --- | ---: | ---: |
| src/adapters/openai-responses.ts | 2,627 | 6 |
| src/bridge.ts | 2,206 | 7 |
| src/server/index.ts | 3,400 | 893 |
| src/server/responses/core.ts | 9,386 | 210 |

이로써 `src/` 의 산출물 제외 2,000줄 이상 파일은 0개가 된다. 산출물은
`src/adapters/cursor/gen/agent_pb.ts`(15,274) 하나이고 ratchet 의 generated 목록에 있다.

## 남은 것

`handleResponsesInner` 는 사라졌지만 그 자리에 1,476줄짜리 `passthrough-dispatch.ts` 가 있다.
2,000줄 게이트는 통과하지만 한 파일이 하나의 일을 한다고 말하기는 어렵다. 다음 라운드의 후보는
줄 수가 아니라 이런 "게이트는 통과하는데 여전히 큰" 리프들이다.

1 change: 1 addition & 0 deletions scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
}
},
"explicit": {
"core-modules.test.ts": "responses",
"chat-responses-control-integration.test.ts": "responses",
"coding-agent-tool-result-images.test.ts": "adapters",
"hub-usage.test.ts": "server",
Expand Down
509 changes: 509 additions & 0 deletions src/server/responses/adapter-continuation.ts

Large diffs are not rendered by default.

214 changes: 214 additions & 0 deletions src/server/responses/adapter-delivery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import type { ResponsesRequestContext } from "./core-options";
import type { PreparedResponsesRequest } from "./request-prepare";
import type { ResponsesTransport } from "./request-transport";
import type { ResponsesSidecarAuth } from "./request-sidecar-auth";
import type { ResponsesEffects } from "./response-effects";
import type { ResponsesCompletionPolicy } from "./completion-policy";
import type { AdapterExchange } from "./adapter-dispatch";
import type { AdapterContinuations } from "./adapter-continuation";
import { guardTerminalEventStream } from "./terminal-guard";
import { guardEmptyCompletionEventStream } from "./empty-completion-guard";
import { bridgeToResponsesSSE, buildResponseJSON, formatErrorResponse } from "../../bridge";
import type { OcxProviderContinuationState, AdapterEvent } from "../../types";
import { rememberResponseState } from "../../responses/state";
import { trackStreamLifetime } from "../lifecycle";
import { awaitThoughtSignatureDurability } from "../../responses/thought-signature-replay";
import { adapterResponseReachedServingTerminal } from "./core-replay";

/** One responsibility of the Responses request pipeline; state owners are explicit. */
export async function deliverAdapterResponse(
requestContext: Pick<ResponsesRequestContext, "logCtx" | "options" | "config">,
requestState: Pick<
PreparedResponsesRequest,
| "parsed"
| "translatorBudget"
| "toolBridgeMaps"
| "rememberKiroDeliveredFinalAnswer"
| "responseStateOptions"
>,
transportState: Pick<ResponsesTransport, "activeAdapter">,
sidecarState: Pick<ResponsesSidecarAuth, "routedCompaction">,
responseEffects: Pick<
ResponsesEffects,
| "cancelResponseCompletion"
| "commitReasoningReplayServingRoute"
| "continuationStateForResponse"
| "notifyResponseComplete"
>,
completionPolicy: Pick<ResponsesCompletionPolicy, "emptyCompletionGuardEnabled">,
adapterExchange: Pick<AdapterExchange, "upstreamResponse" | "upstream" | "cleanupUpstreamAbort">,
continuationState: Pick<AdapterContinuations, "terminalGuardEnabled" | "fetchTerminalGuardContinuation" | "fetchGuardedEmptyCompletionRetry">,
): Promise<Response> {
const { logCtx, options, config } = requestContext;
const {
parsed,
translatorBudget,
toolBridgeMaps,
rememberKiroDeliveredFinalAnswer,
responseStateOptions,
} = requestState;
const { upstreamResponse, upstream, cleanupUpstreamAbort } = adapterExchange;
const {
terminalGuardEnabled,
fetchTerminalGuardContinuation,
fetchGuardedEmptyCompletionRetry,
} = continuationState;
const { emptyCompletionGuardEnabled } = completionPolicy;
const {
cancelResponseCompletion,
commitReasoningReplayServingRoute,
continuationStateForResponse,
notifyResponseComplete,
} = responseEffects;
const { routedCompaction } = sidecarState;


if (parsed.stream) {
const initialEventStream = transportState.activeAdapter.parseStream(
upstreamResponse,
translatorBudget,
logCtx.activeTierMetadata,
);
const eventStream = terminalGuardEnabled
? guardTerminalEventStream({
parsed,
firstEvents: initialEventStream,
adapterName: transportState.activeAdapter.name,
maxAutoContinuations: 1,
continuation: fetchTerminalGuardContinuation,
})
: initialEventStream;
// The empty-completion guard sits OUTSIDE the terminal guard: a completed
// turn with no text and no tool call is retried with the IDENTICAL request
// (fetchTerminalGuardContinuation(parsed) replays the cached byte-identical
// request — same body, same headers, same signal).
const guardedEventStream = emptyCompletionGuardEnabled
? guardEmptyCompletionEventStream({
firstEvents: eventStream,
continuation: fetchGuardedEmptyCompletionRetry,
})
: eventStream;
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
const sseStream = bridgeToResponsesSSE(
guardedEventStream, parsed._responseModelId ?? parsed.modelId, toolNsMap, freeformToolNames, toolSearchToolNames,
() => { cancelResponseCompletion(); upstream.abort(); }, 2_000,
{
translatorBudget,
replayCacheScope: parsed._reasoningReplayScope,
...(options.forceEmptyResponseId ? { responseId: "" } : {}),
stallTimeoutSec: config.stallTimeoutSec,
hideThinkingSummary: parsed.options.hideThinkingSummary,
declaredToolNames,
toolParameterSchemas,
...(options.onFirstOutput ? { onFirstOutput: options.onFirstOutput } : {}),
...(routedCompaction ? { compaction: true } : {}),
// Same grok-surface split as the runTurn branch above.
...(logCtx.surface === "grok" ? { heartbeatStyle: "comment" as const } : {}),
onUsage: usage => {
// Raw adapter usage, pre wire-normalization (see the runTurn branch above).
logCtx.usageFromBridge = true;
if (usage) {
logCtx.usage = usage;
if (logCtx.activeAttempt) logCtx.activeAttempt.usage = usage;
}
},
onCompletedResponse: (response: Record<string, unknown>, providerState?: OcxProviderContinuationState) => {
commitReasoningReplayServingRoute();
rememberKiroDeliveredFinalAnswer(transportState.activeAdapter.name, response);
// Compaction turns must NOT enter the continuation cache: _rawBody still holds the full
// PRE-compaction history, and a later previous_response_id expansion would rehydrate the
// giant stale chain Codex just replaced.
if (!routedCompaction) {
rememberResponseState(
parsed._rawBody,
response,
continuationStateForResponse(providerState),
responseStateOptions(transportState.activeAdapter.name === "kiro"),
);
}
notifyResponseComplete(response);
},
},
);
const bridgeTurnAc = new AbortController();
const trackedSse = trackStreamLifetime(sseStream, bridgeTurnAc, cleanupUpstreamAbort, options.turnAdmissionLease);
return new Response(trackedSse, {
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" },
});
}

if (transportState.activeAdapter.parseResponse) {
let events: AdapterEvent[];
try {
const initialEvents = await transportState.activeAdapter.parseResponse(
upstreamResponse,
translatorBudget,
logCtx.activeTierMetadata,
);
let guardedEvents: AdapterEvent[];
if (terminalGuardEnabled) {
guardedEvents = [];
for await (const event of guardTerminalEventStream({
parsed,
firstEvents: (async function* () { yield* initialEvents; })(),
adapterName: transportState.activeAdapter.name,
maxAutoContinuations: 1,
continuation: fetchTerminalGuardContinuation,
})) guardedEvents.push(event);
} else {
guardedEvents = initialEvents;
}
if (emptyCompletionGuardEnabled) {
events = [];
for await (const event of guardEmptyCompletionEventStream({
firstEvents: (async function* () { yield* guardedEvents; })(),
continuation: fetchGuardedEmptyCompletionRetry,
})) events.push(event);
} else {
events = guardedEvents;
}
} finally {
cleanupUpstreamAbort();
}
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, toolSearchToolNames } = toolBridgeMaps;
let providerState: OcxProviderContinuationState | undefined;
const json = buildResponseJSON(events, parsed._responseModelId ?? parsed.modelId, {
translatorBudget,
replayCacheScope: parsed._reasoningReplayScope,
hideThinkingSummary: parsed.options.hideThinkingSummary,
toolNsMap,
declaredToolNames,
toolParameterSchemas,
freeformToolNames,
toolSearchToolNames,
...(routedCompaction ? { compaction: true } : {}),
onProviderState: state => { providerState = state; },
onUsage: usage => {
logCtx.usageFromBridge = true;
if (usage) {
logCtx.usage = usage;
if (logCtx.activeAttempt) logCtx.activeAttempt.usage = usage;
}
},
});
// See the streaming branch: compaction turns skip the continuation cache.
if (!routedCompaction) {
rememberKiroDeliveredFinalAnswer(transportState.activeAdapter.name, json);
rememberResponseState(
parsed._rawBody,
json,
continuationStateForResponse(providerState),
responseStateOptions(transportState.activeAdapter.name === "kiro"),
);
}
// #1926 gap 2: same buffered-path durability bound as the primary branch.
await awaitThoughtSignatureDurability();
if (adapterResponseReachedServingTerminal(events, json)) {
commitReasoningReplayServingRoute();
}
notifyResponseComplete(json);
return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json" } });
}

return formatErrorResponse(400, "invalid_request_error", "Non-streaming not supported by this adapter");
}
Loading
Loading