Skip to content

Commit 18985e3

Browse files
committed
agents: drop Anthropic thinking blocks on replay
1 parent 212afb6 commit 18985e3

File tree

4 files changed

+27
-17
lines changed

4 files changed

+27
-17
lines changed

src/agents/pi-embedded-runner.sanitize-session-history.test.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ describe("sanitizeSessionHistory", () => {
5252
sessionId: TEST_SESSION_ID,
5353
});
5454

55+
const sanitizeAnthropicHistory = async (params: {
56+
messages: AgentMessage[];
57+
provider?: string;
58+
modelId?: string;
59+
}) =>
60+
sanitizeSessionHistory({
61+
messages: params.messages,
62+
modelApi: "anthropic-messages",
63+
provider: params.provider ?? "anthropic",
64+
modelId: params.modelId ?? "claude-opus-4-6",
65+
sessionManager: makeMockSessionManager(),
66+
sessionId: TEST_SESSION_ID,
67+
});
68+
5569
const getAssistantMessage = (messages: AgentMessage[]) => {
5670
expect(messages[1]?.role).toBe("assistant");
5771
return messages[1] as Extract<AgentMessage, { role: "assistant" }>;
@@ -776,22 +790,15 @@ describe("sanitizeSessionHistory", () => {
776790
expect(types).not.toContain("thinking");
777791
});
778792

779-
it("does not drop thinking blocks for non-copilot providers", async () => {
793+
it("drops assistant thinking blocks for anthropic replay", async () => {
780794
setNonGoogleModelApi();
781795

782796
const messages = makeThinkingAndTextAssistantMessages();
783797

784-
const result = await sanitizeSessionHistory({
785-
messages,
786-
modelApi: "anthropic-messages",
787-
provider: "anthropic",
788-
modelId: "claude-opus-4-6",
789-
sessionManager: makeMockSessionManager(),
790-
sessionId: TEST_SESSION_ID,
791-
});
798+
const result = await sanitizeAnthropicHistory({ messages });
792799

793-
const types = getAssistantContentTypes(result);
794-
expect(types).toContain("thinking");
800+
const assistant = getAssistantMessage(result);
801+
expect(assistant.content).toEqual([{ type: "text", text: "hi" }]);
795802
});
796803

797804
it("does not drop thinking blocks for non-claude copilot models", async () => {

src/agents/pi-embedded-runner/run/attempt.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1944,9 +1944,10 @@ export async function runEmbeddedAttempt(
19441944
activeSession.agent.streamFn = cacheTrace.wrapStreamFn(activeSession.agent.streamFn);
19451945
}
19461946

1947-
// Copilot/Claude can reject persisted `thinking` blocks (e.g. thinkingSignature:"reasoning_text")
1948-
// on *any* follow-up provider call (including tool continuations). Wrap the stream function
1949-
// so every outbound request sees sanitized messages.
1947+
// Anthropic Claude endpoints can reject replayed `thinking` blocks
1948+
// (e.g. thinkingSignature:"reasoning_text") on any follow-up provider
1949+
// call, including tool continuations. Wrap the stream function so every
1950+
// outbound request sees sanitized messages.
19501951
if (transcriptPolicy.dropThinkingBlocks) {
19511952
const inner = activeSession.agent.streamFn;
19521953
activeSession.agent.streamFn = (model, context, options) => {

src/agents/provider-capabilities.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ const DEFAULT_PROVIDER_CAPABILITIES: ProviderCapabilities = {
2929
const PROVIDER_CAPABILITIES: Record<string, Partial<ProviderCapabilities>> = {
3030
anthropic: {
3131
providerFamily: "anthropic",
32+
dropThinkingBlockModelHints: ["claude"],
3233
},
3334
"amazon-bedrock": {
3435
providerFamily: "anthropic",
36+
dropThinkingBlockModelHints: ["claude"],
3537
},
3638
// kimi-coding natively supports Anthropic tool framing (input_schema);
3739
// converting to OpenAI format causes XML text fallback instead of tool_use blocks.

src/agents/transcript-policy.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ export function resolveTranscriptPolicy(params: {
8080
});
8181
const requiresOpenAiCompatibleToolIdSanitization = params.modelApi === "openai-completions";
8282

83-
// GitHub Copilot's Claude endpoints can reject persisted `thinking` blocks with
84-
// non-binary/non-base64 signatures (e.g. thinkingSignature: "reasoning_text").
85-
// Drop these blocks at send-time to keep sessions usable.
83+
// Anthropic Claude endpoints can reject replayed `thinking` blocks unless the
84+
// original signatures are preserved byte-for-byte. Drop them at send-time to
85+
// keep persisted sessions usable across follow-up turns.
8686
const dropThinkingBlocks = shouldDropThinkingBlocksForModel({ provider, modelId });
8787

8888
const needsNonImageSanitize =

0 commit comments

Comments
 (0)