Terminal and side panel
The side panel's two built-in tabs - Chat and Terminal - how to choose which ship and which opens first, and what a Terminal session gives the user's own CLI.
The right side panel hosts two built-in surfaces. Chat is the conversational assistant (Chat). Terminal launches the user's OWN coding CLI (Claude Code, Codex, Grok or OpenCode) in an embedded terminal, in the app's work folder.
Terminal runs on the app's own agent runtime. The CLI runs on the user's machine in a real pty, driven by the runtime the app forks - nothing to install and nothing to pair, and the tab appears whenever config.desktop.agents.enabled is on. A session sees the same connected CLIs and audit log as Chat and Automations, and updating the app updates the runtime with it, so a session can never meet an older one.
Choose which tabs ship
Set config.desktop.agents.sidePanel to pick which tabs ship and which opens first. Both ship by default, with Chat first.
// packages/config/src/desktop.mjs
agents: {
// ...
sidePanel: { chat: true, terminal: true, default: "chat" }
}| Field | Type | Default | Effect |
|---|---|---|---|
chat | boolean | true | Whether the Chat tab ships. |
terminal | boolean | true | Whether the Terminal ships - the panel tab and its sidebar entry. |
default | "chat" | "terminal" | "chat" | Which tab opens the first time the panel is shown. |
- The sidebar's Terminal entry opens the panel on its Terminal tab (there is no separate page - one host per session). Sessions live outside the view, so closing or reopening the panel never stops a running CLI.
- One tab only: set the other
false- the panel renders as a single surface ({ chat: false, terminal: true, default: "terminal" }for a terminal-only app). - No panel: set both
false; the panel and its trigger disappear. - The user's choice wins:
defaultpicks the tab on first open only. After the user switches, their choice is remembered across restarts. Adefaultnaming a disabled tab falls back to the first available one. - The whole panel is gated on
config.desktop.agents.enabled: with AI off, neither tab nor the trigger renders, and the source still ships.
What the Terminal wires in
The app names only the CLI to run, the terminal's size, the starting model when your config pins one, and (optionally) a folder the user picked. The rest of the session is composed by the runtime on the user's machine:
- Any terminal-capable CLI: the picker offers the whole allowlist and launches any of them. Connecting a CLI on the Models screen is what puts it in the runtime's model catalog for Chat and Automations; a terminal session needs only the binary on the user's PATH, so an unconnected CLI is offered a one-click connect beside it rather than being withheld.
- A working folder: the project's work folder by default, its connected folder when one is granted, or whatever the user picks for the session.
- MCP servers: the ones the user added on this machine, plus your product's own MCP server when
config.mcpServeris enabled - so a CLI opened here reaches your product's tools, not only this device's. Claude Code and Codex sessions only: Grok and OpenCode take MCP servers through their config files, not their interactive commands, so those sessions run with the user's own CLI setup unchanged. - An audit entry: the session is recorded in the app's local audit log before the CLI starts.
Nothing extra is asked of the user: the CLI resolves its own login and configuration exactly as it does in a plain terminal.
Your MCP server is wired in as an endpoint, never a credential. The app hands the CLI the URL only; the CLI runs its own browser sign-in against it on first connect, exactly as it would for a server the user added by hand. Terminal sessions with Claude Code or Codex are the only place it is wired in - a chat turn or an automation runs with nobody present to complete that sign-in.
Starting model
config.ai.terminals.cli pins the model a session starts on, per CLI. Empty by default - each CLI picks its own default, on the user's own subscription.
// packages/config/src/index.ts
ai: {
terminals: {
cli: {
"claude-code": { model: "haiku" },
codex: { model: "gpt-5.6-luna" }
}
}
}| Entry | Effect |
|---|---|
[cliId]: { model } - that CLI's own model id | The session launches on that model (--model, or -m for Codex). |
| No entry | The CLI starts on its own default, exactly as in a plain terminal. |
- It seeds the launch only. The user switches model inside the TUI whenever they want.
- Separate from chat titles (
config.ai.titles.cli): naming is one throwaway call that should always be the cheapest model going, a session is the user's real work.
Working folder
A session runs in the active project's work folder - the same one that project's chat turns and automations use, so switching project switches the folder too. Only the folder follows the workspace: the CLI's trust posture and MCP servers stay machine-global. To work on a real project, the user picks one with Choose a folder..., which opens the OS's native dialog.
| Where a session starts | When |
|---|---|
| The project's managed work folder | The normal case |
| The project's connected folder | The user granted one for this project |
| The folder they just picked | An explicit Choose a folder..., which wins over both |
Picking the folder IS the consent, for this session. It holds until they pick another, and no backend can name it. A connected folder is the other kind of consent - durable, granted once on the Project screen - and a session refuses to open when that grant no longer resolves.
Approval prompts
A Terminal session always starts with the CLI's own approval prompts bypassed - it edits and runs without asking, and nothing keeps it in the folder it started in. The tab states this rather than offering a switch.
The containment guarantee elsewhere in these docs covers unattended runs - chat turns and automations the runtime drives with nobody watching. A Terminal session is the user at their own keyboard, and is deliberately not confined.
Chat
The desktop app's persisted chat - a multi-turn conversation on your hosted models or on the user's own coding CLI, as a full page and a side-panel tab.
Automated agents
On-device prompts that run on a cadence - the built-ins you ship, the ones end-users create, and how to keep them firing with the app closed.