






























@@ -30,6 +30,7 @@ import {
3030STRICT_AGENTIC_BLOCKED_TEXT,
3131resolveReplayInvalidFlag,
3232resolveRunLivenessState,
33+resolveSilentToolResultReplyPayload,
3334shouldTreatEmptyAssistantReplyAsSilent,
3435} from "./run/incomplete-turn.js";
3536import type { EmbeddedRunAttemptResult } from "./run/types.js";
@@ -76,6 +77,113 @@ describe("runEmbeddedPiAgent incomplete-turn safety", () => {
7677expect(result.payloads?.[0]?.text).toContain("verify before retrying");
7778});
787980+it("synthesizes a silent cron payload from a trailing current-attempt NO_REPLY tool result", () => {
81+const payload = resolveSilentToolResultReplyPayload({
82+isCronTrigger: true,
83+payloadCount: 0,
84+aborted: false,
85+timedOut: false,
86+attempt: makeAttemptResult({
87+assistantTexts: [],
88+toolMetas: [{ toolName: "exec" }],
89+messagesSnapshot: [
90+{
91+role: "toolResult",
92+content: [{ type: "text", text: "NO_REPLY" }],
93+details: { aggregated: "NO_REPLY" },
94+} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
95+{
96+role: "assistant",
97+stopReason: "stop",
98+provider: "openai",
99+model: "gpt-5.4",
100+content: [],
101+} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
102+],
103+}),
104+});
105+106+expect(payload).toEqual({ text: "NO_REPLY" });
107+});
108+109+it("does not reuse an older NO_REPLY tool result without current-attempt tool activity", () => {
110+const payload = resolveSilentToolResultReplyPayload({
111+isCronTrigger: true,
112+payloadCount: 0,
113+aborted: false,
114+timedOut: false,
115+attempt: makeAttemptResult({
116+assistantTexts: [],
117+toolMetas: [],
118+messagesSnapshot: [
119+{
120+role: "toolResult",
121+content: [{ type: "text", text: "NO_REPLY" }],
122+} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
123+{
124+role: "user",
125+content: [{ type: "text", text: "Current cron prompt" }],
126+} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
127+{
128+role: "assistant",
129+stopReason: "stop",
130+provider: "openai",
131+model: "gpt-5.4",
132+content: [],
133+} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
134+],
135+}),
136+});
137+138+expect(payload).toBeNull();
139+});
140+141+it("treats exact NO_REPLY tool output as a quiet cron success when the final assistant is empty", async () => {
142+mockedClassifyFailoverReason.mockReturnValue(null);
143+mockedRunEmbeddedAttempt.mockResolvedValueOnce(
144+makeAttemptResult({
145+assistantTexts: [],
146+toolMetas: [{ toolName: "exec" }],
147+messagesSnapshot: [
148+{
149+role: "toolResult",
150+content: [{ type: "text", text: "NO_REPLY" }],
151+details: { aggregated: "NO_REPLY" },
152+} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
153+{
154+role: "assistant",
155+stopReason: "stop",
156+provider: "openai",
157+model: "gpt-5.4",
158+content: [],
159+} as unknown as EmbeddedRunAttemptResult["messagesSnapshot"][number],
160+],
161+lastAssistant: {
162+role: "assistant",
163+stopReason: "stop",
164+provider: "openai",
165+model: "gpt-5.4",
166+content: [],
167+} as unknown as EmbeddedRunAttemptResult["lastAssistant"],
168+}),
169+);
170+171+const result = await runEmbeddedPiAgent({
172+ ...overflowBaseRunParams,
173+trigger: "cron",
174+provider: "openai",
175+model: "gpt-5.4",
176+runId: "run-cron-no-reply-empty-final",
177+});
178+179+expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(1);
180+expect(result.payloads).toEqual([{ text: "NO_REPLY" }]);
181+expect(result.meta.livenessState).toBe("working");
182+expect(mockedLog.warn).not.toHaveBeenCalledWith(
183+expect.stringContaining("incomplete turn detected"),
184+);
185+});
186+79187it("uses explicit agentId without a session key before surfacing the strict-agentic blocked state", async () => {
80188mockedClassifyFailoverReason.mockReturnValue(null);
81189mockedRunEmbeddedAttempt.mockResolvedValue(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。