Skip to content

Commit 19614bd

Browse files
committed
fix: tighten transcript repair matching
1 parent 964a6f4 commit 19614bd

2 files changed

Lines changed: 63 additions & 6 deletions

File tree

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

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -699,13 +699,13 @@ describe("ensureVisibleAssistantTextInSessionTranscript", () => {
699699
expect(activeSession.agent.state.messages).toBe(after);
700700
});
701701

702-
it("concatenates delivered chunks only when no canonical assistant text exists", () => {
702+
it("preserves delivered block separators only when no canonical assistant text exists", () => {
703703
const before = [{ role: "user", content: "hello" }] as unknown as AgentMessage[];
704704
const after = [
705705
...before,
706706
{
707707
role: "assistant",
708-
content: [{ type: "text", text: "abcdef" }],
708+
content: [{ type: "text", text: "First paragraph.\n\nSecond paragraph." }],
709709
},
710710
] as unknown as AgentMessage[];
711711
const activeSession = { agent: { state: { messages: before } } };
@@ -722,7 +722,7 @@ describe("ensureVisibleAssistantTextInSessionTranscript", () => {
722722
const repaired = ensureVisibleAssistantTextInSessionTranscript({
723723
activeSession,
724724
sessionManager,
725-
assistantTexts: ["abc", "def"],
725+
assistantTexts: ["First paragraph.", "Second paragraph."],
726726
provider: "openai",
727727
modelId: "gpt-5.5",
728728
modelApi: "responses",
@@ -733,11 +733,62 @@ describe("ensureVisibleAssistantTextInSessionTranscript", () => {
733733
expect(repaired).toBe(true);
734734
expect(appendMessage).toHaveBeenCalledWith(
735735
expect.objectContaining({
736-
content: [{ type: "text", text: "abcdef" }],
736+
content: [{ type: "text", text: "First paragraph.\n\nSecond paragraph." }],
737737
}),
738738
);
739739
});
740740

741+
it("limits duplicate detection to the current turn when repairing", () => {
742+
const before = [
743+
{
744+
role: "assistant",
745+
content: [{ type: "text", text: "Repeated answer" }],
746+
},
747+
{
748+
role: "user",
749+
content: [{ type: "text", text: "ask again" }],
750+
},
751+
] as unknown as AgentMessage[];
752+
const after = [
753+
...before,
754+
{
755+
role: "assistant",
756+
content: [{ type: "text", text: "Repeated answer" }],
757+
},
758+
] as unknown as AgentMessage[];
759+
const activeSession = { agent: { state: { messages: before } } };
760+
const appendMessage = vi.fn();
761+
const sessionManager = {
762+
buildSessionContext: vi.fn().mockReturnValueOnce({ messages: before }).mockReturnValueOnce({
763+
messages: after,
764+
}),
765+
appendMessage,
766+
} as unknown as Parameters<
767+
typeof ensureVisibleAssistantTextInSessionTranscript
768+
>[0]["sessionManager"];
769+
770+
const repaired = ensureVisibleAssistantTextInSessionTranscript({
771+
activeSession,
772+
sessionManager,
773+
visibleText: "Repeated answer",
774+
assistantTexts: ["Repeated answer"],
775+
currentTurnStartIndex: 1,
776+
provider: "openai",
777+
modelId: "gpt-5.5",
778+
modelApi: "responses",
779+
runId: "run-repeated-answer",
780+
now: 5,
781+
});
782+
783+
expect(repaired).toBe(true);
784+
expect(appendMessage).toHaveBeenCalledWith(
785+
expect.objectContaining({
786+
content: [{ type: "text", text: "Repeated answer" }],
787+
}),
788+
);
789+
expect(activeSession.agent.state.messages).toBe(after);
790+
});
791+
741792
it("does not fall back to delivered chunks when canonical assistant text is empty", () => {
742793
const messages = [{ role: "user", content: "hello" }] as unknown as AgentMessage[];
743794
const activeSession = { agent: { state: { messages } } };

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ export function ensureVisibleAssistantTextInSessionTranscript(params: {
611611
>;
612612
visibleText?: string;
613613
assistantTexts: readonly string[];
614+
currentTurnStartIndex?: number;
614615
provider: string;
615616
modelId: string;
616617
modelApi?: string;
@@ -623,15 +624,19 @@ export function ensureVisibleAssistantTextInSessionTranscript(params: {
623624
params.assistantTexts
624625
.map((text) => text.trim())
625626
.filter(Boolean)
626-
.join("")
627+
.join("\n\n")
627628
.trim();
628629
const normalizedVisibleText = normalizeAssistantTranscriptCompareText(visibleText);
629630
if (!normalizedVisibleText) {
630631
return false;
631632
}
632633

633634
const context = params.sessionManager.buildSessionContext();
634-
const alreadyPresent = context.messages.some((message) => {
635+
const currentTurnMessages =
636+
typeof params.currentTurnStartIndex === "number"
637+
? context.messages.slice(Math.max(0, params.currentTurnStartIndex))
638+
: context.messages;
639+
const alreadyPresent = currentTurnMessages.some((message) => {
635640
if (message.role !== "assistant") {
636641
return false;
637642
}
@@ -3323,6 +3328,7 @@ export async function runEmbeddedAttempt(
33233328
? (extractAssistantVisibleText(currentAttemptAssistant) ?? "")
33243329
: undefined,
33253330
assistantTexts,
3331+
currentTurnStartIndex: prePromptMessageCount,
33263332
provider: params.provider,
33273333
modelId: params.modelId,
33283334
modelApi: params.model.api,

0 commit comments

Comments
 (0)