Merge f9c98ca045 into 09be5d45d5
This commit is contained in:
commit
2c9c3c87df
@ -305,6 +305,62 @@ export function applyGoogleTurnOrderingFix(params: {
|
|||||||
return { messages: sanitized, didPrepend };
|
return { messages: sanitized, didPrepend };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes tool call content blocks in assistant messages so they always use
|
||||||
|
* the canonical shape: `{ type: "toolCall", arguments: ... }`.
|
||||||
|
*
|
||||||
|
* Different providers persist tool calls in different shapes:
|
||||||
|
* - Anthropic / pi-ai: `{ type: "toolCall", arguments: {} }`
|
||||||
|
* - Google Antigravity: `{ type: "toolUse", input: {} }`
|
||||||
|
*
|
||||||
|
* When a user switches providers mid-session the old shape can reach the new
|
||||||
|
* provider's serializer unchanged, causing validation failures (e.g. Anthropic
|
||||||
|
* rejects requests when the `input` field is missing on a `tool_use` block).
|
||||||
|
*/
|
||||||
|
function normalizeToolCallBlocks(messages: AgentMessage[]): AgentMessage[] {
|
||||||
|
let changed = false;
|
||||||
|
const out: AgentMessage[] = [];
|
||||||
|
|
||||||
|
for (const msg of messages) {
|
||||||
|
if (!msg || typeof msg !== "object" || (msg as { role?: unknown }).role !== "assistant") {
|
||||||
|
out.push(msg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const assistant = msg as Extract<AgentMessage, { role: "assistant" }>;
|
||||||
|
const content = assistant.content;
|
||||||
|
if (!Array.isArray(content)) {
|
||||||
|
out.push(msg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentChanged = false;
|
||||||
|
const nextContent = content.map((block) => {
|
||||||
|
if (!block || typeof block !== "object") return block;
|
||||||
|
const rec = block as unknown as Record<string, unknown>;
|
||||||
|
const type = rec.type;
|
||||||
|
|
||||||
|
if (type !== "toolCall" && type !== "toolUse" && type !== "functionCall") return block;
|
||||||
|
|
||||||
|
// Ensure `arguments` is populated — prefer existing `arguments`, fall back to `input`, default to {}.
|
||||||
|
const args = rec.arguments ?? rec.input ?? {};
|
||||||
|
if (type === "toolCall" && rec.arguments !== undefined) return block; // already canonical
|
||||||
|
|
||||||
|
contentChanged = true;
|
||||||
|
return { ...rec, type: "toolCall", arguments: args };
|
||||||
|
});
|
||||||
|
|
||||||
|
if (contentChanged) {
|
||||||
|
changed = true;
|
||||||
|
out.push({ ...assistant, content: nextContent } as typeof assistant);
|
||||||
|
} else {
|
||||||
|
out.push(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed ? out : messages;
|
||||||
|
}
|
||||||
|
|
||||||
export async function sanitizeSessionHistory(params: {
|
export async function sanitizeSessionHistory(params: {
|
||||||
messages: AgentMessage[];
|
messages: AgentMessage[];
|
||||||
modelApi?: string | null;
|
modelApi?: string | null;
|
||||||
@ -336,6 +392,12 @@ export async function sanitizeSessionHistory(params: {
|
|||||||
? sanitizeToolUseResultPairing(sanitizedThinking)
|
? sanitizeToolUseResultPairing(sanitizedThinking)
|
||||||
: sanitizedThinking;
|
: sanitizedThinking;
|
||||||
|
|
||||||
|
// Normalize tool call content blocks so every provider serializer sees a consistent shape.
|
||||||
|
// Session history may contain blocks from different providers (e.g. Google Antigravity uses
|
||||||
|
// { type: "toolUse", input: {} } while Anthropic expects { type: "toolCall", arguments: {} }).
|
||||||
|
// Without this, switching providers mid-session causes "input: Field required" API rejections.
|
||||||
|
const normalizedTools = normalizeToolCallBlocks(repairedTools);
|
||||||
|
|
||||||
const isOpenAIResponsesApi =
|
const isOpenAIResponsesApi =
|
||||||
params.modelApi === "openai-responses" || params.modelApi === "openai-codex-responses";
|
params.modelApi === "openai-responses" || params.modelApi === "openai-codex-responses";
|
||||||
const hasSnapshot = Boolean(params.provider || params.modelApi || params.modelId);
|
const hasSnapshot = Boolean(params.provider || params.modelApi || params.modelId);
|
||||||
@ -350,8 +412,8 @@ export async function sanitizeSessionHistory(params: {
|
|||||||
: false;
|
: false;
|
||||||
const sanitizedOpenAI =
|
const sanitizedOpenAI =
|
||||||
isOpenAIResponsesApi && modelChanged
|
isOpenAIResponsesApi && modelChanged
|
||||||
? downgradeOpenAIReasoningBlocks(repairedTools)
|
? downgradeOpenAIReasoningBlocks(normalizedTools)
|
||||||
: repairedTools;
|
: normalizedTools;
|
||||||
|
|
||||||
if (hasSnapshot && (!priorSnapshot || modelChanged)) {
|
if (hasSnapshot && (!priorSnapshot || modelChanged)) {
|
||||||
appendModelSnapshot(params.sessionManager, {
|
appendModelSnapshot(params.sessionManager, {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user