Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions desktop/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,10 @@ export default function App() {
hydrateDisplayMode(settings.displayMode);
setSidebarImConnections(sidebarImConnectionsFromBot(settings.bot, t));
setImTopicSources(sidebarImTopicSourcesFromBot(settings.bot, t));
if (!showToolCallsLoaded.current) {
showToolCallsLoaded.current = true;
setShowToolCalls(settings.showToolCalls !== false);
}
};
void syncDesktopPreferences().catch((e) => {
console.warn("desktop preferences sync failed", e);
Expand Down Expand Up @@ -914,6 +918,8 @@ export default function App() {
const [pendingPlanRevision, setPendingPlanRevision] = useState<string | null>(null);
const [footerHeight, setFooterHeight] = useState(0);
const footerHeightRef = useRef(0);
const [showToolCalls, setShowToolCalls] = useState<boolean>(true);
const showToolCallsLoaded = useRef(false);
const footerRef = useRef<HTMLElement>(null);
const runningRef = useRef(state.running);
const rightDockDetailActive = rightDockMode !== "context" && workspacePreviewActive;
Expand Down Expand Up @@ -2557,6 +2563,7 @@ export default function App() {
actionPending={state.messageAction != null}
rewindDisabled={state.running || state.messageAction != null || state.approval != null || state.ask != null || clearContextPending}
defaultExpandThinking={expandThinking}
showToolCalls={showToolCalls}
/>
)}
</main>
Expand Down Expand Up @@ -2779,6 +2786,7 @@ export default function App() {
.then(applyDesktopPreferences)
.catch((e) => console.warn("desktop preferences refresh failed", e));
}}
onShowToolCallsChange={setShowToolCalls}
/>
)}

