Files
paseo/packages/app/src/command-center/agent-control-contributions.test.ts
T
35f985baff Switch reasoning, mode, plan, and fast from the Command Center (#2274)
* Switch reasoning, mode, plan, and fast from the Command Center

Extend the Command Center (Cmd-K) with selectors for a focused agent's
Thinking (reasoning effort), access Mode, Plan mode, and Fast mode,
mirroring the model switcher. Typing surfaces tiered "<Setting> › <Value>"
breadcrumb rows with icons and a check on the current value, for both a
running agent and a new draft tab. Rows only appear while typing, so the
default palette is unchanged.

- Thinking and Mode are multi-option selects; Plan mode and Fast mode are
  On/Off toggles.
- Plan mode is dual-natured: a permission mode for Claude/Copilot/OpenCode
  (excluded from the Mode list, given its own toggle) and the plan_mode
  feature for Codex. Fast mode drives the fast_mode feature (model-gated).
- All logic lives in command-center/agent-setting-contributions.ts as pure,
  unit-tested builders, with a small icon helper and a hook that registers
  the running-agent selectors; the draft registers from its composer tab.
  No changes to the command palette component and no protocol changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Unify Command Center agent controls

* refactor: clarify agent control ownership

* refactor(app): tighten command center agent controls

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
2026-07-31 17:37:04 +02:00

401 lines
12 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { AgentFeature, AgentMode, AgentSelectOption } from "@getpaseo/protocol/agent-types";
import type { ProviderSelectorProvider } from "@/provider-selection/provider-selection";
import type { CommandCenterContribution } from "./contributions";
import {
buildAgentControlContributions,
type AgentControlContributionSource,
} from "./agent-control-contributions";
function TestIcon() {
return null;
}
const LABELS: AgentControlContributionSource["labels"] = {
model: "Model",
thinking: "Thinking",
mode: "Mode",
planMode: "Plan mode",
fast: "Fast",
on: "On",
off: "Off",
modelKeywords: "model switch",
thinkingKeywords: "reasoning effort",
modeKeywords: "access permission",
planKeywords: "plan planning",
fastKeywords: "fast speed",
};
const ICONS: AgentControlContributionSource["icons"] = {
provider: () => TestIcon,
thinking: TestIcon,
planMode: TestIcon,
mode: () => TestIcon,
feature: () => TestIcon,
};
function provider(input: {
id: string;
label: string;
state?: "models" | "error";
}): ProviderSelectorProvider {
if (input.state === "error") {
return {
id: input.id,
label: input.label,
modelSelection: { kind: "error", message: "Unavailable" },
};
}
return {
id: input.id,
label: input.label,
modelSelection: {
kind: "models",
rows: [
{
favoriteKey: `${input.id}:shared`,
provider: input.id,
providerLabel: input.label,
modelId: "shared",
modelLabel: `${input.label} model`,
description: undefined,
},
],
},
};
}
function toggleFeature(id: string, value = false, icon?: string): AgentFeature {
return { type: "toggle", id, label: id, value, icon };
}
function makeSource(
overrides: {
provider?: string | null;
models?: Partial<AgentControlContributionSource["models"]>;
thinking?: Partial<AgentControlContributionSource["thinking"]>;
modes?: Partial<NonNullable<AgentControlContributionSource["modes"]>>;
features?: Partial<AgentControlContributionSource["features"]>;
} = {},
): AgentControlContributionSource {
return {
serverId: "host",
ownerKey: "agent",
provider: overrides.provider === undefined ? "claude" : overrides.provider,
labels: LABELS,
icons: ICONS,
models: {
providers: [],
selectedProvider: null,
selectedModelId: null,
select: () => undefined,
...overrides.models,
},
thinking: {
options: [],
selectedId: null,
select: () => undefined,
...overrides.thinking,
},
modes: {
options: [],
selectedId: null,
defaultModeId: null,
select: () => undefined,
...overrides.modes,
},
features: {
list: [],
set: () => undefined,
...overrides.features,
},
};
}
function selected(contribution: CommandCenterContribution): boolean {
return contribution.presentation.kind === "choice" ? contribution.presentation.selected : false;
}
const THINKING_OPTIONS: AgentSelectOption[] = [
{ id: "low", label: "Low" },
{ id: "high", label: "High" },
{ id: "xhigh", label: "Extra High" },
];
const CLAUDE_MODES: AgentMode[] = [
{ id: "plan", label: "Plan Mode" },
{ id: "default", label: "Always Ask" },
{ id: "acceptEdits", label: "Accept File Edits" },
];
describe("Command Center agent-control contributions", () => {
it("omits mode-derived choices when no mode control is available", () => {
const contributions = buildAgentControlContributions({
...makeSource({
models: { providers: [provider({ id: "claude", label: "Claude" })] },
features: { list: [toggleFeature("plan_mode", true)] },
}),
modes: undefined,
});
const groups = contributions.map((contribution) => contribution.group);
expect(groups).toContain("models");
expect(groups).toContain("plan-mode");
expect(groups).not.toContain("modes");
});
it("builds unique cross-provider model choices and skips unavailable or empty models", () => {
const selections: string[] = [];
const contributions = buildAgentControlContributions(
makeSource({
provider: null,
models: {
providers: [
provider({ id: "claude", label: "Claude" }),
provider({ id: "codex", label: "Codex" }),
provider({ id: "unavailable", label: "Unavailable", state: "error" }),
{
id: "empty",
label: "Empty",
modelSelection: {
kind: "models",
rows: [
{
favoriteKey: "empty:",
provider: "empty",
providerLabel: "Empty",
modelId: "",
modelLabel: "Default",
description: undefined,
},
],
},
},
],
selectedProvider: "codex",
selectedModelId: "shared",
select: (selectedProvider, modelId) => {
selections.push(`${selectedProvider}:${modelId}`);
},
},
}),
);
expect(
contributions.map((contribution) => ({
id: contribution.id,
selected: selected(contribution),
})),
).toEqual([
{ id: "models:claude:shared", selected: false },
{ id: "models:codex:shared", selected: true },
]);
expect(contributions[0].presentation).toMatchObject({
path: ["Model", "Claude", "Claude model"],
testId: "command-center-model-host:claude:shared",
});
contributions[0].run();
contributions[1].run();
expect(selections).toEqual(["claude:shared"]);
});
it("publishes thinking choices only when selection is meaningful", () => {
const selections: string[] = [];
const contributions = buildAgentControlContributions(
makeSource({
thinking: {
options: THINKING_OPTIONS,
selectedId: "high",
select: (id) => {
selections.push(id);
},
},
}),
).filter((contribution) => contribution.group === "thinking");
expect(contributions.map((contribution) => contribution.id)).toEqual([
"thinking:low",
"thinking:high",
"thinking:xhigh",
]);
expect(contributions.map(selected)).toEqual([false, true, false]);
for (const contribution of contributions) contribution.run();
expect(selections).toEqual(["low", "xhigh"]);
const singleOption = buildAgentControlContributions(
makeSource({
thinking: {
options: [{ id: "high", label: "High" }],
selectedId: "high",
},
}),
);
expect(singleOption.filter((contribution) => contribution.group === "thinking")).toEqual([]);
});
it("formats access modes, excludes planning modes, and marks the current mode", () => {
const selections: string[] = [];
const contributions = buildAgentControlContributions(
makeSource({
modes: {
options: CLAUDE_MODES,
selectedId: "default",
defaultModeId: "default",
select: (id) => {
selections.push(id);
},
},
}),
).filter((contribution) => contribution.group === "modes");
expect(
contributions.map((contribution) => ({
id: contribution.id,
path:
contribution.presentation.kind === "choice" ? contribution.presentation.path : undefined,
selected: selected(contribution),
})),
).toEqual([
{ id: "modes:default", path: ["Mode", "Always ask"], selected: true },
{ id: "modes:acceptEdits", path: ["Mode", "Accept file edits"], selected: false },
]);
contributions[1].run();
expect(selections).toEqual(["acceptEdits"]);
});
it("toggles mode-based Plan through the plan mode and provider default", () => {
const selections: string[] = [];
const contributions = buildAgentControlContributions(
makeSource({
modes: {
options: CLAUDE_MODES,
selectedId: "plan",
defaultModeId: "default",
select: (id) => {
selections.push(id);
},
},
}),
).filter((contribution) => contribution.group === "plan-mode");
expect(
contributions.map((contribution) => ({
id: contribution.id,
selected: selected(contribution),
})),
).toEqual([
{ id: "plan-mode:on", selected: true },
{ id: "plan-mode:off", selected: false },
]);
contributions[1].run();
expect(selections).toEqual(["default"]);
});
it("does not use a planning provider default as the Plan off target", () => {
const selections: string[] = [];
const contributions = buildAgentControlContributions(
makeSource({
modes: {
options: [
{ id: "research", label: "Research", colorTier: "planning" },
{ id: "default", label: "Default", colorTier: "safe" },
],
selectedId: "research",
defaultModeId: "research",
select: (id) => {
selections.push(id);
},
},
}),
).filter((contribution) => contribution.group === "plan-mode");
contributions[1].run();
expect(selections).toEqual(["default"]);
});
it("prefers the plan_mode feature and reads its canonical value", () => {
const calls: [string, unknown][] = [];
const contributions = buildAgentControlContributions(
makeSource({
modes: {
options: CLAUDE_MODES,
selectedId: "default",
defaultModeId: "default",
},
features: {
list: [toggleFeature("plan_mode", true, "list-todo")],
set: (id, value) => {
calls.push([id, value]);
},
},
}),
).filter((contribution) => contribution.group === "plan-mode");
expect(contributions.map(selected)).toEqual([true, false]);
contributions[1].run();
expect(calls).toEqual([["plan_mode", false]]);
});
it("publishes Fast only for an available toggle and ignores unrelated features", () => {
const calls: [string, unknown][] = [];
const contributions = buildAgentControlContributions(
makeSource({
features: {
list: [toggleFeature("fast_mode", false, "zap"), toggleFeature("auto_accept", true)],
set: (id, value) => {
calls.push([id, value]);
},
},
}),
);
const fast = contributions.filter((contribution) => contribution.group === "fast-mode");
expect(fast.map(selected)).toEqual([false, true]);
expect(contributions.some((contribution) => contribution.id.includes("auto_accept"))).toBe(
false,
);
fast[0].run();
expect(calls).toEqual([["fast_mode", true]]);
const unavailable = buildAgentControlContributions(makeSource());
expect(unavailable.filter((contribution) => contribution.group === "fast-mode")).toEqual([]);
});
it("centralizes query-only group ordering between built-in result sections", () => {
const contributions = buildAgentControlContributions(
makeSource({
models: {
providers: [provider({ id: "claude", label: "Claude" })],
selectedProvider: "claude",
selectedModelId: "shared",
},
thinking: { options: THINKING_OPTIONS, selectedId: "high" },
modes: {
options: CLAUDE_MODES,
selectedId: "default",
defaultModeId: "default",
},
features: { list: [toggleFeature("fast_mode")] },
}),
);
const ranks = new Map<string, number>();
for (const contribution of contributions) {
ranks.set(contribution.group, contribution.groupRank);
expect(contribution.visibility).toBe("query");
}
expect([...ranks.entries()]).toEqual([
["models", 1],
["thinking", 1.1],
["modes", 1.2],
["plan-mode", 1.3],
["fast-mode", 1.4],
]);
for (const rank of ranks.values()) expect(rank).toBeLessThan(2);
expect(new Set(contributions.map((contribution) => contribution.id)).size).toBe(
contributions.length,
);
});
});