fix(copilot): stabilize responses configuration (#2579)

Integrated into release/v3.8.2
This commit is contained in:
ivan-mezentsev
2026-05-23 00:15:31 +03:00
committed by GitHub
parent 436196c167
commit 8f0a4c1ade
7 changed files with 26 additions and 865 deletions

View File

@@ -493,7 +493,7 @@ Highlights (full list under `open-sse/services/`):
| Routing intelligence | `intentClassifier.ts`, `taskAwareRouter.ts`, `backgroundTaskDetector.ts`, `volumeDetector.ts`, `wildcardRouter.ts`, `workflowFSM.ts`, `specificityDetector.ts`, `specificityRules.ts`, `specificityTypes.ts` |
| Model handling | `modelCapabilities.ts`, `modelDeprecation.ts`, `modelFamilyFallback.ts`, `modelStrip.ts`, `model.ts`, `provider.ts`, `providerRequestDefaults.ts`, `providerCostData.ts`, `payloadRules.ts` |
| Compression | `compression/` — full compression engine wiring |
| Token + session | `tokenRefresh.ts`, `sessionManager.ts`, `apiKeyRotator.ts`, `contextManager.ts`, `contextHandoff.ts`, `systemPrompt.ts`, `roleNormalizer.ts`, `responsesInputSanitizer.ts`, `responsesToolCallState.ts`, `toolSchemaSanitizer.ts`, `toolLimitDetector.ts`, `thinkingBudget.ts` |
| Token + session | `tokenRefresh.ts`, `sessionManager.ts`, `apiKeyRotator.ts`, `contextManager.ts`, `contextHandoff.ts`, `systemPrompt.ts`, `roleNormalizer.ts`, `responsesInputSanitizer.ts`, `toolSchemaSanitizer.ts`, `toolLimitDetector.ts`, `thinkingBudget.ts` |
| Tier / manifest | `tierResolver.ts`, `tierConfig.ts`, `tierDefaults.json`, `tierTypes.ts`, `manifestAdapter.ts` |
| IP / network | `ipFilter.ts`, `webSearchFallback.ts` |
| Batches | `batchProcessor.ts` |

View File

@@ -24,11 +24,6 @@ import {
type CodexClientIdentity,
} from "../config/codexIdentity.ts";
import { getAccessToken } from "../services/tokenRefresh.ts";
import {
getRememberedFunctionCallsByIds,
getRememberedResponseConversationItems,
getRememberedResponseFunctionCalls,
} from "../services/responsesToolCallState.ts";
import { sanitizeResponsesInputItems } from "../services/responsesInputSanitizer.ts";
import { getThinkingBudgetConfig, ThinkingMode } from "../services/thinkingBudget.ts";
import { CORS_HEADERS } from "../utils/cors.ts";
@@ -310,23 +305,6 @@ function convertSystemToDeveloperRole(body: Record<string, unknown>): void {
}
}
function buildRecoveredToolContextMessage(
droppedItems: Array<Record<string, unknown>>
): Record<string, unknown> {
return {
type: "message",
role: "user",
content: [
{
type: "input_text",
text:
"Recovered tool context from the previous turn. Continue using this context instead of calling the same tools again unless you must.\n" +
JSON.stringify(droppedItems),
},
],
};
}
/**
* Strip server-generated item IDs from the input array.
*
@@ -343,156 +321,8 @@ function buildRecoveredToolContextMessage(
* 3. Strips the "id" field from any object in input whose id matches a
* server-generated prefix (rs_, fc_, resp_, msg_) — so the content is
* preserved but the backend won't try to look it up
* 4. Expands locally remembered conversation snapshots for stateful follow-ups
* when the upstream backend rejects previous_response_id
* 5. Falls back to rehydrating missing function_call items if only the older
* tool-call state is available
* 6. Filters orphaned function_call/function_call_output items when one side
* of the tool exchange is still missing after local replay/fallback repair
*/
function stripStoredItemReferences(body: Record<string, unknown>): void {
const hasInput = Array.isArray(body.input) && body.input.length > 0;
const inputItems = Array.isArray(body.input) ? body.input : [];
const previousResponseId =
typeof body.previous_response_id === "string" ? body.previous_response_id : "";
const rememberedConversationItems =
hasInput && previousResponseId
? getRememberedResponseConversationItems(previousResponseId)
: [];
if (rememberedConversationItems.length > 0) {
body.input = [...rememberedConversationItems, ...inputItems];
}
const inputFunctionCallIds = new Set<string>();
const inputFunctionCallOutputIds = new Set<string>();
for (const item of Array.isArray(body.input) ? body.input : []) {
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
const record = item as Record<string, unknown>;
const type = typeof record.type === "string" ? record.type : "";
const callId = typeof record.call_id === "string" ? record.call_id : "";
if (!callId) continue;
if (type === "function_call") {
inputFunctionCallIds.add(callId);
continue;
}
if (type === "function_call_output") {
inputFunctionCallOutputIds.add(callId);
}
}
const missingFunctionCallIds = [...inputFunctionCallOutputIds].filter(
(callId) => !inputFunctionCallIds.has(callId)
);
if (hasInput && previousResponseId && missingFunctionCallIds.length > 0) {
const rememberedFunctionCalls = getRememberedResponseFunctionCalls(previousResponseId);
const globallyRememberedFunctionCalls = getRememberedFunctionCallsByIds(missingFunctionCallIds);
const injectedFunctionCalls = [...rememberedFunctionCalls, ...globallyRememberedFunctionCalls]
.filter((functionCall) => missingFunctionCallIds.includes(functionCall.call_id))
.filter((functionCall) => !inputFunctionCallIds.has(functionCall.call_id))
.filter(
(functionCall, index, allFunctionCalls) =>
allFunctionCalls.findIndex((candidate) => candidate.call_id === functionCall.call_id) ===
index
)
.map((functionCall) => ({
type: "function_call",
call_id: functionCall.call_id,
name:
typeof functionCall.name === "string"
? functionCall.name.slice(0, 128)
: functionCall.name,
arguments: functionCall.arguments,
}));
if (injectedFunctionCalls.length > 0) {
body.input = [...injectedFunctionCalls, ...inputItems];
for (const functionCall of injectedFunctionCalls) {
inputFunctionCallIds.add(functionCall.call_id);
}
}
}
const finalFunctionCallIds = new Set<string>();
const finalFunctionCallOutputIds = new Set<string>();
if (Array.isArray(body.input)) {
for (const item of body.input) {
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
const record = item as Record<string, unknown>;
const type = typeof record.type === "string" ? record.type : "";
const callId = typeof record.call_id === "string" ? record.call_id : "";
if (!callId) continue;
if (type === "function_call") {
finalFunctionCallIds.add(callId);
continue;
}
if (type === "function_call_output") {
finalFunctionCallOutputIds.add(callId);
}
}
}
const droppedOrphanFunctionCallIds: string[] = [];
const droppedOrphanFunctionCallOutputIds: string[] = [];
const droppedOrphanItems: Array<Record<string, unknown>> = [];
if (Array.isArray(body.input)) {
body.input = body.input.filter((item) => {
if (!item || typeof item !== "object" || Array.isArray(item)) {
return true;
}
const record = item as Record<string, unknown>;
const callId = typeof record.call_id === "string" ? record.call_id : "";
if (!callId) {
return true;
}
if (record.type === "function_call") {
if (finalFunctionCallOutputIds.has(callId)) {
return true;
}
droppedOrphanFunctionCallIds.push(callId);
droppedOrphanItems.push({ ...record });
return false;
}
if (record.type === "function_call_output") {
if (finalFunctionCallIds.has(callId)) {
return true;
}
droppedOrphanFunctionCallOutputIds.push(callId);
droppedOrphanItems.push({ ...record });
return false;
}
return true;
});
}
if (droppedOrphanFunctionCallIds.length > 0) {
console.warn(
`[Codex] stripStoredItemReferences: dropped ${droppedOrphanFunctionCallIds.length} orphan function_call item(s): ${droppedOrphanFunctionCallIds.join(", ")}`
);
}
if (droppedOrphanFunctionCallOutputIds.length > 0) {
console.warn(
`[Codex] stripStoredItemReferences: dropped ${droppedOrphanFunctionCallOutputIds.length} orphan function_call_output item(s): ${droppedOrphanFunctionCallOutputIds.join(", ")}`
);
}
if (Array.isArray(body.input) && body.input.length === 0 && droppedOrphanItems.length > 0) {
body.input = [buildRecoveredToolContextMessage(droppedOrphanItems)];
console.warn(
`[Codex] stripStoredItemReferences: synthesized recovery message from ${droppedOrphanItems.length} dropped orphan tool item(s)`
);
}
// Codex rejects previous_response_id for passthrough requests.
delete body.previous_response_id;
if (Array.isArray(body.input) && body.input.length === 0) {
body.input = [
{
@@ -1413,9 +1243,6 @@ export class CodexExecutor extends BaseExecutor {
}
delete body.reasoning_effort;
// previous_response_id is expanded into a self-contained local replay when
// input is present because Codex rejects that parameter upstream.
// Remove unsupported token limit parameters BEFORE the passthrough return.
// Codex API rejects both max_tokens and max_output_tokens regardless of
// whether the request came via native passthrough or translation.

View File

@@ -1,228 +0,0 @@
import { sanitizeResponsesInputItems } from "./responsesInputSanitizer.ts";
type JsonRecord = Record<string, unknown>;
type RememberedFunctionCall = {
call_id: string;
name: string;
arguments: string;
};
type RememberedResponseToolState = {
functionCalls: RememberedFunctionCall[];
conversationItems: unknown[];
expiresAt: number;
updatedAt: number;
};
type RememberedFunctionCallByIdState = RememberedFunctionCall & {
expiresAt: number;
updatedAt: number;
};
const RESPONSE_TOOL_CALL_TTL_MS = 30 * 60 * 1000;
const RESPONSE_TOOL_CALL_CACHE_MAX_ENTRIES = 512;
const rememberedResponseToolCalls = new Map<string, RememberedResponseToolState>();
const rememberedFunctionCallsById = new Map<string, RememberedFunctionCallByIdState>();
function toRecord(value: unknown): JsonRecord | null {
return value && typeof value === "object" && !Array.isArray(value) ? (value as JsonRecord) : null;
}
function sanitizeRememberedConversationItems(items: readonly unknown[]): unknown[] {
return sanitizeResponsesInputItems(items);
}
function cleanupRememberedResponseToolCalls(now: number = Date.now()) {
for (const [responseId, entry] of rememberedResponseToolCalls.entries()) {
if (entry.expiresAt <= now) {
rememberedResponseToolCalls.delete(responseId);
}
}
for (const [callId, entry] of rememberedFunctionCallsById.entries()) {
if (entry.expiresAt <= now) {
rememberedFunctionCallsById.delete(callId);
}
}
if (rememberedResponseToolCalls.size <= RESPONSE_TOOL_CALL_CACHE_MAX_ENTRIES) {
if (rememberedFunctionCallsById.size <= RESPONSE_TOOL_CALL_CACHE_MAX_ENTRIES) {
return;
}
}
if (rememberedResponseToolCalls.size > RESPONSE_TOOL_CALL_CACHE_MAX_ENTRIES) {
const oldestEntries = [...rememberedResponseToolCalls.entries()].sort(
(a, b) => a[1].updatedAt - b[1].updatedAt
);
while (rememberedResponseToolCalls.size > RESPONSE_TOOL_CALL_CACHE_MAX_ENTRIES) {
const oldest = oldestEntries.shift();
if (!oldest) break;
rememberedResponseToolCalls.delete(oldest[0]);
}
}
if (rememberedFunctionCallsById.size > RESPONSE_TOOL_CALL_CACHE_MAX_ENTRIES) {
const oldestCallEntries = [...rememberedFunctionCallsById.entries()].sort(
(a, b) => a[1].updatedAt - b[1].updatedAt
);
while (rememberedFunctionCallsById.size > RESPONSE_TOOL_CALL_CACHE_MAX_ENTRIES) {
const oldest = oldestCallEntries.shift();
if (!oldest) break;
rememberedFunctionCallsById.delete(oldest[0]);
}
}
}
export function rememberResponseFunctionCalls(
responseId: unknown,
outputItems: readonly unknown[]
) {
const normalizedResponseId = typeof responseId === "string" ? responseId.trim() : "";
if (!normalizedResponseId || !Array.isArray(outputItems) || outputItems.length === 0) {
return;
}
const existingEntry = rememberedResponseToolCalls.get(normalizedResponseId);
const functionCalls: RememberedFunctionCall[] = [];
for (const item of outputItems) {
const record = toRecord(item);
if (!record || record.type !== "function_call") continue;
const callId = typeof record.call_id === "string" ? record.call_id.trim() : "";
const name = typeof record.name === "string" ? record.name.trim() : "";
const argumentsValue =
typeof record.arguments === "string"
? record.arguments
: JSON.stringify(record.arguments ?? {});
if (!callId || !name) continue;
functionCalls.push({
call_id: callId,
name,
arguments: argumentsValue,
});
}
if (functionCalls.length === 0) {
return;
}
cleanupRememberedResponseToolCalls();
const now = Date.now();
for (const functionCall of functionCalls) {
rememberedFunctionCallsById.set(functionCall.call_id, {
...functionCall,
updatedAt: now,
expiresAt: now + RESPONSE_TOOL_CALL_TTL_MS,
});
}
rememberedResponseToolCalls.set(normalizedResponseId, {
functionCalls,
conversationItems: sanitizeRememberedConversationItems(existingEntry?.conversationItems || []),
updatedAt: now,
expiresAt: now + RESPONSE_TOOL_CALL_TTL_MS,
});
}
export function rememberResponseConversationState(
responseId: unknown,
requestInput: readonly unknown[],
outputItems: readonly unknown[]
) {
const normalizedResponseId = typeof responseId === "string" ? responseId.trim() : "";
if (!normalizedResponseId) {
return;
}
const normalizedRequestInput = Array.isArray(requestInput) ? requestInput : [];
const normalizedOutputItems = Array.isArray(outputItems) ? outputItems : [];
const conversationItems = sanitizeRememberedConversationItems([
...normalizedRequestInput,
...normalizedOutputItems,
]);
if (conversationItems.length === 0) {
return;
}
cleanupRememberedResponseToolCalls();
const existingEntry = rememberedResponseToolCalls.get(normalizedResponseId);
rememberedResponseToolCalls.set(normalizedResponseId, {
functionCalls: existingEntry?.functionCalls?.map((functionCall) => ({ ...functionCall })) || [],
conversationItems,
updatedAt: Date.now(),
expiresAt: Date.now() + RESPONSE_TOOL_CALL_TTL_MS,
});
}
export function getRememberedResponseFunctionCalls(responseId: unknown): RememberedFunctionCall[] {
cleanupRememberedResponseToolCalls();
const normalizedResponseId = typeof responseId === "string" ? responseId.trim() : "";
if (!normalizedResponseId) {
return [];
}
const entry = rememberedResponseToolCalls.get(normalizedResponseId);
if (!entry) {
return [];
}
return entry.functionCalls.map((functionCall) => ({ ...functionCall }));
}
export function getRememberedResponseConversationItems(responseId: unknown): unknown[] {
cleanupRememberedResponseToolCalls();
const normalizedResponseId = typeof responseId === "string" ? responseId.trim() : "";
if (!normalizedResponseId) {
return [];
}
const entry = rememberedResponseToolCalls.get(normalizedResponseId);
if (!entry) {
return [];
}
return sanitizeRememberedConversationItems(entry.conversationItems);
}
export function getRememberedFunctionCallsByIds(
callIds: readonly string[]
): RememberedFunctionCall[] {
cleanupRememberedResponseToolCalls();
if (!Array.isArray(callIds) || callIds.length === 0) {
return [];
}
const remembered: RememberedFunctionCall[] = [];
for (const rawCallId of callIds) {
const callId = typeof rawCallId === "string" ? rawCallId.trim() : "";
if (!callId) continue;
const entry = rememberedFunctionCallsById.get(callId);
if (!entry) continue;
remembered.push({
call_id: entry.call_id,
name: entry.name,
arguments: entry.arguments,
});
}
return remembered;
}
export function clearRememberedResponseFunctionCallsForTesting() {
rememberedResponseToolCalls.clear();
rememberedFunctionCallsById.clear();
}

View File

@@ -28,10 +28,6 @@ import {
sanitizeStreamingChunk,
extractThinkingFromContent,
} from "../handlers/responseSanitizer.ts";
import {
rememberResponseConversationState,
rememberResponseFunctionCalls,
} from "../services/responsesToolCallState.ts";
import { buildErrorBody } from "./error.ts";
/**
@@ -1586,25 +1582,6 @@ export function createSSEStream(options: StreamOptions = {}) {
}
clearPendingPassthroughEvent();
if (passthroughResponsesId) {
const requestInput =
body && typeof body === "object" && Array.isArray((body as JsonRecord).input)
? ((body as JsonRecord).input as unknown[])
: [];
rememberResponseConversationState(
passthroughResponsesId,
requestInput,
passthroughResponsesOutputItems
);
}
if (passthroughResponsesId && passthroughResponsesOutputItems.length > 0) {
rememberResponseFunctionCalls(
passthroughResponsesId,
passthroughResponsesOutputItems
);
}
// Estimate usage if provider didn't return valid usage
if (!hasValidUsage(usage) && totalContentLength > 0) {
usage = estimateUsage(body, totalContentLength, sourceFormat || FORMATS.OPENAI);

View File

@@ -128,6 +128,18 @@ export default function CopilotToolCard({
maxOutputTokens,
}));
const responseModels = [...selectedModels].map((modelId) => ({
id: modelId,
name: modelId,
url: `${baseUrl}/v1/responses#models.ai.azure.com`,
supportsReasoningEffort: ["none", "low", "medium", "high", "xhigh"],
zeroDataRetentionEnabled: true,
toolCalling,
vision,
maxInputTokens,
maxOutputTokens,
}));
const config = {
name: "OmniRoute",
vendor: "azure",
@@ -135,7 +147,14 @@ export default function CopilotToolCard({
models,
};
return JSON.stringify(config, null, 2);
const responsesConfig = {
name: "OmniRoute-responses",
vendor: "azure",
apiKey: `\${input:chat.lm.secret.omniroute}`,
models: responseModels,
};
return [config, responsesConfig].map((entry) => JSON.stringify(entry, null, 2)).join(",\n");
};
const handleCopy = async (text: string, field: string) => {

View File

@@ -11,11 +11,6 @@ const core = await import("../../src/lib/db/core.ts");
const { handleChatCore } = await import("../../open-sse/handlers/chatCore.ts");
const { handleComboChat } = await import("../../open-sse/services/combo.ts");
const { CodexExecutor } = await import("../../open-sse/executors/codex.ts");
const {
clearRememberedResponseFunctionCallsForTesting,
getRememberedResponseConversationItems,
rememberResponseFunctionCalls,
} = await import("../../open-sse/services/responsesToolCallState.ts");
const originalFetch = globalThis.fetch;
@@ -192,87 +187,15 @@ async function invokeChatCore({
test.beforeEach(async () => {
globalThis.fetch = originalFetch;
clearRememberedResponseFunctionCallsForTesting();
await resetStorage();
});
test.after(async () => {
globalThis.fetch = originalFetch;
clearRememberedResponseFunctionCallsForTesting();
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("chatCore remembers Codex Responses conversation state from transformed body, not raw client input", async () => {
rememberResponseFunctionCalls("resp_prev_tool_123", [
{
type: "function_call",
call_id: "call_tool_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
},
]);
const { result } = await invokeChatCore({
endpoint: "/v1/responses",
accept: "text/event-stream",
provider: "codex",
model: "gpt-5.5-low",
body: {
model: "gpt-5.5-low",
previous_response_id: "resp_prev_tool_123",
input: [
{
type: "function_call_output",
call_id: "call_tool_123",
output: '{"ok":true}',
},
],
},
responseFactory: () =>
new Response(
[
"event: response.created",
'data: {"type":"response.created","response":{"id":"resp_current_456","model":"gpt-5.5-low","status":"in_progress","output":[]}}',
"",
"event: response.completed",
'data: {"type":"response.completed","response":{"id":"resp_current_456","object":"response","model":"gpt-5.5-low","status":"completed","output":[{"type":"message","role":"assistant","content":[{"type":"output_text","text":"done"}]}],"usage":{"input_tokens":6,"output_tokens":1}}}',
"",
"data: [DONE]",
"",
].join("\n"),
{
status: 200,
headers: { "Content-Type": "text/event-stream" },
}
),
});
assert.equal(result.success, true);
await result.response.text();
await waitForAsyncSideEffects();
const rememberedItems = getRememberedResponseConversationItems("resp_current_456");
const rememberedFunctionCall = rememberedItems.find(
(item) => item && typeof item === "object" && item.type === "function_call"
);
const rememberedFunctionCallOutput = rememberedItems.find(
(item) => item && typeof item === "object" && item.type === "function_call_output"
);
assert.deepEqual(rememberedFunctionCall, {
type: "function_call",
call_id: "call_tool_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
});
assert.deepEqual(rememberedFunctionCallOutput, {
type: "function_call_output",
call_id: "call_tool_123",
output: '{"ok":true}',
});
});
test("CodexExecutor.transformRequest clones the request body before forcing stream=true", () => {
const executor = new CodexExecutor();
const body = {

View File

@@ -12,11 +12,6 @@ import {
isCodexResponsesWebSocketRequired,
parseCodexQuotaHeaders,
} from "../../open-sse/executors/codex.ts";
import {
clearRememberedResponseFunctionCallsForTesting,
rememberResponseConversationState,
rememberResponseFunctionCalls,
} from "../../open-sse/services/responsesToolCallState.ts";
import {
DEFAULT_THINKING_CONFIG,
setThinkingBudgetConfig,
@@ -42,7 +37,6 @@ function getRecord(value: unknown): Record<string, unknown> {
test.afterEach(() => {
setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG);
__setCodexWebSocketTransportForTesting(undefined);
clearRememberedResponseFunctionCallsForTesting();
});
async function withEnv<T>(entries: Record<string, string | undefined>, fn: () => T | Promise<T>) {
@@ -294,7 +288,7 @@ test("CodexExecutor.transformRequest preserves compact requests and native passt
assert.equal(result.instructions, "keep this");
});
test("CodexExecutor.transformRequest preserves store-enabled responses state when explicitly enabled", () => {
test("CodexExecutor.transformRequest preserves previous_response_id without local handling", () => {
const executor = new CodexExecutor();
const body = {
_nativeCodexPassthrough: true,
@@ -314,7 +308,7 @@ test("CodexExecutor.transformRequest preserves store-enabled responses state whe
assert.equal(result._omnirouteResponsesStore, undefined);
assert.equal(result.store, true);
assert.equal(result.previous_response_id, undefined);
assert.equal(result.previous_response_id, "resp_prev_123");
});
test("CodexExecutor.transformRequest strips store from compact requests even when store is enabled", () => {
const executor = new CodexExecutor();
@@ -340,185 +334,6 @@ test("CodexExecutor.transformRequest strips store from compact requests even whe
assert.equal(result.instructions, "keep this");
});
test("CodexExecutor.transformRequest expands remembered conversation state for stateful tool outputs", () => {
const executor = new CodexExecutor();
rememberResponseConversationState(
"resp_prev_tool_123",
[
{
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "Read README.md and summarize it.",
},
],
},
],
[
{
type: "function_call",
call_id: "call_tool_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
},
]
);
const body = {
_nativeCodexPassthrough: true,
previous_response_id: "resp_prev_tool_123",
input: [
{
type: "function_call_output",
call_id: "call_tool_123",
output: '{"ok":true}',
},
],
stream: false,
};
const result = executor.transformRequest("gpt-5.5-low", body, false, {
requestEndpointPath: "/responses",
});
assert.equal(result.previous_response_id, undefined);
assert.equal(result.store, false);
assert.equal(result.input.length, 3);
assert.deepEqual(result.input[0], {
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "Read README.md and summarize it.",
},
],
});
assert.deepEqual(result.input[1], {
type: "function_call",
call_id: "call_tool_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
});
assert.deepEqual(result.input[2], {
type: "function_call_output",
call_id: "call_tool_123",
output: '{"ok":true}',
});
});
test("CodexExecutor.transformRequest does not replay internal assistant commentary", () => {
const executor = new CodexExecutor();
rememberResponseConversationState(
"resp_prev_commentary_123",
[
{
type: "message",
role: "user",
content: [{ type: "input_text", text: "Summarize the previous result." }],
},
{
role: "assistant",
phase: "commentary",
content: [{ type: "output_text", text: "Need inspect raw tool output first." }],
},
{
type: "message",
role: "assistant",
content: [{ type: "output_text", text: "Visible assistant answer." }],
},
],
[
{
type: "function_call",
call_id: "call_safe_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
},
]
);
const body = {
_nativeCodexPassthrough: true,
previous_response_id: "resp_prev_commentary_123",
input: [
{
type: "function_call_output",
call_id: "call_safe_123",
output: '{"ok":true}',
},
],
stream: false,
};
const result = executor.transformRequest("gpt-5.5-low", body, false, {
requestEndpointPath: "/responses",
});
assert.equal(result.previous_response_id, undefined);
assert.equal(result.input.length, 4);
assert.equal(
result.input.some((item) => JSON.stringify(item).includes("Need inspect raw tool output")),
false
);
assert.equal(result.input[0].role, "user");
assert.equal(result.input[1].role, "assistant");
assert.equal(result.input[2].type, "function_call");
assert.equal(result.input[3].type, "function_call_output");
});
test("CodexExecutor.transformRequest preserves replayed assistant final_answer messages", () => {
const executor = new CodexExecutor();
rememberResponseConversationState(
"resp_prev_final_answer_123",
[
{
type: "message",
role: "user",
content: [{ type: "input_text", text: "9+10?" }],
},
{
type: "message",
role: "assistant",
phase: "final_answer",
content: [{ type: "output_text", text: "19" }],
},
],
[]
);
const result = executor.transformRequest(
"gpt-5.5-low",
{
_nativeCodexPassthrough: true,
previous_response_id: "resp_prev_final_answer_123",
input: [
{
type: "message",
role: "user",
content: [{ type: "input_text", text: "did you answered?" }],
},
],
stream: false,
},
false,
{ requestEndpointPath: "/responses" }
);
assert.equal(
result.input.some((item) => JSON.stringify(item).includes('"text":"19"')),
true
);
assert.equal(
result.input.some((item) => {
if (!item || typeof item !== "object" || Array.isArray(item)) return false;
return item.role === "assistant" && item.phase === "final_answer";
}),
true
);
});
test("CodexExecutor.transformRequest strips raw internal assistant commentary without dropping useful Responses items", () => {
const executor = new CodexExecutor();
const body = {
@@ -645,16 +460,8 @@ test("CodexExecutor.transformRequest strips internal assistant commentary before
assert.equal(result.messages, undefined);
});
test("CodexExecutor.transformRequest rehydrates missing function_call items for stateful tool outputs", () => {
test("CodexExecutor.transformRequest does not locally replay previous_response_id tool follow-ups", () => {
const executor = new CodexExecutor();
rememberResponseFunctionCalls("resp_prev_tool_123", [
{
type: "function_call",
call_id: "call_tool_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
},
]);
const body = {
_nativeCodexPassthrough: true,
previous_response_id: "resp_prev_tool_123",
@@ -672,179 +479,15 @@ test("CodexExecutor.transformRequest rehydrates missing function_call items for
requestEndpointPath: "/responses",
});
assert.equal(result.previous_response_id, undefined);
assert.equal(result.previous_response_id, "resp_prev_tool_123");
assert.equal(result.store, false);
assert.equal(result.input.length, 1);
assert.deepEqual(result.input[0], {
type: "function_call",
call_id: "call_tool_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
});
assert.deepEqual(result.input[1], {
type: "function_call_output",
call_id: "call_tool_123",
output: '{"ok":true}',
});
});
test("CodexExecutor.transformRequest filters orphaned function_call_output items after replay repair", () => {
const executor = new CodexExecutor();
const body = {
_nativeCodexPassthrough: true,
previous_response_id: "resp_prev_orphan_123",
input: [
{
type: "function_call",
call_id: "call_valid_123",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
},
{
type: "function_call_output",
call_id: "call_valid_123",
output: '{"ok":true}',
},
{
type: "function_call_output",
call_id: "call_orphan_123",
output: '{"stale":true}',
},
],
stream: false,
};
const result = executor.transformRequest("gpt-5.5-low", body, false, {
requestEndpointPath: "/responses",
});
assert.equal(result.previous_response_id, undefined);
assert.equal(result.input.filter((item) => item.type === "function_call_output").length, 1);
assert.equal(
result.input.find((item) => item.type === "function_call_output")?.call_id,
"call_valid_123"
);
});
test("CodexExecutor.transformRequest filters orphaned function_call items after replay repair", () => {
const executor = new CodexExecutor();
const body = {
_nativeCodexPassthrough: true,
previous_response_id: "resp_prev_orphan_call_123",
input: [
{
type: "function_call",
call_id: "call_valid_456",
name: "workspace_read_file",
arguments: '{"path":"README.md"}',
},
{
type: "function_call_output",
call_id: "call_valid_456",
output: '{"ok":true}',
},
{
type: "function_call",
call_id: "call_orphan_456",
name: "workspace_search",
arguments: '{"query":"Authorization"}',
},
],
stream: false,
};
const result = executor.transformRequest("gpt-5.5-low", body, false, {
requestEndpointPath: "/responses",
});
assert.equal(result.previous_response_id, undefined);
assert.equal(result.input.filter((item) => item.type === "function_call").length, 1);
assert.equal(
result.input.find((item) => item.type === "function_call")?.call_id,
"call_valid_456"
);
});
test("CodexExecutor.transformRequest synthesizes a recovery message when orphan cleanup empties input", () => {
const executor = new CodexExecutor();
const body = {
_nativeCodexPassthrough: true,
conversation_id: "conv_empty_after_cleanup",
session_id: "sess_empty_after_cleanup",
previous_response_id: "resp_prev_empty_after_cleanup",
input: [
{
type: "function_call",
call_id: "call_orphan_only_1",
name: "workspace_search",
arguments: '{"query":"auth"}',
},
{
type: "function_call_output",
call_id: "call_orphan_only_2",
output: '{"matches":1}',
},
],
stream: false,
};
const result = executor.transformRequest("gpt-5.5-low", body, false, {
requestEndpointPath: "/responses",
});
assert.equal(result.previous_response_id, undefined);
assert.equal(result.conversation_id, undefined);
assert.equal(result.session_id, undefined);
assert.equal(result.prompt_cache_key, "sess_empty_after_cleanup");
assert.equal(Array.isArray(result.input), true);
assert.equal(result.input.length, 1);
assert.deepEqual(result.input[0].type, "message");
assert.deepEqual(result.input[0].role, "user");
assert.match(result.input[0].content[0].text, /Recovered tool context from the previous turn/);
assert.match(result.input[0].content[0].text, /call_orphan_only_1/);
assert.match(result.input[0].content[0].text, /call_orphan_only_2/);
});
test("CodexExecutor.transformRequest repairs orphan function_call_output from global remembered call_id cache", () => {
const executor = new CodexExecutor();
rememberResponseFunctionCalls("resp_older_tool_123", [
{
type: "function_call",
call_id: "call_old_123",
name: "workspace_search",
arguments: '{"query":"Authorization"}',
},
]);
const body = {
_nativeCodexPassthrough: true,
previous_response_id: "resp_prev_without_that_call",
input: [
{
type: "function_call_output",
call_id: "call_old_123",
output: '{"matches":1}',
},
],
stream: false,
};
const result = executor.transformRequest("gpt-5.5-low", body, false, {
requestEndpointPath: "/responses",
});
assert.equal(result.previous_response_id, undefined);
assert.deepEqual(result.input[0], {
type: "function_call",
call_id: "call_old_123",
name: "workspace_search",
arguments: '{"query":"Authorization"}',
});
assert.deepEqual(result.input[1], {
type: "function_call_output",
call_id: "call_old_123",
output: '{"matches":1}',
});
});
test("CodexExecutor.transformRequest applies per-connection reasoning and service tier defaults", () => {
const executor = new CodexExecutor();
const result = executor.transformRequest(