|
|
|
|
@@ -25,6 +25,10 @@ const state = vi.hoisted(() => ({
|
|
|
|
|
isCliProviderMock: vi.fn((_: unknown) => false),
|
|
|
|
|
isInternalMessageChannelMock: vi.fn((_: unknown) => false),
|
|
|
|
|
createBlockReplyDeliveryHandlerMock: vi.fn(),
|
|
|
|
|
isCompactionFailureErrorMock: vi.fn((_: string | undefined) => false),
|
|
|
|
|
isContextOverflowErrorMock: vi.fn((_: string | undefined) => false),
|
|
|
|
|
isLikelyContextOverflowErrorMock: vi.fn((_: string | undefined) => false),
|
|
|
|
|
updateSessionStoreMock: vi.fn(),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const GENERIC_RUN_FAILURE_TEXT =
|
|
|
|
|
@@ -87,10 +91,11 @@ vi.mock("../../agents/pi-embedded-helpers.js", () => ({
|
|
|
|
|
}
|
|
|
|
|
return undefined;
|
|
|
|
|
},
|
|
|
|
|
isCompactionFailureError: () => false,
|
|
|
|
|
isContextOverflowError: () => false,
|
|
|
|
|
isCompactionFailureError: (message?: string) => state.isCompactionFailureErrorMock(message),
|
|
|
|
|
isContextOverflowError: (message?: string) => state.isContextOverflowErrorMock(message),
|
|
|
|
|
isBillingErrorMessage: () => false,
|
|
|
|
|
isLikelyContextOverflowError: () => false,
|
|
|
|
|
isLikelyContextOverflowError: (message?: string) =>
|
|
|
|
|
state.isLikelyContextOverflowErrorMock(message),
|
|
|
|
|
isOverloadedErrorMessage: (message: string) => /overloaded|capacity/i.test(message),
|
|
|
|
|
isRateLimitErrorMessage: (message: string) =>
|
|
|
|
|
/rate.limit|too many requests|429|usage limit/i.test(message),
|
|
|
|
|
@@ -101,7 +106,7 @@ vi.mock("../../agents/pi-embedded-helpers.js", () => ({
|
|
|
|
|
vi.mock("../../config/sessions.js", () => ({
|
|
|
|
|
resolveGroupSessionKey: vi.fn(() => null),
|
|
|
|
|
resolveSessionTranscriptPath: vi.fn(),
|
|
|
|
|
updateSessionStore: vi.fn(),
|
|
|
|
|
updateSessionStore: state.updateSessionStoreMock,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
vi.mock("../../globals.js", () => ({
|
|
|
|
|
@@ -270,10 +275,13 @@ function createFollowupRun(): FollowupRun {
|
|
|
|
|
function createMockReplyOperation(): {
|
|
|
|
|
replyOperation: ReplyOperation;
|
|
|
|
|
failMock: ReturnType<typeof vi.fn>;
|
|
|
|
|
updateSessionIdMock: ReturnType<typeof vi.fn>;
|
|
|
|
|
} {
|
|
|
|
|
const failMock = vi.fn();
|
|
|
|
|
const updateSessionIdMock = vi.fn();
|
|
|
|
|
return {
|
|
|
|
|
failMock,
|
|
|
|
|
updateSessionIdMock,
|
|
|
|
|
replyOperation: {
|
|
|
|
|
key: "main",
|
|
|
|
|
sessionId: "session",
|
|
|
|
|
@@ -282,7 +290,7 @@ function createMockReplyOperation(): {
|
|
|
|
|
phase: "running",
|
|
|
|
|
result: null,
|
|
|
|
|
setPhase: vi.fn(),
|
|
|
|
|
updateSessionId: vi.fn(),
|
|
|
|
|
updateSessionId: updateSessionIdMock,
|
|
|
|
|
attachBackend: vi.fn(),
|
|
|
|
|
detachBackend: vi.fn(),
|
|
|
|
|
complete: vi.fn(),
|
|
|
|
|
@@ -389,7 +397,6 @@ function createMinimalRunAgentTurnParams(overrides?: {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set<Promise<void>>(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -410,6 +417,19 @@ describe("buildContextOverflowRecoveryText", () => {
|
|
|
|
|
expect(text).not.toContain("heartbeat model bleed");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("keeps the preserved-session copy with the existing overflow hint", () => {
|
|
|
|
|
const text = buildContextOverflowRecoveryText({
|
|
|
|
|
preserveSessionMapping: true,
|
|
|
|
|
cfg: {},
|
|
|
|
|
primaryProvider: "openrouter",
|
|
|
|
|
primaryModel: "qwen3.6-plus",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(text).toContain("kept this conversation mapped to the current session");
|
|
|
|
|
expect(text).toContain("reserveTokensFloor");
|
|
|
|
|
expect(text).not.toContain("reset our conversation");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("points to heartbeat model bleed when the last runtime model matches configured heartbeat.model", () => {
|
|
|
|
|
const text = buildContextOverflowRecoveryText({
|
|
|
|
|
cfg: {
|
|
|
|
|
@@ -593,6 +613,13 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
state.isInternalMessageChannelMock.mockReturnValue(false);
|
|
|
|
|
state.createBlockReplyDeliveryHandlerMock.mockReset();
|
|
|
|
|
state.createBlockReplyDeliveryHandlerMock.mockReturnValue(undefined);
|
|
|
|
|
state.isCompactionFailureErrorMock.mockReset();
|
|
|
|
|
state.isCompactionFailureErrorMock.mockReturnValue(false);
|
|
|
|
|
state.isContextOverflowErrorMock.mockReset();
|
|
|
|
|
state.isContextOverflowErrorMock.mockReturnValue(false);
|
|
|
|
|
state.isLikelyContextOverflowErrorMock.mockReset();
|
|
|
|
|
state.isLikelyContextOverflowErrorMock.mockReturnValue(false);
|
|
|
|
|
state.updateSessionStoreMock.mockReset();
|
|
|
|
|
state.runWithModelFallbackMock.mockImplementation(async (params: FallbackRunnerParams) => ({
|
|
|
|
|
result: await params.run("anthropic", "claude"),
|
|
|
|
|
provider: "anthropic",
|
|
|
|
|
@@ -1250,7 +1277,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1367,7 +1393,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1443,7 +1468,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1508,7 +1532,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1569,7 +1592,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1628,7 +1650,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1681,7 +1702,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1738,7 +1758,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1785,7 +1804,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -1952,7 +1970,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks,
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2004,7 +2021,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2049,7 +2065,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2338,7 +2353,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks,
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2385,7 +2399,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks,
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2431,7 +2444,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks,
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2484,7 +2496,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks,
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2715,7 +2726,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2761,7 +2771,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2826,7 +2835,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2883,7 +2891,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -2944,7 +2951,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3032,7 +3038,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks,
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3167,7 +3172,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3251,7 +3255,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3298,7 +3301,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3344,7 +3346,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3393,7 +3394,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3450,7 +3450,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3515,7 +3514,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3579,7 +3577,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3637,7 +3634,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3690,7 +3686,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3742,7 +3737,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3782,7 +3776,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3843,7 +3836,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3892,7 +3884,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3946,7 +3937,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -3993,7 +3983,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4032,7 +4021,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4137,7 +4125,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4367,7 +4354,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4383,6 +4369,95 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("preserves the active session when embedded overflow recovery fails", async () => {
|
|
|
|
|
state.isContextOverflowErrorMock.mockReturnValue(true);
|
|
|
|
|
state.runEmbeddedPiAgentMock.mockResolvedValueOnce({
|
|
|
|
|
payloads: [],
|
|
|
|
|
meta: {
|
|
|
|
|
error: {
|
|
|
|
|
message: "400 The prompt is too long: 203557, model maximum context length: 196607",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const activeSessionEntry = { sessionId: "session", updatedAt: 1 } as SessionEntry;
|
|
|
|
|
const activeSessionStore = { "agent:main:main": activeSessionEntry };
|
|
|
|
|
const { replyOperation, failMock, updateSessionIdMock } = createMockReplyOperation();
|
|
|
|
|
const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
|
|
|
|
|
const result = await runAgentTurnWithFallback({
|
|
|
|
|
...createMinimalRunAgentTurnParams({
|
|
|
|
|
sessionCtx: {
|
|
|
|
|
Provider: "webchat",
|
|
|
|
|
MessageSid: "msg",
|
|
|
|
|
} as unknown as TemplateContext,
|
|
|
|
|
}),
|
|
|
|
|
replyOperation,
|
|
|
|
|
sessionKey: "agent:main:main",
|
|
|
|
|
getActiveSessionEntry: () => activeSessionEntry,
|
|
|
|
|
activeSessionStore,
|
|
|
|
|
storePath: "/tmp/sessions.json",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(result.kind).toBe("final");
|
|
|
|
|
if (result.kind === "final") {
|
|
|
|
|
expect(result.payload.text).toContain("kept this conversation mapped to the current session");
|
|
|
|
|
expect(result.payload.text).toContain("reserveTokensFloor");
|
|
|
|
|
expectRecordFields(requireRecord(getReplyPayloadMetadata(result.payload), "reply metadata"), {
|
|
|
|
|
deliverDespiteSourceReplySuppression: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
expect(failMock).toHaveBeenCalledWith(
|
|
|
|
|
"run_failed",
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
message: "400 The prompt is too long: 203557, model maximum context length: 196607",
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
expect(activeSessionStore["agent:main:main"]?.sessionId).toBe("session");
|
|
|
|
|
expect(updateSessionIdMock).not.toHaveBeenCalled();
|
|
|
|
|
expect(state.updateSessionStoreMock).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("preserves the active session when compaction failure is thrown before reply", async () => {
|
|
|
|
|
state.isCompactionFailureErrorMock.mockReturnValue(true);
|
|
|
|
|
state.runEmbeddedPiAgentMock.mockRejectedValueOnce(
|
|
|
|
|
new Error("Auto-compaction failed: nothing to compact"),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const activeSessionEntry = { sessionId: "session", updatedAt: 1 } as SessionEntry;
|
|
|
|
|
const activeSessionStore = { "agent:main:main": activeSessionEntry };
|
|
|
|
|
const { replyOperation, failMock, updateSessionIdMock } = createMockReplyOperation();
|
|
|
|
|
const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
|
|
|
|
|
const result = await runAgentTurnWithFallback({
|
|
|
|
|
...createMinimalRunAgentTurnParams({
|
|
|
|
|
sessionCtx: {
|
|
|
|
|
Provider: "webchat",
|
|
|
|
|
MessageSid: "msg",
|
|
|
|
|
} as unknown as TemplateContext,
|
|
|
|
|
}),
|
|
|
|
|
replyOperation,
|
|
|
|
|
sessionKey: "agent:main:main",
|
|
|
|
|
getActiveSessionEntry: () => activeSessionEntry,
|
|
|
|
|
activeSessionStore,
|
|
|
|
|
storePath: "/tmp/sessions.json",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(result.kind).toBe("final");
|
|
|
|
|
if (result.kind === "final") {
|
|
|
|
|
expect(result.payload.text).toContain("kept this conversation mapped to the current session");
|
|
|
|
|
expect(result.payload.text).toContain("reserveTokensFloor");
|
|
|
|
|
expectRecordFields(requireRecord(getReplyPayloadMetadata(result.payload), "reply metadata"), {
|
|
|
|
|
deliverDespiteSourceReplySuppression: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
expect(failMock).toHaveBeenCalledWith(
|
|
|
|
|
"run_failed",
|
|
|
|
|
expect.objectContaining({ message: "Auto-compaction failed: nothing to compact" }),
|
|
|
|
|
);
|
|
|
|
|
expect(activeSessionStore["agent:main:main"]?.sessionId).toBe("session");
|
|
|
|
|
expect(updateSessionIdMock).not.toHaveBeenCalled();
|
|
|
|
|
expect(state.updateSessionStoreMock).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("surfaces gateway reauth guidance for known OAuth refresh failures", async () => {
|
|
|
|
|
state.runEmbeddedPiAgentMock.mockRejectedValueOnce(
|
|
|
|
|
new Error(
|
|
|
|
|
@@ -4407,7 +4482,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4447,7 +4521,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4485,7 +4558,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4525,7 +4597,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4565,7 +4636,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4601,7 +4671,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4636,7 +4705,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4675,7 +4743,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4742,7 +4809,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4797,7 +4863,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4882,7 +4947,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -4940,7 +5004,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -5003,7 +5066,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -5072,7 +5134,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -5143,7 +5204,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -5207,7 +5267,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
@@ -5274,7 +5333,6 @@ describe("runAgentTurnWithFallback", () => {
|
|
|
|
|
shouldEmitToolResult: () => true,
|
|
|
|
|
shouldEmitToolOutput: () => false,
|
|
|
|
|
pendingToolTasks: new Set(),
|
|
|
|
|
resetSessionAfterCompactionFailure: async () => false,
|
|
|
|
|
resetSessionAfterRoleOrderingConflict: async () => false,
|
|
|
|
|
isHeartbeat: false,
|
|
|
|
|
sessionKey: "main",
|
|
|
|
|
|