1import { describe, expect, it } from "vite-plus/test";
2
3import { ThreadId, TurnItemId, type OrchestrationV2TurnItem } from "@t3tools/contracts";
4import * as DateTime from "effect/DateTime";
5
6import {
7 resolveWorkEntryToolPresentation,
8 toolItemForDisplay,
9 type WorkLogPresentationEntry,
10 workEntryIndicatesToolFailure,
11 workEntryDisplayIndicatesToolFailure,
12} from "./presentation.js";
13
14function commandItem(
15 fields: Partial<Extract<OrchestrationV2TurnItem, { type: "command_execution" }>> = {},
16): OrchestrationV2TurnItem {
17 return {
18 id: TurnItemId.make("command"),
19 threadId: ThreadId.make("thread"),
20 runId: null,
21 nodeId: null,
22 providerThreadId: null,
23 providerTurnId: null,
24 nativeItemRef: null,
25 parentItemId: null,
26 ordinal: 1,
27 status: "completed",
28 title: null,
29 startedAt: null,
30 completedAt: null,
31 updatedAt: DateTime.makeUnsafe("2026-09-08T00:00:00.000Z"),
32 type: "command_execution",
33 input: 'rg "command not found"',
34 ...fields,
35 };
36}
37
38describe("workEntryIndicatesToolFailure", () => {
39 const base = {
40 id: "w1",
41 createdAt: "2026-01-01T00:00:00.000Z",
42 label: "Read",
43 };
44
45 it.each([
46 [{ exitCode: 2 }, true],
47 [{ output: "sh: missing-command: command not found" }, true],
48 [{ output: `${"x".repeat(32_768)} command not found` }, false],
49 ] as const)(
50 "preserves command failure state after removing displayed output: %j",
51 (fields, failed) => {
52 const structuredPayload = commandItem(fields);
53 const entry: WorkLogPresentationEntry = {
54 ...base,
55 tone: "tool",
56 itemType: "command_execution",
57 toolLifecycleStatus: "completed",
58 structuredPayload,
59 };
60 expect(workEntryDisplayIndicatesToolFailure(entry)).toBe(failed);
61 expect(JSON.stringify(toolItemForDisplay(structuredPayload))).not.toContain('"output":');
62 },
63 );
64
65 it.each([
66 "logs/app.log:12: Error: ENOENT: no such file or directory",
67 "fixtures/ci.txt:3: Process exited with exit code 2",
68 ])("trusts a zero exit code over failure text in the output and command: %s", (output) => {
69 const entry: WorkLogPresentationEntry = {
70 ...base,
71 tone: "tool",
72 itemType: "command_execution",
73 toolLifecycleStatus: "completed",
74 command: 'rg -n "ENOENT|exit code" logs fixtures',
75 structuredPayload: commandItem({ output, exitCode: 0, outputIndicatesFailure: true }),
76 };
77 expect(workEntryIndicatesToolFailure(entry)).toBe(false);
78 expect(workEntryDisplayIndicatesToolFailure(entry)).toBe(false);
79 });
80
81 it("ignores failure phrases in reasoning text", () => {
82 const entry: WorkLogPresentationEntry = {
83 ...base,
84 label: "Thinking",
85 tone: "thinking",
86 itemType: "reasoning",
87 toolLifecycleStatus: "completed",
88 detail: "That exit code 2 is just from listing the deleted directory, which is expected.",
89 };
90 expect(workEntryIndicatesToolFailure(entry)).toBe(false);
91 expect(workEntryDisplayIndicatesToolFailure(entry)).toBe(false);
92 });
93
94 it("does not treat error text in a command as rendered failure", () => {
95 const entry = {
96 ...base,
97 label: "Ran command",
98 tone: "tool",
99 toolLifecycleStatus: "completed",
100 command: 'rg "file not found"',
101 detail: "Found 3 matches",
102 } satisfies WorkLogPresentationEntry;
103
104 expect(workEntryDisplayIndicatesToolFailure(entry)).toBe(false);
105
106 expect(workEntryIndicatesToolFailure(entry)).toBe(true);
107 expect(workEntryDisplayIndicatesToolFailure({ ...entry, detail: "File not found" })).toBe(true);
108 });
109});
110
111describe("resolveWorkEntryToolPresentation", () => {
112 it("shows returned MCP errors as failures even in the live activity row", () => {
113 const entry: WorkLogPresentationEntry = {
114 id: "clone",
115 createdAt: "2026-09-19T00:00:00.000Z",
116 tone: "tool",
117 label: "T3-code.t3_project_clone",
118 toolLifecycleStatus: "inProgress",
119 itemType: "dynamic_tool",
120 toolData: { output: { isError: true } },
121 };
122 expect(resolveWorkEntryToolPresentation(entry)?.displayName).toBe(
123 "Failed to clone a repository",
124 );
125 expect(workEntryDisplayIndicatesToolFailure(entry)).toBe(true);
126 const childFailure = {
127 ...entry,
128 label: "T3-code.task_status",
129 toolLifecycleStatus: "completed" as const,
130 toolData: { output: { taskId: "child", status: "failed", summary: "command not found" } },
131 };
132 expect(workEntryDisplayIndicatesToolFailure(childFailure)).toBe(false);
133 });
134 it.each([
135 "mcp__t3-code__preview_click",
136 "mcp__t3_code__preview_click",
137 "mcp__t3code__preview_click",
138 "T3-code.preview_click",
139 "t3-code · preview_click completed",
140 "t3_code/preview_click",
141 "preview_click",
142 ])("recognizes browser tool names across providers: %s", (label) => {
143 expect(resolveWorkEntryToolPresentation({ label })).toEqual({
144 displayName: "Clicking in the preview browser",
145 icon: "browser",
146 });
147 });
148
149 it("uses structured MCP identity when the provider supplies a custom title", () => {
150 expect(
151 resolveWorkEntryToolPresentation({
152 label: "Tool call complete",
153 toolTitle: "Inspect the current page",
154 toolData: { server: "t3-code", tool: "preview_snapshot", result: { title: "Example" } },
155 }),
156 ).toEqual({ displayName: "Taking a snapshot of the preview page", icon: "browser" });
157 });
158
159 it("uses the summary's state only when the provider omitted a lifecycle status", () => {
160 const entry = { label: "T3-code.preview_click" };
161 expect(resolveWorkEntryToolPresentation(entry, "inProgress")?.displayName).toBe(
162 "Clicking in the preview browser",
163 );
164 expect(resolveWorkEntryToolPresentation(entry, "completed")?.displayName).toBe(
165 "Clicked in the preview browser",
166 );
167 expect(
168 resolveWorkEntryToolPresentation({ ...entry, toolLifecycleStatus: "completed" }, "inProgress")
169 ?.displayName,
170 ).toBe("Clicked in the preview browser");
171 expect(
172 resolveWorkEntryToolPresentation({ ...entry, toolLifecycleStatus: "failed" }, "completed")
173 ?.displayName,
174 ).toBe("Failed to click in the preview browser");
175 });
176
177 it.each([
178 ["https://github.com/acme/web/pull/7", "PR #7"],
179 ["https://gitlab.com/acme/web/-/merge_requests/7", "MR #7"],
180 ])("names a linked change request with its host's term (%s)", (url, target) => {
181 expect(
182 resolveWorkEntryToolPresentation({
183 label: "T3-code.link_pull_request",
184 toolLifecycleStatus: "completed",
185 toolData: { arguments: { url } },
186 })?.displayName,
187 ).toMatch(new RegExp(` ${target}$`));
188 });
189
190 it("does not brand unknown tools or another server's matching tool name", () => {
191 for (const label of [
192 "mcp__github__preview_click",
193 "t3-code.unknown_tool",
194 "t3-code.toString",
195 "Search files",
196 ]) {
197 expect(resolveWorkEntryToolPresentation({ label })).toBeNull();
198 }
199 expect(
200 resolveWorkEntryToolPresentation({
201 label: "preview_click",
202 toolData: { server: "another-server", tool: "preview_click" },
203 }),
204 ).toBeNull();
205 });
206});