- {hasContent ? (
-
- ) : (
-
- {renderedHeader}
+ {headerActions ? (
+
+ {headerButton}
+
+ {headerActions}
+
+ ) : (
+ headerButton
)}
{expanded && hasContent && children}
diff --git a/site/src/pages/AgentsPage/components/ChatElements/tools/utils.test.ts b/site/src/pages/AgentsPage/components/ChatElements/tools/utils.test.ts
index 2f462e255d..09be688333 100644
--- a/site/src/pages/AgentsPage/components/ChatElements/tools/utils.test.ts
+++ b/site/src/pages/AgentsPage/components/ChatElements/tools/utils.test.ts
@@ -1,6 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import {
- BORDER_BG_STYLE,
buildEditDiff,
buildWriteFileDiff,
COLLAPSED_OUTPUT_HEIGHT,
@@ -9,6 +8,7 @@ import {
diffViewerCSS,
fileViewerCSS,
formatResultOutput,
+ formatShellDurationMs,
getDiffViewerOptions,
getFileContentForViewer,
getFileViewerOptions,
@@ -70,20 +70,45 @@ describe("shortDurationMs", () => {
expect(shortDurationMs(1000)).toBe("1s");
expect(shortDurationMs(30_000)).toBe("30s");
expect(shortDurationMs(59_000)).toBe("59s");
+ expect(shortDurationMs(59_499)).toBe("59s");
});
it("formats minutes", () => {
+ expect(shortDurationMs(59_500)).toBe("1m");
expect(shortDurationMs(60_000)).toBe("1m");
expect(shortDurationMs(300_000)).toBe("5m");
expect(shortDurationMs(3_540_000)).toBe("59m");
+ expect(shortDurationMs(3_569_999)).toBe("59m");
});
it("formats hours", () => {
+ expect(shortDurationMs(3_570_000)).toBe("1h");
expect(shortDurationMs(3_600_000)).toBe("1h");
expect(shortDurationMs(7_200_000)).toBe("2h");
});
});
+describe("formatShellDurationMs", () => {
+ it("returns empty string for invalid values", () => {
+ expect(formatShellDurationMs(undefined)).toBe("");
+ expect(formatShellDurationMs(-1)).toBe("");
+ expect(formatShellDurationMs(Number.NaN)).toBe("");
+ expect(formatShellDurationMs(Number.POSITIVE_INFINITY)).toBe("");
+ });
+
+ it("formats milliseconds and rounded seconds", () => {
+ expect(formatShellDurationMs(100)).toBe("100ms");
+ expect(formatShellDurationMs(47_200)).toBe("47.2s");
+ expect(formatShellDurationMs(59_949)).toBe("59.9s");
+ expect(formatShellDurationMs(59_950)).toBe("1m");
+ });
+
+ it("formats rounded minutes and hours", () => {
+ expect(formatShellDurationMs(3_596_999)).toBe("59.9m");
+ expect(formatShellDurationMs(3_597_000)).toBe("1h");
+ });
+});
+
describe("normalizeStatus", () => {
it("lowercases and trims", () => {
expect(normalizeStatus(" COMPLETED ")).toBe("completed");
@@ -840,13 +865,6 @@ describe("constants", () => {
expect(DIFFS_FONT_STYLE).toHaveProperty("--diffs-line-height", "1.5");
});
- it("BORDER_BG_STYLE has expected background", () => {
- expect(BORDER_BG_STYLE).toHaveProperty(
- "background",
- "hsl(var(--border-default))",
- );
- });
-
it("fileViewerCSS is a non-empty string", () => {
expect(typeof fileViewerCSS).toBe("string");
expect(fileViewerCSS.length).toBeGreaterThan(0);
diff --git a/site/src/pages/AgentsPage/components/ChatElements/tools/utils.ts b/site/src/pages/AgentsPage/components/ChatElements/tools/utils.ts
index 467a7387bd..87d80e6891 100644
--- a/site/src/pages/AgentsPage/components/ChatElements/tools/utils.ts
+++ b/site/src/pages/AgentsPage/components/ChatElements/tools/utils.ts
@@ -55,11 +55,38 @@ export const shortDurationMs = (durationMs: number | undefined): string => {
if (seconds < 60) {
return `${seconds}s`;
}
- const minutes = Math.round(seconds / 60);
+ const minutes = Math.round(durationMs / 60_000);
if (minutes < 60) {
return `${minutes}m`;
}
- const hours = Math.round(minutes / 60);
+ const hours = Math.round(durationMs / 3_600_000);
+ return `${hours}h`;
+};
+
+const roundToTenths = (value: number): number => Number(value.toFixed(1));
+
+export const formatShellDurationMs = (
+ durationMs: number | undefined,
+): string => {
+ if (
+ durationMs === undefined ||
+ durationMs < 0 ||
+ !Number.isFinite(durationMs)
+ ) {
+ return "";
+ }
+ if (durationMs < 1000) {
+ return `${Math.round(durationMs)}ms`;
+ }
+ const seconds = roundToTenths(durationMs / 1000);
+ if (seconds < 60) {
+ return `${seconds}s`;
+ }
+ const minutes = roundToTenths(durationMs / 60_000);
+ if (minutes < 60) {
+ return `${minutes}m`;
+ }
+ const hours = roundToTenths(durationMs / 3_600_000);
return `${hours}h`;
};
@@ -439,10 +466,6 @@ export const DIFFS_FONT_STYLE = {
"--diffs-line-height": "1.5",
} as CSSProperties;
-export const BORDER_BG_STYLE = {
- background: "hsl(var(--border-default))",
-};
-
/**
* Checks whether a tool result should be rendered as a syntax-highlighted
* file viewer. Returns the file path, content, and whether the header
diff --git a/site/src/pages/AgentsPage/components/DisplayModeSettings.tsx b/site/src/pages/AgentsPage/components/DisplayModeSettings.tsx
index 2e0b861ff5..2db2d8eb30 100644
--- a/site/src/pages/AgentsPage/components/DisplayModeSettings.tsx
+++ b/site/src/pages/AgentsPage/components/DisplayModeSettings.tsx
@@ -115,6 +115,23 @@ export const ThinkingDisplaySettings: FC = () => {
);
};
+export const ShellToolDisplaySettings: FC = () => {
+ return (
+
settings.shell_tool_display_mode}
+ updateSettings={(value) => ({
+ shell_tool_display_mode: value,
+ })}
+ />
+ );
+};
+
export const CodeDiffDisplaySettings: FC = () => {
return (