























@@ -5066,4 +5066,120 @@ describe("runAgentTurnWithFallback", () => {
50665066modelOverrideFallbackOriginModel: "claude-opus",
50675067});
50685068});
5069+5070+it("latches assistant error stub suppression across main reply fallback candidates", async () => {
5071+state.runWithModelFallbackMock.mockImplementationOnce(async (params: FallbackRunnerParams) => {
5072+await params.run("anthropic", "claude-opus-4-7").catch(() => undefined);
5073+await params.run("anthropic", "claude-opus-4-6").catch(() => undefined);
5074+return {
5075+result: await params.run("openai", "gpt-5.4"),
5076+provider: "openai",
5077+model: "gpt-5.4",
5078+attempts: [],
5079+};
5080+});
5081+state.runEmbeddedPiAgentMock.mockImplementationOnce(
5082+async (args: {
5083+onAssistantErrorMessagePersisted?: (message: {
5084+role: "assistant";
5085+content: string;
5086+stopReason: "error";
5087+}) => void;
5088+}) => {
5089+args.onAssistantErrorMessagePersisted?.({
5090+role: "assistant",
5091+content: "[assistant turn failed before producing content]",
5092+stopReason: "error",
5093+});
5094+throw new Error("upstream 500");
5095+},
5096+);
5097+state.runEmbeddedPiAgentMock.mockRejectedValueOnce(new Error("upstream 500"));
5098+state.runEmbeddedPiAgentMock.mockResolvedValueOnce({
5099+payloads: [{ text: "ok" }],
5100+meta: {},
5101+});
5102+5103+const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
5104+await runAgentTurnWithFallback(createMinimalRunAgentTurnParams());
5105+5106+expect(state.runEmbeddedPiAgentMock).toHaveBeenCalledTimes(3);
5107+expectMockCallArgFields(state.runEmbeddedPiAgentMock, 0, "primary candidate", {
5108+suppressAssistantErrorPersistence: false,
5109+});
5110+expectMockCallArgFields(state.runEmbeddedPiAgentMock, 1, "first fallback candidate", {
5111+suppressAssistantErrorPersistence: true,
5112+});
5113+expectMockCallArgFields(state.runEmbeddedPiAgentMock, 2, "second fallback candidate", {
5114+suppressAssistantErrorPersistence: true,
5115+});
5116+});
5117+5118+it("does not suppress the first embedded assistant error after a CLI fallback failure", async () => {
5119+state.isCliProviderMock.mockImplementation((provider: unknown) => provider === "anthropic");
5120+state.runWithModelFallbackMock.mockImplementationOnce(async (params: FallbackRunnerParams) => {
5121+await params.run("anthropic", "claude-opus-4-7").catch(() => undefined);
5122+return {
5123+result: await params.run("openai", "gpt-5.4"),
5124+provider: "openai",
5125+model: "gpt-5.4",
5126+attempts: [],
5127+};
5128+});
5129+state.runCliAgentMock.mockRejectedValueOnce(new Error("cli failed"));
5130+state.runEmbeddedPiAgentMock.mockResolvedValueOnce({
5131+payloads: [{ text: "ok" }],
5132+meta: {},
5133+});
5134+5135+const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
5136+await runAgentTurnWithFallback(createMinimalRunAgentTurnParams());
5137+5138+expect(state.runCliAgentMock).toHaveBeenCalledOnce();
5139+expect(state.runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
5140+expectMockCallArgFields(state.runEmbeddedPiAgentMock, 0, "embedded fallback candidate", {
5141+suppressAssistantErrorPersistence: false,
5142+});
5143+});
5144+5145+it("latches queued user message persistence across main reply fallback candidates", async () => {
5146+state.runWithModelFallbackMock.mockImplementationOnce(async (params: FallbackRunnerParams) => {
5147+await params.run("anthropic", "claude-opus-4-7").catch(() => undefined);
5148+return {
5149+result: await params.run("openai", "gpt-5.4"),
5150+provider: "openai",
5151+model: "gpt-5.4",
5152+attempts: [],
5153+};
5154+});
5155+state.runEmbeddedPiAgentMock.mockImplementationOnce(
5156+async (args: {
5157+onUserMessagePersisted?: (m: {
5158+role: "user";
5159+content: Array<{ type: "text"; text: string }>;
5160+}) => void;
5161+}) => {
5162+args.onUserMessagePersisted?.({
5163+role: "user",
5164+content: [{ type: "text", text: "queued" }],
5165+});
5166+throw new Error("upstream 500");
5167+},
5168+);
5169+state.runEmbeddedPiAgentMock.mockResolvedValueOnce({
5170+payloads: [{ text: "ok" }],
5171+meta: {},
5172+});
5173+5174+const runAgentTurnWithFallback = await getRunAgentTurnWithFallback();
5175+await runAgentTurnWithFallback(createMinimalRunAgentTurnParams());
5176+5177+expect(state.runEmbeddedPiAgentMock).toHaveBeenCalledTimes(2);
5178+expectMockCallArgFields(state.runEmbeddedPiAgentMock, 0, "primary candidate", {
5179+suppressNextUserMessagePersistence: false,
5180+});
5181+expectMockCallArgFields(state.runEmbeddedPiAgentMock, 1, "fallback candidate", {
5182+suppressNextUserMessagePersistence: true,
5183+});
5184+});
50695185});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。