
























@@ -1991,6 +1991,95 @@ describe("CodexAppServerEventProjector", () => {
19911991expect(payload.text).toContain("```txt\nopened\n```");
19921992});
199319931994+it("keeps side-effect evidence for dynamic tools that error after execution", async () => {
1995+const projector = await createProjector();
1996+1997+projector.recordDynamicToolCall({
1998+callId: "call-process-kill",
1999+tool: "process",
2000+arguments: { action: "kill", sessionId: "session-1" },
2001+});
2002+projector.recordDynamicToolResult({
2003+callId: "call-process-kill",
2004+tool: "process",
2005+success: false,
2006+terminalType: "error",
2007+sideEffectEvidence: true,
2008+contentItems: [{ type: "inputText", text: "process exited" }],
2009+});
2010+2011+const result = projector.buildResult(buildEmptyToolTelemetry());
2012+2013+expect(result.replayMetadata).toEqual({ hadPotentialSideEffects: true, replaySafe: false });
2014+});
2015+2016+it("does not keep side-effect evidence for pre-execution dynamic tool errors", async () => {
2017+const projector = await createProjector();
2018+2019+projector.recordDynamicToolCall({
2020+callId: "call-unknown-message",
2021+tool: "message",
2022+arguments: { action: "send", text: "hello" },
2023+});
2024+projector.recordDynamicToolResult({
2025+callId: "call-unknown-message",
2026+tool: "message",
2027+success: false,
2028+terminalType: "error",
2029+contentItems: [{ type: "inputText", text: "Unknown OpenClaw tool: message" }],
2030+});
2031+2032+const result = projector.buildResult(buildEmptyToolTelemetry());
2033+2034+expect(result.replayMetadata).toEqual({ hadPotentialSideEffects: false, replaySafe: true });
2035+});
2036+2037+it("does not mark blocked dynamic tools as side-effecting", async () => {
2038+const projector = await createProjector();
2039+2040+projector.recordDynamicToolCall({
2041+callId: "call-bash-blocked",
2042+tool: "bash",
2043+arguments: { command: "touch blocked.txt" },
2044+});
2045+projector.recordDynamicToolResult({
2046+callId: "call-bash-blocked",
2047+tool: "bash",
2048+success: false,
2049+terminalType: "blocked",
2050+sideEffectEvidence: true,
2051+contentItems: [{ type: "inputText", text: "blocked" }],
2052+});
2053+2054+const result = projector.buildResult(buildEmptyToolTelemetry());
2055+2056+expect(result.replayMetadata).toEqual({ hadPotentialSideEffects: false, replaySafe: true });
2057+});
2058+2059+it("treats completed native MCP tool calls as side-effect evidence", async () => {
2060+const projector = await createProjector();
2061+2062+await projector.handleNotification({
2063+method: "item/completed",
2064+params: {
2065+threadId: "thread-1",
2066+turnId: "turn-1",
2067+item: {
2068+id: "mcp-1",
2069+type: "mcpToolCall",
2070+server: "github",
2071+tool: "create_issue",
2072+status: "completed",
2073+arguments: { title: "check replay safety" },
2074+},
2075+},
2076+});
2077+2078+const result = projector.buildResult(buildEmptyToolTelemetry());
2079+2080+expect(result.replayMetadata).toEqual({ hadPotentialSideEffects: true, replaySafe: false });
2081+});
2082+19942083it("suppresses transcript progress for message-like tools", async () => {
19952084const onAgentEvent = vi.fn();
19962085const onToolResult = vi.fn();
@@ -2021,7 +2110,7 @@ describe("CodexAppServerEventProjector", () => {
20212110expect(onToolResult).not.toHaveBeenCalled();
20222111});
202321122024-it("suppresses transcript progress for activity-log bash commands", async () => {
2113+it("does not parse shell command text to suppress transcript progress", async () => {
20252114const onAgentEvent = vi.fn();
20262115const onToolResult = vi.fn();
20272116const projector = await createProjector({
@@ -2047,12 +2136,11 @@ describe("CodexAppServerEventProjector", () => {
20472136contentItems: [{ type: "inputText", text: "Logged: [web_search] Grilled salmon research" }],
20482137});
204921382050-const toolEvents = onAgentEvent.mock.calls.filter(([event]) => {
2051-const record = requireRecord(event, "agent event");
2052-return record.stream === "tool";
2053-});
2054-expect(toolEvents).toHaveLength(0);
2055-expect(onToolResult).not.toHaveBeenCalled();
2139+expect(onAgentEvent).not.toHaveBeenCalled();
2140+const toolProgressText = onToolResult.mock.calls
2141+.map(([payload]) => (payload as { text?: string }).text ?? "")
2142+.join("\n");
2143+expect(toolProgressText).toContain("log_activity.sh");
2056214420572145const result = projector.buildResult(buildEmptyToolTelemetry());
20582146expect(result.messagesSnapshot.some((message) => message.role === "toolResult")).toBe(true);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。