Expand Down
43 changes: 40 additions & 3 deletions desktop/frontend/src/components/SettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const SETTINGS_TABS: SettingsTab[] = ["general", "models", "bots", "mcp", "skill
// SettingsPanel is the desktop settings centre — a centred modal with left
// navigation and a right content area. It hosts all settings pages plus MCP,
// Skills, and Memory management, replacing the old per-feature drawers.
export function SettingsPanel({ onClose, onChanged, initialTab, isDevBuild }: { onClose: () => void; onChanged: () => void; initialTab?: SettingsTab; isDevBuild?: boolean }) {
export function SettingsPanel({ onClose, onChanged, initialTab, isDevBuild, onShowToolCallsChange }: { onClose: () => void; onChanged: () => void; initialTab?: SettingsTab; isDevBuild?: boolean; onShowToolCallsChange?: (show: boolean) => void }) {
const t = useT();
const [s, setS] = useState<SettingsView | null>(null);
const [busy, setBusy] = useState(false);
Expand All @@ -59,6 +59,18 @@ export function SettingsPanel({ onClose, onChanged, initialTab, isDevBuild }: {
setThemeStyleState(nextStyle);
}, [s?.desktopTheme, s?.desktopThemeStyle]);

// Push the persisted showToolCalls flag into App-level state once on load, so
// the transcript honours the persisted preference even if the user never opens
// Settings. We do not subscribe to s?.showToolCalls here — that would feed our
// own optimistic updates back into App and cause stale-button flicker.
const syncedShowToolCalls = useRef(false);
useEffect(() => {
if (s && !syncedShowToolCalls.current) {
syncedShowToolCalls.current = true;
onShowToolCallsChange?.(s.showToolCalls !== false);
}
}, [s, onShowToolCallsChange]);

// apply runs a mutation, re-reads settings, and refreshes the topbar/model.
const apply = async (fn: () => Promise<void>) => {
setBusy(true);
Expand Down Expand Up @@ -125,7 +137,7 @@ export function SettingsPanel({ onClose, onChanged, initialTab, isDevBuild }: {
<div className="empty">{t("settings.loading")}</div>
) : (
<>
{tab === "general" && s && <SettingsPageShell key={tab} s={s} tab={tab} busy={busy} apply={apply}><GeneralSection s={s} busy={busy} apply={apply} /></SettingsPageShell>}
{tab === "general" && s && <SettingsPageShell key={tab} s={s} tab={tab} busy={busy} apply={apply}><GeneralSection s={s} busy={busy} apply={apply} onShowToolCallsChange={onShowToolCallsChange} onOptimisticShowToolCalls={(next) => setS((prev) => (prev ? { ...prev, showToolCalls: next } : prev))} /></SettingsPageShell>}
{tab === "models" && s && <SettingsPageShell key={tab} s={s} tab={tab} busy={busy} apply={apply}><ModelsSection s={s} busy={busy} apply={apply} backgroundApply={backgroundApply} /></SettingsPageShell>}
{tab === "bots" && isDevBuild && s && <SettingsPageShell key={tab} s={s} tab={tab} busy={busy} apply={apply}><BotsSection s={s} busy={busy} apply={apply} /></SettingsPageShell>}
{tab === "mcp" && <SettingsPageShell key={tab} s={s} tab={tab} busy={false} apply={apply}><MCPServersSettingsPage /></SettingsPageShell>}
Expand Down Expand Up @@ -636,7 +648,7 @@ function reasoningProtocolLabel(protocol: string, t: ReturnType<typeof useT>): s
}
}

function GeneralSection({ s, busy, apply }: SectionProps) {
function GeneralSection({ s, busy, apply, onShowToolCallsChange, onOptimisticShowToolCalls }: SectionProps & { onShowToolCallsChange?: (show: boolean) => void; onOptimisticShowToolCalls?: (show: boolean) => void }) {
const { t, setPref } = useI18n();
const closeBehavior = normalizeCloseBehavior(s.closeBehavior);
const [displayMode, setDisplayMode] = useState<DisplayMode>(() => normalizeDisplayMode(getDisplayMode()));
Expand All @@ -647,6 +659,13 @@ function GeneralSection({ s, busy, apply }: SectionProps) {
setPref(next);
void apply(() => app.SetDesktopLanguage(next));
};
const showToolCalls = s.showToolCalls !== false;
const setShowToolCalls = (next: boolean) => {
if (next === showToolCalls) return;
onOptimisticShowToolCalls?.(next);
onShowToolCallsChange?.(next);
void apply(() => app.SetShowToolCalls(next));
};
return (
<SettingsSection title={t("settings.tab.general")}>
<SettingsField label={t("settings.language")}>
Expand Down Expand Up @@ -722,6 +741,24 @@ function GeneralSection({ s, busy, apply }: SectionProps) {
))}
</div>
</SettingsField>
<SettingsField label={t("settings.showToolCalls")} hint={t("settings.showToolCallsHint")}>
<div className="set-seg">
<button
className={`set-seg__btn${showToolCalls ? " set-seg__btn--on" : ""}`}
disabled={busy}
onClick={() => setShowToolCalls(true)}
>
{t("common.on")}
</button>
<button
className={`set-seg__btn${!showToolCalls ? " set-seg__btn--on" : ""}`}
disabled={busy}
onClick={() => setShowToolCalls(false)}
>
{t("common.off")}
</button>
</div>
</SettingsField>
</SettingsSection>
);
}
Expand Down
14 changes: 12 additions & 2 deletions desktop/frontend/src/components/Transcript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export function Transcript({
rewindDisabled = false,
questionNavigator = true,
defaultExpandThinking = false,
showToolCalls = true,
}: {
items: Item[];
live?: LiveStream;
Expand All @@ -180,6 +181,7 @@ export function Transcript({
rewindDisabled?: boolean;
questionNavigator?: boolean;
defaultExpandThinking?: boolean;
showToolCalls?: boolean;
}) {
const scrollRef = useRef<HTMLDivElement>(null);
const stick = useRef(true);
Expand Down Expand Up @@ -435,6 +437,7 @@ export function Transcript({
if (it.parentId) break;
if (it.name === "todo_write") break;
if (it.name === "exit_plan_mode") break;
if (!showToolCalls) break;
out.push(<ToolCard key={it.id} item={it} subcalls={subcallsByParent.get(it.id)} />);
break;
case "phase": out.push(<PhaseCard key={it.id} text={it.text} />); break;
Expand Down Expand Up @@ -489,6 +492,7 @@ export function Transcript({
return next;
});
}}
showToolCalls={showToolCalls}
/>
)}
{hotZoneNodes}
Expand Down Expand Up @@ -519,6 +523,7 @@ const WarmZone = memo(function WarmZone({
defaultExpandThinking = false,
onToggleColdPage,
onToggleWarmTurn,
showToolCalls = true,
}: {
turnGroups: TurnGroup[];
expandedWarmTurns: ReadonlySet<number>;
Expand All @@ -532,11 +537,12 @@ const WarmZone = memo(function WarmZone({
warmOpenAction: OpenTurnAction | null;
warmActionPending: boolean;
warmRewindDisabled: boolean;
warmOnRewind: ((turn: number, scope: string) => void) | undefined;
warmOnRewind?: (turn: number, scope: string) => void;
warmSetOpenAction: (action: OpenTurnAction | null) => void;
defaultExpandThinking?: boolean;
onToggleColdPage: () => void;
onToggleWarmTurn: (g: number, expand: boolean) => void;
showToolCalls?: boolean;
}) {
const t = useT();
const out: React.ReactNode[] = [];
Expand Down Expand Up @@ -590,6 +596,7 @@ const WarmZone = memo(function WarmZone({
onRewind={warmOnRewind}
setOpenAction={warmSetOpenAction}
defaultExpandThinking={defaultExpandThinking}
showToolCalls={showToolCalls}
/>
</WarmTurnCard>,
);
Expand Down Expand Up @@ -634,6 +641,7 @@ function WarmTurnItems({
onRewind,
setOpenAction,
defaultExpandThinking = false,
showToolCalls = true,
}: {
startIdx: number;
endIdx: number;
Expand All @@ -644,9 +652,10 @@ function WarmTurnItems({
openAction: OpenTurnAction | null;
actionPending: boolean;
rewindDisabled: boolean;
onRewind: ((turn: number, scope: string) => void) | undefined;
onRewind?: (turn: number, scope: string) => void;
setOpenAction: (action: OpenTurnAction | null) => void;
defaultExpandThinking?: boolean;
showToolCalls?: boolean;
}) {
const nodes: React.ReactNode[] = [];
let actionText = "";
Expand Down Expand Up @@ -716,6 +725,7 @@ function WarmTurnItems({
if (it.parentId) break;
if (it.name === "todo_write") break;
if (it.name === "exit_plan_mode") break;
if (!showToolCalls) break;
nodes.push(<ToolCard key={it.id} item={it} subcalls={subcalls.get(it.id)} />);
break;
}
Expand Down
9 changes: 9 additions & 0 deletions desktop/frontend/src/lib/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export interface AppBindings {
SubmitToTab(tabID: string, input: string): Promise<void>;
SubmitDisplay(display: string, input: string): Promise<void>;
SubmitDisplayToTab(tabID: string, display: string, input: string): Promise<void>;
SubmitDisplayToTabWithRefs(tabID: string, display: string, input: string, refs: string): Promise<void>;
RunShell(command: string): Promise<void>;
RunShellForTab(tabID: string, command: string): Promise<void>;
Steer(text: string): Promise<void>;
Expand Down Expand Up @@ -224,6 +225,7 @@ export interface AppBindings {
TestBotConnection(id: string, target?: string): Promise<BotConnectionDiagnostic>;
SetCloseBehavior(mode: string): Promise<void>;
SetDisplayMode(mode: string): Promise<void>;
SetShowToolCalls(show: boolean): Promise<void>;
SetDesktopLanguage(lang: string): Promise<void>;
SetDesktopAppearance(theme: string, style: string): Promise<void>;
SetDesktopCheckUpdates(enabled: boolean): Promise<void>;
Expand Down Expand Up @@ -785,6 +787,7 @@ function makeMockApp(): AppBindings {
telemetry: true,
metrics: false,
expandThinking: false,
showToolCalls: true,
configPath: "~/projects/reasonix/reasonix.toml",
providerKinds: ["openai"],
autoApproveTools: false,
Expand Down Expand Up @@ -1375,6 +1378,9 @@ function makeMockApp(): AppBindings {
async SubmitDisplayToTab(_tabID, display, input) {
await withMockTabScope(_tabID, () => this.SubmitDisplay(display, input));
},
async SubmitDisplayToTabWithRefs(_tabID, display, input, _refs) {
await withMockTabScope(_tabID, () => this.SubmitDisplay(display, input));
},
async RunShell(command) {
cancelled = false;
emitMockTurnStarted();
Expand Down Expand Up @@ -2249,6 +2255,9 @@ function makeMockApp(): AppBindings {
async SetDisplayMode(mode: string) {
settings.displayMode = mode;
},
async SetShowToolCalls(show: boolean) {
settings.showToolCalls = Boolean(show);
},
async SetDesktopLanguage(lang: string) {
settings.desktopLanguage = lang === "en" || lang === "zh" ? lang : "";
},
Expand Down
1 change: 1 addition & 0 deletions desktop/frontend/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,7 @@ export interface SettingsView {
telemetry: boolean; // anonymous launch ping (install id + version + OS)
metrics: boolean; // opt-in aggregate agent metrics (anonymous signal/bucket counts)
expandThinking: boolean; // show reasoning text expanded by default
showToolCalls: boolean; // whether the transcript renders ToolCard blocks
configPath: string;
providerKinds: string[]; // provider implementations the kernel registered (for the kind picker)
autoApproveTools: boolean;
Expand Down
4 changes: 4 additions & 0 deletions desktop/frontend/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const en = {
"common.collapse": "Collapse",
"common.none": "none",
"common.auto": "auto",
"common.on": "On",
"common.off": "Off",
"common.busyHint": "Finish or stop the current turn first",
"common.loading": "Loading…",
"app.splashSubtitle": "Agent Workspace",
Expand Down Expand Up @@ -661,6 +663,8 @@ export const en = {
"settings.autoPlan": "Automatic plan mode",
"settings.autoPlan.off": "Off",
"settings.autoPlan.on": "On",
"settings.showToolCalls": "Show tool calls",
"settings.showToolCallsHint": "Render tool call and tool result blocks in the transcript. Off hides them; agent execution and history are unaffected.",
"settings.agentRuntime": "Agent runtime",
"settings.agentRuntimeHint": "Saved as user defaults. A project's ./reasonix.toml can override them; use 0 for no limit.",
"settings.executorMaxSteps": "Executor step limit",
Expand Down
4 changes: 4 additions & 0 deletions desktop/frontend/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export const zh: Record<DictKey, string> = {
"common.collapse": "收起",
"common.none": "无",
"common.auto": "自动",
"common.on": "开启",
"common.off": "关闭",
"common.busyHint": "请先完成或停止当前回合",
"common.loading": "加载中…",
"app.splashSubtitle": "Agent Workspace",
Expand Down Expand Up @@ -663,6 +665,8 @@ export const zh: Record<DictKey, string> = {
"settings.autoPlan": "自动计划模式",
"settings.autoPlan.off": "关闭",
"settings.autoPlan.on": "开启",
"settings.showToolCalls": "显示工具调用",
"settings.showToolCallsHint": "在对话中渲染工具调用与结果块。关闭后仅隐藏显示,不影响 agent 执行与历史。",
"settings.agentRuntime": "Agent 运行",
"settings.agentRuntimeHint": "保存为用户级默认值。项目的 ./reasonix.toml 可以覆盖;设为 0 表示不限。",
"settings.executorMaxSteps": "执行轮数上限",
Expand Down
10 changes: 10 additions & 0 deletions desktop/settings_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ type SettingsView struct {
Telemetry bool `json:"telemetry"`
Metrics bool `json:"metrics"`
ExpandThinking bool `json:"expandThinking"`
ShowToolCalls bool `json:"showToolCalls"`
ConfigPath string `json:"configPath"`
// ProviderKinds lists the provider implementations the kernel actually
// registered (provider.Kinds()), so the editor's "kind" picker offers only
Expand Down Expand Up @@ -333,6 +334,7 @@ func (a *App) Settings() SettingsView {
Telemetry: true,
Metrics: false,
ExpandThinking: false,
ShowToolCalls: true,
}
}
ctrl := a.activeCtrl()
Expand Down Expand Up @@ -381,6 +383,7 @@ func (a *App) Settings() SettingsView {
Telemetry: cfg.DesktopTelemetry(),
Metrics: cfg.DesktopMetrics(),
ExpandThinking: cfg.Desktop.ExpandThinking,
ShowToolCalls: cfg.DesktopShowToolCalls(),
ConfigPath: cfgPath,
ProviderKinds: nonNil(provider.Kinds()),
AutoApproveTools: ctrl != nil && ctrl.AutoApproveTools(),
Expand Down Expand Up @@ -1283,6 +1286,13 @@ func (a *App) SetDisplayMode(mode string) error {
return a.applyConfigOnly(func(c *config.Config) error { return c.SetDesktopDisplayMode(mode) })
}

// SetShowToolCalls toggles whether the desktop transcript renders ToolCard
// blocks. It is a UI-only preference and does not affect agent execution,
// tool invocation, message history, or provider-visible request data.
func (a *App) SetShowToolCalls(show bool) error {
return a.applyConfigOnly(func(c *config.Config) error { return c.SetDesktopShowToolCalls(show) })
}

// SetDesktopLanguage updates only the desktop UI language. It deliberately does
// not touch config.language, which the CLI/model-facing runtime uses.
func (a *App) SetDesktopLanguage(lang string) error {
Expand Down
11 changes: 11 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type DesktopConfig struct {
CheckUpdates *bool `toml:"check_updates"` // startup update checks; nil keeps the default enabled
Telemetry *bool `toml:"telemetry"` // anonymous launch ping (install id + version + OS); nil keeps the default enabled
Metrics *bool `toml:"metrics"` // opt-in aggregate agent metrics (anonymous signal/bucket counts; no content); nil = disabled
ShowToolCalls *bool `toml:"show_tool_calls"` // nil/true renders tool calls; false hides them. UI-only.
ProviderAccess []string `toml:"provider_access"` // desktop-only list of provider entries shown in Settings > Model > Access
ExpandThinking bool `toml:"expand_thinking"` // true = show reasoning text expanded by default; false = collapsed
}
Expand Down Expand Up @@ -234,6 +235,16 @@ func (c *Config) DesktopMetrics() bool {
return *c.Desktop.Metrics
}

// DesktopShowToolCalls reports whether the desktop transcript should render
// ToolCard blocks. Defaults to true; an explicit false hides them. UI-only —
// does not affect agent execution, tool invocation, or persisted messages.
func (c *Config) DesktopShowToolCalls() bool {
if c.Desktop.ShowToolCalls == nil {
return true
}
return *c.Desktop.ShowToolCalls
}

// LSPConfig governs the optional Language Server Protocol tools (lsp_definition,
// lsp_references, lsp_hover, lsp_diagnostics). Enabled defaults to true; the
// servers themselves are never bundled — each resolves on PATH and the tool
Expand Down
9 changes: 9 additions & 0 deletions internal/config/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,15 @@ func (c *Config) SetShowReasoning(on bool) error {
return nil
}

// SetDesktopShowToolCalls toggles the UI-only desktop transcript tool-call
// visibility. It is intentionally a desktop render concern and must not
// affect agent execution, tool invocation, or persisted messages.
func (c *Config) SetDesktopShowToolCalls(show bool) error {
v := show
c.Desktop.ShowToolCalls = &v
return nil
}

// SetProviderThinking updates a provider's provider-specific thinking mode knob.
func (c *Config) SetProviderThinking(name, thinking string) error {
for i := range c.Providers {
Expand Down
Loading
Loading