Skip to content

Commit 9061d1e

Browse files
clawsweeper[bot]openclaw-clownfish[bot]vincentkoc
authored
fix(agents): preserve string user content when merging turns
Co-authored-by: openclaw-clawsweeper[bot] <280122609+openclaw-clawsweeper[bot]@users.noreply.github.com> Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
1 parent e20147a commit 9061d1e

3 files changed

Lines changed: 38 additions & 12 deletions

File tree

src/agents/pi-embedded-helpers.validate-turns.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,26 @@ describe("mergeConsecutiveUserTurns", () => {
357357
expect(merged.timestamp).toBe(2000);
358358
});
359359

360+
it("preserves string content while merging content", () => {
361+
const previous = {
362+
role: "user",
363+
content: "before",
364+
timestamp: 1000,
365+
} as Extract<AgentMessage, { role: "user" }>;
366+
const current = {
367+
role: "user",
368+
content: "after",
369+
timestamp: 2000,
370+
} as Extract<AgentMessage, { role: "user" }>;
371+
372+
const merged = mergeConsecutiveUserTurns(previous, current);
373+
374+
expect(merged.content).toEqual([
375+
{ type: "text", text: "before" },
376+
{ type: "text", text: "after" },
377+
]);
378+
});
379+
360380
it("backfills timestamp from earlier message when missing", () => {
361381
const previous = {
362382
role: "user",

src/agents/pi-embedded-helpers/turns.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ type AnthropicContentBlock = {
1010
toolUseId?: string;
1111
toolCallId?: string;
1212
};
13+
type UserContentBlock = Extract<
14+
Extract<AgentMessage, { role: "user" }>["content"],
15+
readonly unknown[]
16+
>[number];
1317

1418
function isToolCallBlock(block: AnthropicContentBlock): boolean {
1519
return block.type === "toolUse" || block.type === "toolCall" || block.type === "functionCall";
@@ -350,8 +354,8 @@ export function mergeConsecutiveUserTurns(
350354
current: Extract<AgentMessage, { role: "user" }>,
351355
): Extract<AgentMessage, { role: "user" }> {
352356
const mergedContent = [
353-
...(Array.isArray(previous.content) ? previous.content : []),
354-
...(Array.isArray(current.content) ? current.content : []),
357+
...normalizeUserContentForMerge(previous.content),
358+
...normalizeUserContentForMerge(current.content),
355359
];
356360

357361
return {
@@ -361,6 +365,16 @@ export function mergeConsecutiveUserTurns(
361365
};
362366
}
363367

368+
function normalizeUserContentForMerge(content: unknown): UserContentBlock[] {
369+
if (Array.isArray(content)) {
370+
return content as UserContentBlock[];
371+
}
372+
if (typeof content === "string") {
373+
return [{ type: "text", text: content }];
374+
}
375+
return [];
376+
}
377+
364378
/**
365379
* Validates and fixes conversation turn sequences for Anthropic API.
366380
* Anthropic requires strict alternating user→assistant pattern.

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

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,17 +1141,9 @@ describe("sanitizeSessionHistory", () => {
11411141
"```",
11421142
].join("\n");
11431143
const messages = castAgentMessages([
1144-
{
1145-
role: "user",
1146-
content: [{ type: "text", text: "First" }],
1147-
timestamp: nextTimestamp(),
1148-
},
1144+
makeUserMessage("First"),
11491145
makeAssistantMessage([{ type: "text", text: metadataOnlyText }]),
1150-
{
1151-
role: "user",
1152-
content: [{ type: "text", text: "Second" }],
1153-
timestamp: nextTimestamp(),
1154-
},
1146+
makeUserMessage("Second"),
11551147
]);
11561148

11571149
const sanitized = await sanitizeSessionHistory({

0 commit comments

Comments
 (0)