diff --git a/docs/web-ui/terminal-rendering.md b/docs/web-ui/terminal-rendering.md
index 35798fa0..a6e100aa 100644
--- a/docs/web-ui/terminal-rendering.md
+++ b/docs/web-ui/terminal-rendering.md
@@ -35,6 +35,11 @@ leading the summary text rather than the icon.
| `warn` | amber, left rule | stderr, an unrecognised event shape, API retries |
| `error` | red, left rule | turn failed, a tool result with `is_error: true` |
+The settings menu's "hide debug output" toggle (`getHideDebugPref()`,
+`@hive/shared/prefs.js`) skips `debug`-level rows entirely rather than
+muting them further — client-side only, same live-read-no-reload shape
+as the expand-tool-output preference above.
+
Per-tool icon + summary formatting (what a `Read`/`Edit`/`send` call's row
actually says) lives in `stream_enrich.rs`'s `fmt_tool_use()` family —
read that when you need the specifics, this doc doesn't duplicate it.
diff --git a/frontend/packages/agent/src/Root.tsx b/frontend/packages/agent/src/Root.tsx
index 8c29be4f..23d7fed4 100644
--- a/frontend/packages/agent/src/Root.tsx
+++ b/frontend/packages/agent/src/Root.tsx
@@ -12,6 +12,7 @@ import { useApplyMotionOverride } from "@hive/shared/motion-apply.js";
import { Header } from "./components/Header.js";
import { MetaNav } from "./components/MetaNav.js";
import { ExpandDetailsSetting } from "./components/ExpandDetailsSetting.js";
+import { HideDebugSetting } from "./components/HideDebugSetting.js";
import { StatusChips } from "./components/StatusChips.js";
import { LiveStream, type LiveStreamHandle } from "./components/LiveStream.js";
import { HeaderPill } from "./components/HeaderPill.js";
@@ -151,6 +152,7 @@ export function Root() {
) : null}
+
>
);
diff --git a/frontend/packages/agent/src/components/HideDebugSetting.tsx b/frontend/packages/agent/src/components/HideDebugSetting.tsx
new file mode 100644
index 00000000..bff063ae
--- /dev/null
+++ b/frontend/packages/agent/src/components/HideDebugSetting.tsx
@@ -0,0 +1,25 @@
+// Agent-terminal-only settings-menu row: "hide debug output". Same shape
+// as `ExpandDetailsSetting` (its own file's header comment explains why
+// this lives here rather than as a `SettingsMenu` prop) — mara: "add a
+// setting to not display verbose output... like the grey colored debug
+// stuff."
+import { useState } from "preact/hooks";
+import { getHideDebugPref, setHideDebugPref } from "@hive/shared/prefs.js";
+
+export function HideDebugSetting() {
+ const [hideDebug, setHideDebugState] = useState(() => getHideDebugPref());
+ return (
+
+ );
+}
diff --git a/frontend/packages/agent/src/components/Row.tsx b/frontend/packages/agent/src/components/Row.tsx
index 8ff6d1ca..18ade774 100644
--- a/frontend/packages/agent/src/components/Row.tsx
+++ b/frontend/packages/agent/src/components/Row.tsx
@@ -10,7 +10,7 @@ import { useEffect, useRef } from "preact/hooks";
import type { TermRow } from "../lib/termMsg.js";
import { linkifyToNodes } from "../lib/linkify.js";
import { renderMarkdown } from "../lib/markdown.js";
-import { getExpandDetailsPref } from "@hive/shared/prefs.js";
+import { getExpandDetailsPref, getHideDebugPref } from "@hive/shared/prefs.js";
function MarkdownBody({ text }: { text: string }) {
const ref = useRef(null);
@@ -51,6 +51,12 @@ function DiffBody({ text }: { text: string }) {
}
export function Row({ row }: { row: TermRow }) {
+ // "hide debug output" is a skip, not a dimmer — a hidden debug row
+ // still occupies no DOM node at all, same as it never streamed, rather
+ // than a CSS `display: none` that would keep it in the layout/DOM for
+ // no benefit.
+ if (row.level === "debug" && getHideDebugPref()) return null;
+
const cssClass = "level-" + row.level;
const icon = row.icon != null && row.icon !== "" && (
{row.icon}
diff --git a/frontend/packages/shared/src/prefs.ts b/frontend/packages/shared/src/prefs.ts
index 4e927483..4ba17362 100644
--- a/frontend/packages/shared/src/prefs.ts
+++ b/frontend/packages/shared/src/prefs.ts
@@ -2,8 +2,9 @@
// so a future second consumer (e.g. swarm-ui's own terminal) gets the
// exact same key name for free via the browser's shared per-origin
// storage, rather than an independent copy that could drift.
-// `ExpandDetailsSetting` (the per-agent page's own settings popover) is
-// the only writer today; `Row.tsx` is the only reader.
+// `ExpandDetailsSetting` / `HideDebugSetting` (the per-agent page's own
+// settings popover rows) are the only writers today; `Row.tsx` is the
+// only reader of either.
const EXPAND_DETAILS_KEY = "hive-agent-expand-details";
@@ -29,3 +30,27 @@ export function setExpandDetailsPref(v: boolean): void {
/* localStorage unavailable — preference is session-only */
}
}
+
+const HIDE_DEBUG_KEY = "hive-agent-hide-debug";
+
+// Whether a per-agent terminal's `debug`-level rows (thinking, coalesced
+// ticks, ambient harness chatter — see docs/web-ui/terminal-rendering.md's
+// Levels table) should be skipped entirely rather than rendered muted.
+// Same shape as `getExpandDetailsPref` above: pure client-side, no
+// backend field, read live (not cached) so a change applies to the next
+// rendered row without a reload.
+export function getHideDebugPref(): boolean {
+ try {
+ return localStorage.getItem(HIDE_DEBUG_KEY) === "1";
+ } catch {
+ return false;
+ }
+}
+export function setHideDebugPref(v: boolean): void {
+ try {
+ if (v) localStorage.setItem(HIDE_DEBUG_KEY, "1");
+ else localStorage.removeItem(HIDE_DEBUG_KEY);
+ } catch {
+ /* localStorage unavailable — preference is session-only */
+ }
+}