Skip to content

Commit f463c75

Browse files
committed
refactor: use OutputPaths compact mode in BashBackgroundListToolCall
- Add compact prop to OutputPaths for inline use in cards - Remove redundant empty state in expanded view (header already shows it)
1 parent 24b83e9 commit f463c75

4 files changed

Lines changed: 149 additions & 151 deletions

File tree

src/browser/components/tools/BashBackgroundListToolCall.tsx

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
LoadingDots,
1515
ToolIcon,
1616
ErrorBox,
17+
OutputPaths,
1718
} from "./shared/ToolPrimitives";
1819
import {
1920
useToolExpansion,
@@ -102,28 +103,13 @@ export const BashBackgroundListToolCall: React.FC<BashBackgroundListToolCallProp
102103
<div className="text-text-secondary truncate font-mono" title={proc.script}>
103104
{proc.script}
104105
</div>
105-
<div className="text-text-secondary mt-1 space-y-0.5 text-[10px]">
106-
<div>
107-
<span className="opacity-60">stdout:</span> {proc.stdout_path}
108-
</div>
109-
<div>
110-
<span className="opacity-60">stderr:</span> {proc.stderr_path}
111-
</div>
112-
</div>
106+
<OutputPaths stdout={proc.stdout_path} stderr={proc.stderr_path} compact />
113107
</div>
114108
))}
115109
</div>
116110
</DetailSection>
117111
)}
118112

