


























@@ -129,6 +129,17 @@ function agentMessageDelta(delta: string, itemId = "msg-1"): ProjectorNotificati
129129return forCurrentTurn("item/agentMessage/delta", { itemId, delta });
130130}
131131132+function appServerError(params: { message: string; willRetry: boolean }): ProjectorNotification {
133+return forCurrentTurn("error", {
134+error: {
135+message: params.message,
136+codexErrorInfo: null,
137+additionalDetails: null,
138+},
139+willRetry: params.willRetry,
140+});
141+}
142+132143function turnCompleted(items: unknown[] = []): ProjectorNotification {
133144return {
134145method: "turn/completed",
@@ -235,6 +246,40 @@ describe("CodexAppServerEventProjector", () => {
235246expect(result.lastAssistant?.content).toEqual([{ type: "text", text: "OK from raw" }]);
236247});
237248249+it("does not fail a completed reply after a retryable app-server error notification", async () => {
250+const projector = await createProjector();
251+252+await projector.handleNotification(agentMessageDelta("still working"));
253+await projector.handleNotification(
254+appServerError({ message: "stream disconnected", willRetry: true }),
255+);
256+await projector.handleNotification(
257+turnCompleted([{ type: "agentMessage", id: "msg-1", text: "final answer" }]),
258+);
259+260+const result = projector.buildResult(buildEmptyToolTelemetry());
261+262+expect(result.assistantTexts).toEqual(["final answer"]);
263+expect(result.promptError).toBeNull();
264+expect(result.promptErrorSource).toBeNull();
265+expect(result.lastAssistant?.stopReason).toBe("stop");
266+expect(result.lastAssistant?.errorMessage).toBeUndefined();
267+});
268+269+it("uses nested app-server error messages for terminal errors", async () => {
270+const projector = await createProjector();
271+272+await projector.handleNotification(
273+appServerError({ message: "stream failed permanently", willRetry: false }),
274+);
275+276+const result = projector.buildResult(buildEmptyToolTelemetry());
277+278+expect(result.promptError).toBe("stream failed permanently");
279+expect(result.promptErrorSource).toBe("prompt");
280+expect(result.lastAssistant).toBeUndefined();
281+});
282+238283it("normalizes snake_case current token usage fields", async () => {
239284const projector = await createProjector();
240285此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。