Skip to content

Commit 36f648d

Browse files
committed
fix: preserve pi assistant fallback context
1 parent e05db0c commit 36f648d

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

src/agents/pi-embedded-runner/run.cross-provider-fallback-error-context.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,53 @@ describe("runEmbeddedPiAgent cross-provider fallback error handling", () => {
174174
});
175175
});
176176

177+
it("keeps PI-stamped session assistant errors for the current candidate after compaction", async () => {
178+
const getLastFormattedAssistant = captureFormattedAssistant();
179+
const sameCandidateErrorMessage = "429 current PI-stamped candidate rate limit";
180+
mockedIsFailoverAssistantError.mockImplementation((...args: unknown[]) => {
181+
const assistant = args[0];
182+
return isCurrentAttemptAssistant(assistant) && assistant.provider === "pi";
183+
});
184+
mockedIsRateLimitAssistantError.mockImplementation((...args: unknown[]) => {
185+
const assistant = args[0];
186+
return isCurrentAttemptAssistant(assistant) && assistant.provider === "pi";
187+
});
188+
mockedRunEmbeddedAttempt.mockResolvedValueOnce(
189+
makeAttemptResult({
190+
assistantTexts: [],
191+
lastAssistant: makeAssistantMessageFixture({
192+
stopReason: "error",
193+
errorMessage: sameCandidateErrorMessage,
194+
provider: "pi",
195+
model: "pi",
196+
content: [],
197+
}),
198+
currentAttemptAssistant: undefined,
199+
}),
200+
);
201+
202+
const promise = runEmbeddedPiAgent({
203+
...overflowBaseRunParams,
204+
runId: "run-compaction-pi-stamped-fallback-error-context",
205+
config: makeCrossProviderFallbackConfig(),
206+
});
207+
208+
await expect(promise).rejects.toBeInstanceOf(MockedFailoverError);
209+
await expect(promise).rejects.toThrow(sameCandidateErrorMessage);
210+
expect(mockedIsRateLimitAssistantError).toHaveBeenCalledTimes(1);
211+
const rateLimitCalls = mockedIsRateLimitAssistantError.mock.calls as unknown[][];
212+
expect(rateLimitCalls.at(-1)?.[0]).toMatchObject({
213+
provider: "pi",
214+
model: "pi",
215+
errorMessage: sameCandidateErrorMessage,
216+
});
217+
expect(getLastFormattedAssistant()).toMatchObject({
218+
provider: "pi",
219+
model: "pi",
220+
errorMessage: sameCandidateErrorMessage,
221+
});
222+
});
223+
177224
it("does not reuse a prior provider session assistant when the current candidate times out", async () => {
178225
const getLastFormattedAssistant = captureFormattedAssistant();
179226
mockedRunEmbeddedAttempt.mockResolvedValueOnce(

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,14 @@ export function isAssistantForModelRef(
105105
assistant: { provider?: string; model?: string } | undefined,
106106
ref: { provider: string; model: string },
107107
): boolean {
108-
return assistant?.provider?.trim() === ref.provider && assistant?.model?.trim() === ref.model;
108+
if (!assistant) {
109+
return false;
110+
}
111+
const resolved = resolveReportedModelRef({
112+
...ref,
113+
assistant,
114+
});
115+
return resolved.provider === ref.provider && resolved.model === ref.model;
109116
}
110117

111118
function isEmbeddedHarnessProvider(provider: string): boolean {

0 commit comments

Comments
 (0)