diff --git a/src/agents/pi-embedded-helpers/bootstrap.ts b/src/agents/pi-embedded-helpers/bootstrap.ts index 51d728ba422..bca9f64107b 100644 --- a/src/agents/pi-embedded-helpers/bootstrap.ts +++ b/src/agents/pi-embedded-helpers/bootstrap.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import type { AgentMessage } from "@mariozechner/pi-agent-core"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; +import { sanitizeGoogleAssistantFirstOrdering } from "../../shared/google-turn-ordering.js"; import { normalizeOptionalString } from "../../shared/string-coerce.js"; import { truncateUtf16Safe } from "../../utils.js"; import type { WorkspaceBootstrapFile } from "../workspace.js"; @@ -261,28 +262,5 @@ export function buildBootstrapContextFiles( } export function sanitizeGoogleTurnOrdering(messages: AgentMessage[]): AgentMessage[] { - const GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT = "(session bootstrap)"; - const first = messages[0] as { role?: unknown; content?: unknown } | undefined; - const role = first?.role; - const content = first?.content; - if ( - role === "user" && - typeof content === "string" && - content.trim() === GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT - ) { - return messages; - } - if (role !== "assistant") { - return messages; - } - - // Cloud Code Assist rejects histories that begin with a model turn (tool call or text). - // Prepend a tiny synthetic user turn so the rest of the transcript can be used. - const bootstrap: AgentMessage = { - role: "user", - content: GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT, - timestamp: Date.now(), - } as AgentMessage; - - return [bootstrap, ...messages]; + return sanitizeGoogleAssistantFirstOrdering(messages); } diff --git a/src/plugins/provider-replay-helpers.ts b/src/plugins/provider-replay-helpers.ts index 9e28fdfdcde..134a19318a4 100644 --- a/src/plugins/provider-replay-helpers.ts +++ b/src/plugins/provider-replay-helpers.ts @@ -1,4 +1,5 @@ import type { AgentMessage } from "@mariozechner/pi-agent-core"; +import { sanitizeGoogleAssistantFirstOrdering } from "../shared/google-turn-ordering.js"; import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; import type { ProviderReasoningOutputMode, @@ -130,31 +131,6 @@ export function buildHybridAnthropicOrOpenAIReplayPolicy( } const GOOGLE_TURN_ORDERING_CUSTOM_TYPE = "google-turn-ordering-bootstrap"; -const GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT = "(session bootstrap)"; - -function sanitizeGoogleAssistantFirstOrdering(messages: AgentMessage[]): AgentMessage[] { - const first = messages[0] as { role?: unknown; content?: unknown } | undefined; - const role = first?.role; - const content = first?.content; - if ( - role === "user" && - typeof content === "string" && - content.trim() === GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT - ) { - return messages; - } - if (role !== "assistant") { - return messages; - } - - const bootstrap: AgentMessage = { - role: "user", - content: GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT, - timestamp: Date.now(), - } as AgentMessage; - - return [bootstrap, ...messages]; -} function hasGoogleTurnOrderingMarker(sessionState: ProviderReplaySessionState): boolean { return sessionState diff --git a/src/shared/google-turn-ordering.ts b/src/shared/google-turn-ordering.ts new file mode 100644 index 00000000000..3aacbc1af4a --- /dev/null +++ b/src/shared/google-turn-ordering.ts @@ -0,0 +1,27 @@ +import type { AgentMessage } from "@mariozechner/pi-agent-core"; + +export const GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT = "(session bootstrap)"; + +export function sanitizeGoogleAssistantFirstOrdering(messages: AgentMessage[]): AgentMessage[] { + const first = messages[0] as { role?: unknown; content?: unknown } | undefined; + const role = first?.role; + const content = first?.content; + if ( + role === "user" && + typeof content === "string" && + content.trim() === GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT + ) { + return messages; + } + if (role !== "assistant") { + return messages; + } + + const bootstrap: AgentMessage = { + role: "user", + content: GOOGLE_TURN_ORDER_BOOTSTRAP_TEXT, + timestamp: Date.now(), + } as AgentMessage; + + return [bootstrap, ...messages]; +}