119-
{result?.success && processes.length === 0 && (
120-
<DetailSection>
121-
<div className="text-text-secondary text-[11px] italic">
122-
No background processes running
123-
</div>
124-
</DetailSection>
125-
)}
126-
127113
{status === "executing" && !result && (
128114
<DetailSection>
129115
<div className="text-[11px]">

src/browser/components/tools/shared/ToolPrimitives.tsx

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -192,21 +192,33 @@ export const ErrorBox: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({
192192

193193
/**
194194
* Output file paths display (stdout/stderr)
195+
* @param compact - Use smaller text without background (for inline use in cards)
195196
*/
196197
interface OutputPathsProps {
197198
stdout: string;
198199
stderr: string;
200+
compact?: boolean;
199201
}
200202

201-
export const OutputPaths: React.FC<OutputPathsProps> = ({ stdout, stderr }) => (
202-
<div className="bg-code-bg space-y-1 rounded px-2 py-1.5 font-mono text-[11px]">
203-
<div>
204-
<span className="text-text-secondary">stdout:</span>{" "}
205-
<span className="text-text">{stdout}</span>
203+
export const OutputPaths: React.FC<OutputPathsProps> = ({ stdout, stderr, compact }) =>
204+
compact ? (
205+
<div className="text-text-secondary mt-1 space-y-0.5 text-[10px]">
206+
<div>
207+
<span className="opacity-60">stdout:</span> {stdout}
208+
</div>
209+
<div>
210+
<span className="opacity-60">stderr:</span> {stderr}
211+
</div>
206212
</div>
207-
<div>
208-
<span className="text-text-secondary">stderr:</span>{" "}
209-
<span className="text-text">{stderr}</span>
213+
) : (
214+
<div className="bg-code-bg space-y-1 rounded px-2 py-1.5 font-mono text-[11px]">
215+
<div>
216+
<span className="text-text-secondary">stdout:</span>{" "}
217+
<span className="text-text">{stdout}</span>
218+
</div>
219+
<div>
220+
<span className="text-text-secondary">stderr:</span>{" "}
221+
<span className="text-text">{stderr}</span>
222+
</div>
210223
</div>
211-
</div>
212-
);
224+
);

src/browser/utils/messages/StreamingMessageAggregator.init.test.ts

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -7,70 +7,70 @@ interface InitDisplayedMessage {
77
exitCode: number | null;
88
}
99

10-
// describe("Init display after cleanup changes", () => {
11-
// it("should display init messages correctly", () => {
12-
// const aggregator = new StreamingMessageAggregator("2024-01-01T00:00:00.000Z");
10+
describe("Init display after cleanup changes", () => {
11+
it("should display init messages correctly", () => {
12+
const aggregator = new StreamingMessageAggregator("2024-01-01T00:00:00.000Z");
1313

14-
// // Simulate init start
15-
// aggregator.handleMessage({
16-
// type: "init-start",
17-
// hookPath: "/test/.mux/init",
18-
// timestamp: Date.now(),
19-
// });
14+
// Simulate init start
15+
aggregator.handleMessage({
16+
type: "init-start",
17+
hookPath: "/test/.mux/init",
18+
timestamp: Date.now(),
19+
});
2020

21-
// let messages = aggregator.getDisplayedMessages();
22-
// expect(messages).toHaveLength(1);
23-
// expect(messages[0].type).toBe("workspace-init");
24-
// expect((messages[0] as InitDisplayedMessage).status).toBe("running");
21+
let messages = aggregator.getDisplayedMessages();
22+
expect(messages).toHaveLength(1);
23+
expect(messages[0].type).toBe("workspace-init");
24+
expect((messages[0] as InitDisplayedMessage).status).toBe("running");
2525

26-
// // Simulate init output
27-
// aggregator.handleMessage({
28-
// type: "init-output",
29-
// line: "Installing dependencies...",
30-
// timestamp: Date.now(),
31-
// isError: false,
32-
// });
26+
// Simulate init output
27+
aggregator.handleMessage({
28+
type: "init-output",
29+
line: "Installing dependencies...",
30+
timestamp: Date.now(),
31+
isError: false,
32+
});
3333

34-
// messages = aggregator.getDisplayedMessages();
35-
// expect(messages).toHaveLength(1);
36-
// expect((messages[0] as InitDisplayedMessage).lines).toContain("Installing dependencies...");
34+
messages = aggregator.getDisplayedMessages();
35+
expect(messages).toHaveLength(1);
36+
expect((messages[0] as InitDisplayedMessage).lines).toContain("Installing dependencies...");
3737

38-
// // Simulate init end
39-
// aggregator.handleMessage({
40-
// type: "init-end",
41-
// exitCode: 0,
42-
// timestamp: Date.now(),
43-
// });
38+
// Simulate init end
39+
aggregator.handleMessage({
40+
type: "init-end",
41+
exitCode: 0,
42+
timestamp: Date.now(),
43+
});
4444

45-
// messages = aggregator.getDisplayedMessages();
46-
// expect(messages).toHaveLength(1);
47-
// expect((messages[0] as InitDisplayedMessage).status).toBe("success");
48-
// expect((messages[0] as InitDisplayedMessage).exitCode).toBe(0);
49-
// });
45+
messages = aggregator.getDisplayedMessages();
46+
expect(messages).toHaveLength(1);
47+
expect((messages[0] as InitDisplayedMessage).status).toBe("success");
48+
expect((messages[0] as InitDisplayedMessage).exitCode).toBe(0);
49+
});
5050

51-
// it("should handle init-output without init-start (defensive)", () => {
52-
// const aggregator = new StreamingMessageAggregator("2024-01-01T00:00:00.000Z");
51+
it("should handle init-output without init-start (defensive)", () => {
52+
const aggregator = new StreamingMessageAggregator("2024-01-01T00:00:00.000Z");
5353

54-
// // This might crash with non-null assertion if initState is null
55-
// expect(() => {
56-
// aggregator.handleMessage({
57-
// type: "init-output",
58-
// line: "Some output",
59-
// timestamp: Date.now(),
60-
// isError: false,
61-
// });
62-
// }).not.toThrow();
63-
// });
54+
// This might crash with non-null assertion if initState is null
55+
expect(() => {
56+
aggregator.handleMessage({
57+
type: "init-output",
58+
line: "Some output",
59+
timestamp: Date.now(),
60+
isError: false,
61+
});
62+
}).not.toThrow();
63+
});
6464

65-
// it("should handle init-end without init-start (defensive)", () => {
66-
// const aggregator = new StreamingMessageAggregator("2024-01-01T00:00:00.000Z");
65+
it("should handle init-end without init-start (defensive)", () => {
66+
const aggregator = new StreamingMessageAggregator("2024-01-01T00:00:00.000Z");
6767

68-
// expect(() => {
69-
// aggregator.handleMessage({
70-
// type: "init-end",
71-
// exitCode: 0,
72-
// timestamp: Date.now(),
73-
// });
74-
// }).not.toThrow();
75-
// });
76-
// });
68+
expect(() => {
69+
aggregator.handleMessage({
70+
type: "init-end",
71+
exitCode: 0,
72+
timestamp: Date.now(),
73+
});
74+
}).not.toThrow();
75+
});
76+
});

src/browser/utils/thinking/policy.test.ts

Lines changed: 68 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -120,74 +120,74 @@ describe("getThinkingPolicyForModel", () => {
120120
});
121121
});
122122

123-
// describe("enforceThinkingPolicy", () => {
124-
// describe("single-option policy models (gpt-5-pro)", () => {
125-
// test("enforces high for any requested level", () => {
126-
// expect(enforceThinkingPolicy("openai:gpt-5-pro", "off")).toBe("high");
127-
// expect(enforceThinkingPolicy("openai:gpt-5-pro", "low")).toBe("high");
128-
// expect(enforceThinkingPolicy("openai:gpt-5-pro", "medium")).toBe("high");
129-
// expect(enforceThinkingPolicy("openai:gpt-5-pro", "high")).toBe("high");
130-
// });
131-
132-
// test("enforces high for versioned gpt-5-pro", () => {
133-
// expect(enforceThinkingPolicy("openai:gpt-5-pro-2025-10-06", "low")).toBe("high");
134-
// });
135-
// });
136-
137-
// describe("multi-option policy models", () => {
138-
// test("allows requested level if in allowed set", () => {
139-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4", "off")).toBe("off");
140-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4", "low")).toBe("low");
141-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4", "medium")).toBe("medium");
142-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4", "high")).toBe("high");
143-
// });
144-
145-
// test("falls back to medium when requested level not allowed", () => {
146-
// // Simulating behavior with gpt-5-pro (only allows "high")
147-
// // When requesting "low", falls back to first allowed level which is "high"
148-
// expect(enforceThinkingPolicy("openai:gpt-5-pro", "low")).toBe("high");
149-
// });
150-
// });
151-
152-
// describe("Opus 4.5 (all levels supported)", () => {
153-
// test("allows all levels including off", () => {
154-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "off")).toBe("off");
155-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "low")).toBe("low");
156-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "medium")).toBe("medium");
157-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "high")).toBe("high");
158-
// });
159-
160-
// test("allows off for versioned model", () => {
161-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4-5-20251101", "off")).toBe("off");
162-
// });
163-
// });
164-
165-
// describe("GPT-5.1-Codex-Max (5 levels including xhigh)", () => {
166-
// test("allows all 5 levels including xhigh", () => {
167-
// expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "off")).toBe("off");
168-
// expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "low")).toBe("low");
169-
// expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "medium")).toBe("medium");
170-
// expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "high")).toBe("high");
171-
// expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "xhigh")).toBe("xhigh");
172-
// });
173-
174-
// test("allows xhigh for versioned model", () => {
175-
// expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max-2025-12-01", "xhigh")).toBe("xhigh");
176-
// });
177-
// });
178-
179-
// describe("xhigh fallback for non-codex-max models", () => {
180-
// test("falls back to medium when xhigh requested on standard model", () => {
181-
// // Standard models don't support xhigh, so fall back to medium (preferred fallback)
182-
// expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "xhigh")).toBe("medium");
183-
// });
184-
185-
// test("falls back to high when xhigh requested on gpt-5-pro", () => {
186-
// // gpt-5-pro only supports high, so xhigh falls back to high
187-
// expect(enforceThinkingPolicy("openai:gpt-5-pro", "xhigh")).toBe("high");
188-
// });
189-
// });
190-
// });
123+
describe("enforceThinkingPolicy", () => {
124+
describe("single-option policy models (gpt-5-pro)", () => {
125+
test("enforces high for any requested level", () => {
126+
expect(enforceThinkingPolicy("openai:gpt-5-pro", "off")).toBe("high");
127+
expect(enforceThinkingPolicy("openai:gpt-5-pro", "low")).toBe("high");
128+
expect(enforceThinkingPolicy("openai:gpt-5-pro", "medium")).toBe("high");
129+
expect(enforceThinkingPolicy("openai:gpt-5-pro", "high")).toBe("high");
130+
});
131+
132+
test("enforces high for versioned gpt-5-pro", () => {
133+
expect(enforceThinkingPolicy("openai:gpt-5-pro-2025-10-06", "low")).toBe("high");
134+
});
135+
});
136+
137+
describe("multi-option policy models", () => {
138+
test("allows requested level if in allowed set", () => {
139+
expect(enforceThinkingPolicy("anthropic:claude-opus-4", "off")).toBe("off");
140+
expect(enforceThinkingPolicy("anthropic:claude-opus-4", "low")).toBe("low");
141+
expect(enforceThinkingPolicy("anthropic:claude-opus-4", "medium")).toBe("medium");
142+
expect(enforceThinkingPolicy("anthropic:claude-opus-4", "high")).toBe("high");
143+
});
144+
145+
test("falls back to medium when requested level not allowed", () => {
146+
// Simulating behavior with gpt-5-pro (only allows "high")
147+
// When requesting "low", falls back to first allowed level which is "high"
148+
expect(enforceThinkingPolicy("openai:gpt-5-pro", "low")).toBe("high");
149+
});
150+
});
151+
152+
describe("Opus 4.5 (all levels supported)", () => {
153+
test("allows all levels including off", () => {
154+
expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "off")).toBe("off");
155+
expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "low")).toBe("low");
156+
expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "medium")).toBe("medium");
157+
expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "high")).toBe("high");
158+
});
159+
160+
test("allows off for versioned model", () => {
161+
expect(enforceThinkingPolicy("anthropic:claude-opus-4-5-20251101", "off")).toBe("off");
162+
});
163+
});
164+
165+
describe("GPT-5.1-Codex-Max (5 levels including xhigh)", () => {
166+
test("allows all 5 levels including xhigh", () => {
167+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "off")).toBe("off");
168+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "low")).toBe("low");
169+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "medium")).toBe("medium");
170+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "high")).toBe("high");
171+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "xhigh")).toBe("xhigh");
172+
});
173+
174+
test("allows xhigh for versioned model", () => {
175+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max-2025-12-01", "xhigh")).toBe("xhigh");
176+
});
177+
});
178+
179+
describe("xhigh fallback for non-codex-max models", () => {
180+
test("falls back to medium when xhigh requested on standard model", () => {
181+
// Standard models don't support xhigh, so fall back to medium (preferred fallback)
182+
expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "xhigh")).toBe("medium");
183+
});
184+
185+
test("falls back to high when xhigh requested on gpt-5-pro", () => {
186+
// gpt-5-pro only supports high, so xhigh falls back to high
187+
expect(enforceThinkingPolicy("openai:gpt-5-pro", "xhigh")).toBe("high");
188+
});
189+
});
190+
});
191191

192192
// Note: Tests for invalid levels removed - TypeScript type system prevents invalid
193193
// ThinkingLevel values at compile time, making runtime invalid-level tests unnecessary.

0 commit comments

Comments
 (0)