feat: add toolset switching UI (#115)

This commit is contained in:
Charles Packer
2025-11-23 20:02:04 -08:00
committed by GitHub
parent 836a488263
commit 592ed66e1b
4 changed files with 236 additions and 0 deletions

View File

@@ -45,6 +45,7 @@ import { ReasoningMessage } from "./components/ReasoningMessageRich";
import { SessionStats as SessionStatsComponent } from "./components/SessionStats";
// import { ToolCallMessage } from "./components/ToolCallMessage";
import { ToolCallMessage } from "./components/ToolCallMessageRich";
import { ToolsetSelector } from "./components/ToolsetSelector";
// import { UserMessage } from "./components/UserMessage";
import { UserMessage } from "./components/UserMessageRich";
import { WelcomeScreen } from "./components/WelcomeScreen";
@@ -204,6 +205,10 @@ export default function App({
// Model selector state
const [modelSelectorOpen, setModelSelectorOpen] = useState(false);
const [toolsetSelectorOpen, setToolsetSelectorOpen] = useState(false);
const [currentToolset, setCurrentToolset] = useState<
"codex" | "default" | null
>(null);
const [llmConfig, setLlmConfig] = useState<LlmConfig | null>(null);
const [agentName, setAgentName] = useState<string | null>(null);
@@ -788,6 +793,12 @@ export default function App({
return { submitted: true };
}
// Special handling for /toolset command - opens selector
if (msg.trim() === "/toolset") {
setToolsetSelectorOpen(true);
return { submitted: true };
}
// Special handling for /agent command - show agent link
if (msg.trim() === "/agent") {
const cmdId = uid("cmd");
@@ -1725,6 +1736,7 @@ export default function App({
selectedModel.handle ?? "",
agentId,
);
setCurrentToolset(toolsetName);
// Update the same command with final result (include toolset info)
const autoToolsetLine = toolsetName
@@ -1765,6 +1777,60 @@ export default function App({
[agentId, refreshDerived],
);
const handleToolsetSelect = useCallback(
async (toolsetId: "codex" | "default") => {
setToolsetSelectorOpen(false);
const cmdId = uid("cmd");
try {
// Immediately add command to transcript with "running" phase
buffersRef.current.byId.set(cmdId, {
kind: "command",
id: cmdId,
input: `/toolset ${toolsetId}`,
output: `Switching toolset to ${toolsetId}...`,
phase: "running",
});
buffersRef.current.order.push(cmdId);
refreshDerived();
// Lock input during async operation
setCommandRunning(true);
// Force switch to the selected toolset
const { forceToolsetSwitch } = await import("../tools/toolset");
await forceToolsetSwitch(toolsetId, agentId);
setCurrentToolset(toolsetId);
// Update the command with final result
buffersRef.current.byId.set(cmdId, {
kind: "command",
id: cmdId,
input: `/toolset ${toolsetId}`,
output: `Switched toolset to ${toolsetId}`,
phase: "finished",
success: true,
});
refreshDerived();
} catch (error) {
buffersRef.current.byId.set(cmdId, {
kind: "command",
id: cmdId,
input: `/toolset ${toolsetId}`,
output: `Failed to switch toolset: ${error instanceof Error ? error.message : String(error)}`,
phase: "finished",
success: false,
});
refreshDerived();
} finally {
// Unlock input
setCommandRunning(false);
}
},
[agentId, refreshDerived],
);
const handleAgentSelect = useCallback(
async (targetAgentId: string) => {
setAgentSelectorOpen(false);
@@ -2082,6 +2148,7 @@ export default function App({
!showExitStats &&
pendingApprovals.length === 0 &&
!modelSelectorOpen &&
!toolsetSelectorOpen &&
!agentSelectorOpen &&
!planApprovalPending
}
@@ -2108,6 +2175,15 @@ export default function App({
/>
)}
{/* Toolset Selector - conditionally mounted as overlay */}
{toolsetSelectorOpen && (
<ToolsetSelector
currentToolset={currentToolset ?? undefined}
onSelect={handleToolsetSelect}
onCancel={() => setToolsetSelectorOpen(false)}
/>
)}
{/* Agent Selector - conditionally mounted as overlay */}
{agentSelectorOpen && (
<AgentSelector