From 5f0e037dae6714295befaccb9811138e2bda1719 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 19 May 2026 12:33:22 +0900 Subject: [PATCH] fix(plugin): cover Anthropic-family prefill guard Include Vertex Anthropic in the assistant-tail prefill guard after Cubic flagged the strict provider check. Keep the non-Anthropic opencode negative control covered by tests. Plan: plans/fix-anthropic-assistant-prefill-tail.md --- src/plugin/messages-transform.test.ts | 41 +++++++++++++++++++++++++++ src/plugin/messages-transform.ts | 7 +++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/plugin/messages-transform.test.ts b/src/plugin/messages-transform.test.ts index 06eeb7005..02f6951dd 100644 --- a/src/plugin/messages-transform.test.ts +++ b/src/plugin/messages-transform.test.ts @@ -328,6 +328,47 @@ describe("createMessagesTransformHandler", () => { }) }) + it("#given an Anthropic-family provider history ends with a rejecting assistant tail #when messages transform runs #then it appends a synthetic user recovery turn", async () => { + //#given + const messages: TestMessage[] = [ + { + info: { + id: "msg_user_vertex_anthropic", + role: "user", + sessionID: "ses_vertex_anthropic", + agent: "sisyphus", + model: { providerID: "google-vertex-anthropic", modelID: "claude-opus-4-7" }, + }, + parts: [{ type: "text", text: "continue" }], + }, + { + info: { + id: "msg_assistant_vertex_anthropic", + role: "assistant", + sessionID: "ses_vertex_anthropic", + }, + parts: [{ type: "text", text: "done" }], + }, + ] + + //#when + await runHandler(makeHooks({}), messages) + + //#then + expect(messages).toHaveLength(3) + expect(messages.at(-1)?.info).toMatchObject({ + role: "user", + sessionID: "ses_vertex_anthropic", + agent: "sisyphus", + model: { providerID: "google-vertex-anthropic", modelID: "claude-opus-4-7" }, + }) + expect(messages.at(-1)?.parts[0]).toMatchObject({ + type: "text", + text: "[internal] Continue from the previous assistant state.", + synthetic: true, + }) + }) + it("#given rejecting model metadata uses direct provider and model fields #when messages transform runs #then it appends a synthetic user recovery turn", async () => { //#given const messages: TestMessage[] = [ diff --git a/src/plugin/messages-transform.ts b/src/plugin/messages-transform.ts index 8c455f053..d49d53f44 100644 --- a/src/plugin/messages-transform.ts +++ b/src/plugin/messages-transform.ts @@ -5,7 +5,10 @@ import { normalizeModelID } from "../shared/model-normalization" import type { CreatedHooks } from "../create-hooks" const ASSISTANT_PREFILL_RECOVERY_TEXT = "[internal] Continue from the previous assistant state." -const ASSISTANT_PREFILL_UNSUPPORTED_PROVIDER = "anthropic" +const ASSISTANT_PREFILL_UNSUPPORTED_PROVIDERS = new Set([ + "anthropic", + "google-vertex-anthropic", +]) const ASSISTANT_PREFILL_UNSUPPORTED_MODEL_PREFIXES = [ "claude-opus-4-7", "claude-opus-4-6", @@ -94,7 +97,7 @@ function shouldRepairAssistantPrefillForModel(model: ModelIdentifier | undefined } const providerID = model.providerID.toLowerCase() - if (providerID !== ASSISTANT_PREFILL_UNSUPPORTED_PROVIDER) { + if (!ASSISTANT_PREFILL_UNSUPPORTED_PROVIDERS.has(providerID)) { return false }