magpie-agent-console 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 qingnian108
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,158 @@
1
- # Temporary Holding Version
1
+ # Agent Console
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A Magpie page plugin that drives real local Agent CLIs (Claude Code, Codex, Cursor, Copilot, OpenCode, Pi, Devin, dsh). Each conversation is a **persistent Agent session** on a standalone Node daemon. Close or refresh the page — the Agent keeps running while Magpie is open. Reopen the page and you pick up where you left off. When Magpie quits, the daemon and its Agents stop; threads are persisted and can be resumed next time.
4
+
5
+ ## Install
6
+
7
+ Requires [Magpie](https://usemagpie.ai) ≥ 0.1.1340 and Node.js ≥ 20.
8
+
9
+ ```bash
10
+ magpie plugin add magpie-agent-console
11
+ ```
12
+
13
+ Or in Magpie: **Settings → Plugins → Add plugin**, enter `magpie-agent-console`. After install, **Agent Console** appears in the sidebar plugin group.
14
+
15
+ Install the Agent CLIs you want to use (for example `claude` or `codex`). Missing CLIs show an install hint in Settings.
16
+
17
+ ---
18
+
19
+ This is not a gateway chat box. The daemon talks to the real CLIs through `@anthropic-ai/claude-agent-sdk`, `codex app-server`, and ACP, normalizes a structured event stream (tool calls, thinking, permission requests, usage), and pushes it to the React page over WebSocket.
20
+
21
+ UI principle: **obvious at a glance, nothing extra**. Three columns — sidebar (projects × threads), conversation timeline, and a right panel (changes / files / terminal).
22
+
23
+ ## Features
24
+
25
+ - **Persistent sessions**: each thread binds to a long-lived Agent process; after a daemon restart, running threads are marked closed and can be resumed (`--resume` / `thread/resume`)
26
+ - **Multiple Agents**: Claude Code, Codex, Cursor, Copilot, OpenCode, Pi, Devin, dsh; missing CLIs show an install hint
27
+ - **Model / permission / thinking**: per-thread; four permission modes and five thinking levels (what each Agent actually supports)
28
+ - **Structured timeline**: user message → thinking (expandable) → tool calls (read / edit / command, with line-level diffs) → todos → usage and duration; answer permission requests (tools / plan / questions) on the card
29
+ - **Steer and queue**: steer while running, queue the next message, interrupt
30
+ - **Changes panel**: git status refresh every 3s, unified per-file diffs; discarded untracked files go to trash instead of a hard delete
31
+ - **Files panel**: lazy directory tree (respects `.gitignore`), syntax-highlighted preview
32
+ - **Terminal panel**: real PTY via node-pty (pwsh / powershell); reconnects after reopening the page; degrades if PTY is unavailable
33
+ - **Import history**: import matching sessions from `~/.claude/projects` and `~/.codex/sessions` as new threads (resumable)
34
+ - **Thread management**: search (Ctrl+K), pin, rename, archive, delete (trash), export Markdown, thread info (usage / cost / session ID)
35
+ - **Mock Agent**: `AGENT_CONSOLE_MOCK=1` registers a demo Agent with 8 scripts (`default` / `perm` / `plan` / `ask` / `slow` / `error` / `long` / `exit`) so UI work does not burn tokens
36
+
37
+ ## Architecture
38
+
39
+ ```
40
+ Magpie window
41
+ └─ page/index.html (sandbox="allow-scripts" iframe, served by Magpie from /_magpie/plugin/<per-run id>/)
42
+ │ reads page/session.js {port, key} (gate key only, written by the plugin host)
43
+ │ navigates itself to http://127.0.0.1:<port>/open?k=<key>&theme=…
44
+ ▼
45
+ Magpie plugin host (Bun) ── index.mjs + lib/gate.mjs "gate" on 127.0.0.1:<port> (default 3488)
46
+ │ first /open: spawns the daemon as a child process (node, --port 0, stdin lifeline, --parent-pid)
47
+ │ 302 → http://127.0.0.1:<daemonPort>/?theme=…#t=<token>
48
+ ▼
49
+ daemon (Node, daemon/dist/main.js) ── HTTP + WebSocket on an ephemeral 127.0.0.1 port
50
+ └─ React app (app/) reads the token from location.hash, sends it as the x-agent-console header
51
+ and in the WebSocket hello
52
+ ```
53
+
54
+ ## Security model
55
+
56
+ The daemon can run Agents with full access to your machine, so the token that unlocks it must never be readable by another web page.
57
+
58
+ - **Two secrets per plugin-host run.** Each run generates two independent random 256-bit values: a **gate key** and the **daemon token**. Only the gate key is ever written to a file.
59
+ - **Why `session.js` holds only the gate key.** Magpie serves the page folder at `/_magpie/plugin/<per-run id>/`, where the id is an HMAC that changes every time Magpie starts and is handed out only to Magpie's own window. Magpie does not check `Origin`, `Host`, `Sec-Fetch-*` or a cookie on these files, and sends no `Cross-Origin-Resource-Policy`, so the unguessable path is their only protection. A classic `<script src>` include is not subject to CORS, so any page that learned the path (a different localhost port, a `file:` page, any website) could run `session.js` and read `globalThis.AgentConsoleSession`. That is why the file never contains the daemon token.
60
+ - **Token handoff.** After the gate binds its port, the host writes `{port, key}` to `page/session.js` (atomic write, mode 0600; git-ignored and excluded from the npm package). The page navigates to the gate's `/open?k=<key>`; the gate compares the key in constant time, starts or reuses the daemon and redirects to it with the daemon token **only in the URL fragment** (`#t=…`). The fragment is never sent to a server, written to a log, or leaked through `Referer`, and the window that ends up holding it belongs to the daemon's origin (an opaque origin inside Magpie's sandboxed frame), so the page that started the navigation cannot read it. A leaked gate key therefore lets another page at most open the console UI in a frame or tab it cannot read; it does not unlock the daemon's API or WebSocket. No HTML served by the daemon contains the token; an unauthenticated `GET /` returns a page with no secret in it.
61
+ - **`Origin: null` is not trusted.** Any site can produce `Origin: null` (sandboxed iframes, `data:` and `file:` pages). The token is the only authority:
62
+ - static routes (`/`, `app.js`, …) refuse `Origin: null` and foreign origins with 403 and never send CORS headers;
63
+ - token routes (`/api/*`, `/attachments/*`, WebSocket hello) accept `Origin: null` only as a way to *present* the token (the Magpie iframe has an opaque origin); `access-control-allow-origin: null` is emitted only on responses to requests that carried a valid token, plus the preflight for `/api/upload`; credentials are never allowed;
64
+ - any other origin gets 403 even with a valid token.
65
+ - **Host is checked on every request and WebSocket upgrade** (gate and daemon): it must be `127.0.0.1:<port>` or `localhost:<port>` with the actually bound port, otherwise 421. This blocks DNS rebinding, where the attacker's requests carry no `Origin` at all.
66
+ - The gate answers only `GET /open?k=<gate key>` (the daemon token is not accepted there), refuses any request that carries an `Origin` header, and sends `Cache-Control: no-store`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`.
67
+ - WebSocket connections must send `hello` with the token within 5 seconds, otherwise they are closed with code 4401.
68
+
69
+ ## Lifecycle
70
+
71
+ - Importing `index.mjs` has no side effects (no port, no process, no files).
72
+ - `server()` (called by Magpie's plugin host at host start-up — this includes short-lived hosts started by CLI commands such as `magpie plugin add` or `magpie plugin list`) binds the gate and writes `page/session.js`. If the port is busy it retries every 2 s in the background without blocking the host; a detached 0.1.0 daemon still holding the port is shut down once, only after it proves its identity by answering `/api/ping` with the token from `daemon.json`.
73
+ - The daemon starts when the page is opened for the first time and is reused for later opens. It is a normal (non-detached) child of the plugin host.
74
+ - The daemon exits when its stdin pipe closes or its parent pid disappears (checked every 2 s) — i.e. when Magpie quits or the plugin host restarts — and after 60 minutes idle.
75
+ - Magpie's plugin host treats the value returned by `server()` as OpenCode hooks and never calls `stop()`; it exits the Bun process when Magpie closes its stdin. A synchronous `exit` handler therefore closes the daemon's lifeline, signals it, and removes the `session.js` this host wrote. `stop()` (used by tests and other hosts) closes the gate, ends the daemon (graceful, then forced) and removes `session.js`.
76
+ - Agents stop with the daemon. Threads are persisted and can be resumed after reopening the page. On macOS/Linux every Agent CLI, the Codex app-server and the no-PTY shell run in their own process group, so stopping one ends everything it started; on Windows the same is done with `taskkill /T`. A process `exit` hook reaps any tree still alive when the daemon exits.
77
+
78
+ ## Platforms
79
+
80
+ The plugin runs wherever Magpie runs (Windows, macOS, Linux). It needs **Node.js 20+** on `PATH`: Magpie's plugin host is Bun, but the daemon and `node-pty` need Node. If Node is missing, opening the page shows "Node.js was not found" (the gate's 503 page) instead of silently running under Bun; a too-old Node gives a similar message.
81
+
82
+ - **Finding Agent CLIs.** `PATH` is scanned in JavaScript (no `where.exe`/`which`; `where.exe` prints in the console code page, which garbled Chinese user names). The first hit in `PATH` order wins. Common install dirs are also searched because a GUI app may start with a minimal `PATH` (Magpie adds the login-shell `PATH` itself; this is a fallback): Homebrew (`/opt/homebrew/bin`, `/usr/local/bin`), `~/.local/bin`, npm global, Volta, Bun, `~/.claude/local`, `%APPDATA%\npm`.
83
+ - **Windows shims.** `.exe` runs directly; npm `.cmd` shims run their `cli.js` with Node; `.ps1`-only shims run with `powershell -NoProfile -ExecutionPolicy Bypass -File`; any other `.cmd`/`.bat` runs through `cmd.exe` with cross-spawn-style escaping (spaces, `&`, `%`, quotes and Chinese in paths and arguments are safe).
84
+ - **Terminal.** `node-pty` prebuilds cover win32/darwin/linux × x64/arm64 (Linux prebuilds are glibc only). Default shell: Windows `pwsh` → `powershell` → `cmd`; macOS/Linux `$SHELL` if it exists, else `zsh` (macOS) / `bash` / `sh`. If `node-pty` cannot load, a simple non-PTY terminal is used. On macOS the exec bit of `node-pty`'s `spawn-helper` is restored if the package extraction dropped it.
85
+ - **Folder picker.** Windows: PowerShell `FolderBrowserDialog`; macOS: `osascript` "choose folder"; Linux: `zenity` or `kdialog` when a graphical session exists; otherwise type the path.
86
+ - **Paths.** Project paths are compared case-insensitively on Windows and macOS (and NFC-normalized on macOS); `realpath.native` resolves 8.3 short names and `subst` drives; UNC paths work. Discarding an untracked file moves it to the trash with a copy fallback when the project and data dir are on different drives.
87
+ - **Encoding.** Agent output is decoded as UTF-8; the folder picker falls back to GBK (CP936); the file preview tries UTF-8, then GBK. Git runs with `core.quotepath=false` so Chinese file names show as-is.
88
+ - **Magpie desktop window → `http://127.0.0.1`.** Magpie's window origin is `wails://` on macOS/Linux and `http://wails.localhost` on Windows. The plugin frame navigates itself to the gate on `http://127.0.0.1`, as Magpie's plugin-runtime docs describe for page plugins. On macOS, Magpie's `Info.plist` has no ATS keys and ATS exempts IP-address hosts by default, and the main window has no CSP that could block the frame, so the navigation is allowed. This is the one item that still needs a run on a real Mac.
89
+ - **Clickjacking / `frame-ancestors`.** Not set on purpose. The UI must load inside Magpie's frame on every mode: `wails://` (a non-special scheme whose origin WebKit may treat as opaque), `http://wails.localhost`, and `magpie web` on any address, LAN IP or reverse proxy. No `frame-ancestors` list covers all of them without breaking one. Framing the console also requires the gate key, which only Magpie's per-run page path hands out.
90
+
91
+ ## Reproducible build
92
+
93
+ `app/app.js`, `app/app.css`, `app/index.html`, `daemon/dist/main.js` and `daemon/dist/main.js.map` are committed build outputs of `web/src` and `daemon/src` (esbuild, options in `scripts/build-v1.mjs`). The build is deterministic: no timestamps, no absolute paths, LF line endings enforced by `.gitattributes`.
94
+
95
+ ```bash
96
+ npm ci
97
+ npm run build
98
+ git diff --exit-code app daemon/dist # no output = identical bytes
99
+ npm run verify:build # rebuilds into a temp dir, byte-compares, prints SHA-256
100
+ ```
101
+
102
+ `verify:build` prints `OK <sha256> <file>` for each bundle and exits non-zero on any mismatch.
103
+
104
+ ## Dependencies
105
+
106
+ All dependencies and devDependencies are pinned to exact versions, and `package-lock.json` matches them (`npm ci` succeeds). Runtime dependencies: `@agentclientprotocol/sdk`, `@anthropic-ai/claude-agent-sdk`, `@tanstack/react-virtual`, `@xterm/addon-fit`, `@xterm/xterm`, `clsx`, `dompurify`, `highlight.js`, `ignore`, `lucide-react`, `marked`, `node-pty`, `react`, `react-dom`, `ws`, `zod`, `zustand` (most of the UI packages are bundled into `app/app.js`; the daemon loads its packages from `node_modules`).
107
+
108
+ - `node-pty` is a native module and ships prebuilt binaries. Magpie installs plugins with install scripts disabled; if the binary cannot load, the terminal panel degrades to a simple non-PTY terminal.
109
+ - `@anthropic-ai/claude-agent-sdk` is distributed under Anthropic's commercial terms.
110
+
111
+ ## Data directory
112
+
113
+ ```
114
+ $XDG_CONFIG_HOME/magpie/agent-console/ (default ~/.config/magpie/agent-console on every OS;
115
+ on Windows ~ is %USERPROFILE%, the same root Magpie uses)
116
+ daemon.json pid/port/token/version of the running daemon (mode 0600)
117
+ settings.json settings (default Agent / permission / send key / panel widths…)
118
+ projects.json project list
119
+ threads/<id>/ meta.json + timeline.jsonl (event persistence, replay on reconnect)
120
+ attachments/ uploaded files
121
+ trash/ deleted threads and discarded new files (not hard-deleted)
122
+ logs/ daily rotating logs (kept 7 days); logs/daemon-spawn.log has the plugin host's view
123
+ ```
124
+
125
+ Magpie in portable mode keeps its own config next to the executable, but this plugin still uses the directory above (it follows `XDG_CONFIG_HOME`).
126
+
127
+ ## Development
128
+
129
+ ```bash
130
+ npm run build # daemon esm + web iife (app/ and daemon/dist are committed)
131
+ npm run verify:build # check committed bundles match a clean build
132
+ npm run dev # daemon on port 3499, data dir _tmp/xdg-dev (does not touch real data)
133
+ npm run check # typecheck both tsconfigs
134
+ npm test # vitest (test/v1 + web/src)
135
+ npm run e2e:v1 # e2e: spawn daemon(3497) + Edge/Chrome/Chromium, Mock Agent (MSEDGE_PATH / CHROME_PATH to override)
136
+ node scripts/verify-windows.mjs [--quick] # one-command platform self-check, see docs/VERIFY-windows.md
137
+ npm run shot:v1 # screenshot set (light/dark × scenes → _tmp/shots-v1/)
138
+
139
+ # Mock (no real tokens)
140
+ AGENT_CONSOLE_MOCK=1 npm run dev
141
+ # scripts: default /perm /plan /ask /slow /error /long (5000-event stress) /exit
142
+ ```
143
+
144
+ | Env | Purpose |
145
+ |---|---|
146
+ | `AGENT_CONSOLE_PORT` | gate port in the plugin / daemon port in standalone mode (3488; plugin `options.port` wins) |
147
+ | `AGENT_CONSOLE_MOCK=1` | register the mock provider |
148
+ | `AGENT_CONSOLE_TOKEN` | fixed page token (tests/screenshots only; do not set in production) |
149
+ | `AGENT_CONSOLE_IDLE_EXIT_MIN` | idle auto-exit minutes (default 60) |
150
+ | `XDG_CONFIG_HOME` | data root (`_tmp/xdg-dev` in development) |
151
+
152
+ Daemon CLI: `node daemon/dist/main.js [--port N|0] [--data DIR] [--token T] [--parent-pid PID]`. In standalone mode (no `--parent-pid`) it prints the URL to open, `http://127.0.0.1:<port>/#t=<token>`, to the console.
153
+
154
+ ## License
155
+
156
+ MIT (see LICENSE). Third-party licenses are in THIRD-PARTY-NOTICES.md: 16 runtime packages (mostly MIT; DOMPurify MPL-2.0/Apache-2.0, highlight.js BSD-3-Clause, lucide-react ISC; `@anthropic-ai/claude-agent-sdk` is Anthropic's commercial terms). Build/test: esbuild, vitest, TypeScript, playwright-core.
157
+
158
+ Architecture takes cues from Paseo (persistent daemon + provider abstraction) and T3 Code (timeline shape and three-column layout) — ideas only, no copied code.
@@ -0,0 +1,44 @@
1
+ # Third-Party Notices
2
+
3
+ ## Design references
4
+
5
+ The persistent-session architecture, event protocol and UI layout of this plugin were designed
6
+ with reference to the following open-source projects (only design ideas and protocol shapes were
7
+ referenced; no source code was copied):
8
+
9
+ - **Paseo** (https://github.com/getpaseo/paseo)
10
+ — persistent daemon + WebSocket event push, and the provider session abstraction.
11
+ - **T3 Code** (https://github.com/pingdotgg/t3code, MIT)
12
+ — timeline item shapes (tool groups, permission cards, plan cards) and the three-column workbench layout.
13
+
14
+ ## npm dependencies and their licenses
15
+
16
+ Runtime dependencies (`dependencies`):
17
+
18
+ | Package | Version | License |
19
+ |---|---|---|
20
+ | @agentclientprotocol/sdk | 2.1.0 | Apache-2.0 |
21
+ | @anthropic-ai/claude-agent-sdk | 0.3.312 | SEE LICENSE IN README.md (Anthropic commercial terms) |
22
+ | @tanstack/react-virtual | 3.14.24 | MIT |
23
+ | @xterm/addon-fit | 0.11.0 | MIT |
24
+ | @xterm/xterm | 6.0.0 | MIT |
25
+ | clsx | 2.1.1 | MIT |
26
+ | dompurify | 3.4.17 | MPL-2.0 OR Apache-2.0 |
27
+ | highlight.js | 11.12.0 | BSD-3-Clause |
28
+ | ignore | 7.0.14 | MIT |
29
+ | lucide-react | 1.64.0 | ISC |
30
+ | marked | 18.1.2 | MIT |
31
+ | node-pty | 1.2.0-beta.15 | MIT |
32
+ | react | 19.3.1 | MIT |
33
+ | react-dom | 19.3.1 | MIT |
34
+ | ws | 8.22.0 | MIT |
35
+ | zod | 4.6.7 | MIT |
36
+ | zustand | 5.0.16 | MIT |
37
+
38
+ Development dependencies (`devDependencies`; nothing from them ships except the built output):
39
+ typescript (Apache-2.0), esbuild (MIT), vitest (MIT), playwright-core (Apache-2.0),
40
+ @types/* (MIT).
41
+
42
+ The Codex app-server JSON-RPC schema files (`daemon/src/providers/codex/schema/`) are generated
43
+ locally by `codex app-server generate-json-schema`. They describe the OpenAI Codex CLI interface
44
+ and are used under the Codex license; the plugin uses them only for protocol integration.
package/app/app.css ADDED
@@ -0,0 +1,37 @@
1
+ :root{--bg: var(--magpie-bg, #ffffff);--fg: var(--magpie-fg, #1f1f1f);--muted: var(--magpie-muted, #737373);--accent: var(--magpie-accent, #0d9488);--accent-fg: #ffffff;--accent-soft: color-mix(in oklab, var(--accent) 12%, var(--bg));--surface: color-mix(in oklab, var(--fg) 3%, var(--bg));--surface-hover: color-mix(in oklab, var(--fg) 6%, var(--bg));--line: color-mix(in oklab, var(--fg) 9%, var(--bg));--ok: #16a34a;--warn: #d97706;--danger: #dc2626;--info: #2563eb;--code-bg: color-mix(in oklab, var(--fg) 4%, var(--bg));--r-s: 6px;--r-m: 8px;--r-l: 12px;--r-full: 999px;--font-ui: var(--magpie-font, "Segoe UI Variable Text", "Segoe UI", -apple-system, "PingFang SC", "Microsoft YaHei UI", system-ui, sans-serif);--font-mono: "Cascadia Code", "Cascadia Mono", "JetBrains Mono", ui-monospace, Consolas, monospace;--fs-s: 12px;--fs-m: 13px;--fs-l: 14px;--fs-hero: 18px;--lh: 1.6;--shadow-pop: 0 8px 24px -6px rgb(0 0 0 / .16), 0 0 0 1px var(--line);--term-bg: #ffffff;--term-fg: #1e1e1e;--ease: cubic-bezier(.2,.7,.2,1);--dur-1: .12s;--dur-2: .2s}@media(prefers-color-scheme:dark){:root:not([data-theme=light]){--bg: var(--magpie-bg, #1a1a1a);--fg: var(--magpie-fg, #ececec);--muted: var(--magpie-muted, #9b9b9b);--accent: var(--magpie-accent, #2dd4bf);--accent-fg: #04201c;--info: #60a5fa;--shadow-pop: 0 8px 24px -6px rgb(0 0 0 / .5), 0 0 0 1px var(--line);--term-bg: #0c0c0c;--term-fg: #cccccc}}[data-theme=dark]{--bg: var(--magpie-bg, #1a1a1a);--fg: var(--magpie-fg, #ececec);--muted: var(--magpie-muted, #9b9b9b);--accent: var(--magpie-accent, #2dd4bf);--accent-fg: #04201c;--info: #60a5fa;--shadow-pop: 0 8px 24px -6px rgb(0 0 0 / .5), 0 0 0 1px var(--line);--term-bg: #0c0c0c;--term-fg: #cccccc}@layer reset{html,body{margin:0;padding:0;height:100%}body{background:var(--bg);color:var(--fg);font-family:var(--font-ui);font-size:var(--fs-m);line-height:var(--lh);-webkit-font-smoothing:antialiased}*{box-sizing:border-box}#root{height:100%}button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer}input,textarea,select{font:inherit;color:inherit}a{color:var(--accent)}:focus-visible{outline:2px solid color-mix(in oklab,var(--accent) 55%,var(--bg));outline-offset:-1px}}*{scrollbar-width:thin;scrollbar-color:color-mix(in oklab,var(--fg) 18%,transparent) transparent}*::-webkit-scrollbar{width:8px;height:8px}*::-webkit-scrollbar-thumb{background:color-mix(in oklab,var(--fg) 18%,transparent);border-radius:4px}*::-webkit-scrollbar-thumb:hover{background:color-mix(in oklab,var(--fg) 30%,transparent)}*::-webkit-scrollbar-track{background:transparent}.ibtn{width:28px;height:28px;flex:none;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--r-s);color:var(--muted);transition:background var(--dur-1),color var(--dur-1)}.ibtn:hover{background:var(--surface-hover);color:var(--fg)}.ibtn[disabled]{opacity:.35;cursor:default}.ibtn.on{color:var(--accent)}.ibtn svg{width:16px;height:16px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;height:30px;padding:0 14px;border-radius:var(--r-s);font-size:var(--fs-m);background:var(--surface-hover);color:var(--fg);transition:background var(--dur-1),filter var(--dur-1)}.btn:hover{filter:brightness(1.03)}.btn[disabled]{opacity:.4;cursor:default}.btn.primary{background:var(--accent);color:var(--accent-fg)}.btn.primary:hover{filter:brightness(1.08)}.btn.danger{color:var(--danger)}.btn.ghost{background:none}.btn.ghost:hover{background:var(--surface-hover)}.tinput{width:100%;height:30px;padding:0 10px;border:1px solid var(--line);border-radius:var(--r-s);background:var(--bg);font-size:var(--fs-m);outline:none}.tinput:focus{border-color:color-mix(in oklab,var(--accent) 55%,var(--bg))}.tinput.err{border-color:var(--danger)}.tinput::placeholder{color:var(--muted)}textarea.tinput{height:auto;min-height:60px;padding:6px 10px;resize:vertical}select.tinput{padding-right:26px;appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),linear-gradient(135deg,var(--muted) 50%,transparent 50%);background-position:calc(100% - 14px) 50%,calc(100% - 10px) 50%;background-size:4px 4px;background-repeat:no-repeat}.menu{position:fixed;z-index:60;min-width:160px;max-width:280px;background:var(--bg);border-radius:var(--r-m);box-shadow:var(--shadow-pop);padding:4px;font-size:var(--fs-m)}.mi{display:flex;align-items:center;gap:8px;width:100%;height:28px;padding:0 10px;border-radius:var(--r-s);text-align:left;color:var(--fg)}.mi:hover:not([disabled]){background:var(--surface-hover)}.mi[disabled]{color:var(--muted);cursor:default}.mi.danger{color:var(--danger)}.mi .mi-check{margin-left:auto;color:var(--accent)}.msep{height:1px;margin:4px 6px;background:var(--line)}.mlabel{padding:6px 10px 2px;font-size:var(--fs-s);color:var(--muted)}.dlg-scrim{position:fixed;inset:0;z-index:70;background:#00000052;display:flex;align-items:center;justify-content:center;padding:24px}.dlg{width:440px;max-width:100%;max-height:80vh;display:flex;flex-direction:column;background:var(--bg);border-radius:var(--r-l);box-shadow:var(--shadow-pop)}.dlg-title{flex:none;padding:14px 16px 4px;font-size:var(--fs-l);font-weight:600}.dlg-body{flex:1;min-height:0;overflow-y:auto;padding:8px 16px}.dlg-foot{flex:none;display:flex;justify-content:flex-end;gap:8px;padding:10px 16px 14px}.dlg-row{margin:10px 0}.dlg-row-top{align-items:flex-start}.dlg-row>label{display:block;margin-bottom:4px;font-size:var(--fs-s);color:var(--muted)}.provider-list{display:flex;flex-direction:column;gap:6px;width:100%;max-height:280px;overflow:auto}.provider-list{display:flex;flex-direction:column;gap:6px;flex:1;min-width:0}.provider-redetect{align-self:flex-start;margin-bottom:4px}.provider-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px;padding:6px 8px;border-radius:var(--r-m);background:var(--bg-2)}.provider-row.open{padding-bottom:10px}.provider-head{display:flex;align-items:center;gap:10px;flex:1;min-width:0;border:0;background:none;text-align:left;cursor:pointer;color:inherit;padding:0}.provider-detail{flex:1 1 100%;display:flex;flex-direction:column;gap:6px;padding:4px 0 0 28px}.provider-hint-row{display:flex;gap:8px;align-items:flex-start;flex-wrap:wrap}.btn.sm{font-size:var(--fs-s);padding:4px 10px;height:auto}.provider-meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.provider-name{font-size:var(--fs-m);font-weight:500}.provider-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.provider-dot-on{background:var(--ok, #3ecf8e)}.provider-dot-off{background:var(--muted);opacity:.5}.provider-dot-pending{background:var(--warn, #e6a817)}.provider-switch input{width:16px;height:16px;cursor:pointer}.dlg-hint{font-size:var(--fs-s);color:var(--muted)}.dlg-hint.err{color:var(--danger)}.toasts{position:fixed;left:50%;bottom:24px;transform:translate(-50%);z-index:80;display:flex;flex-direction:column;gap:8px;align-items:center}.toast{max-width:480px;padding:8px 14px;border-radius:var(--r-m);background:var(--fg);color:var(--bg);font-size:var(--fs-m);box-shadow:var(--shadow-pop);animation:toast-in var(--dur-2) var(--ease)}.toast.err{background:var(--danger);color:#fff}@keyframes toast-in{0%{opacity:0;transform:translateY(8px)}}.dot{width:6px;height:6px;border-radius:50%;flex:none;background:var(--muted)}.dot.run{background:var(--accent);animation:breathe 1.6s infinite}.dot.err{background:var(--danger)}.dot.wait{background:var(--warn);animation:breathe 1.6s infinite}@keyframes breathe{50%{opacity:.35}}.boot-screen{height:100%;display:flex;align-items:center;justify-content:center;gap:8px;color:var(--muted);font-size:var(--fs-m)}.boot-screen .btn{margin-top:12px}.boot-wrap{display:flex;flex-direction:column;align-items:center}.boot-dot{width:8px;height:8px;border-radius:50%;background:var(--muted)}.boot-dot[data-state=open]{background:var(--ok)}.boot-dot[data-state=reconnecting]{background:var(--warn)}.boot-dot[data-state=down]{background:var(--danger)}.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.app{display:flex;height:100vh;min-width:480px;overflow:hidden}.sidebar{width:var(--sb-w, 248px);flex:none;display:flex;flex-direction:column;background:var(--surface);border-right:1px solid var(--line)}.sb-row{display:flex;align-items:center;gap:2px;height:40px;padding:6px 6px 4px;flex:none}.sb-new{flex:1;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px;border-radius:var(--r-s);font-size:var(--fs-m);text-align:left;transition:background var(--dur-1)}.sb-new:hover{background:var(--surface-hover)}.sb-row .ibtn{width:28px;height:28px}.sb-search{flex:1;display:flex;align-items:center;gap:6px;height:28px;padding:0 8px;border-radius:var(--r-s);background:var(--surface-hover);color:var(--muted)}.sb-search input{flex:1;min-width:0;border:0;background:none;font-size:var(--fs-s);outline:none;color:var(--fg)}.sb-list{flex:1;overflow-y:auto;padding:0 6px 8px}.pj-group{margin-top:2px}.pj{display:flex;align-items:center;gap:2px;height:30px;padding:0 4px 0 0;border-radius:var(--r-s)}.pj:hover{background:var(--surface-hover)}.pj-toggle{flex:1;min-width:0;height:100%;display:flex;align-items:center;gap:6px;padding-left:2px;text-align:left;font-size:var(--fs-m);font-weight:500}.pj-caret{width:14px;flex:none;display:inline-flex;justify-content:center;color:var(--muted);transition:transform var(--dur-1)}.pj-caret.open{transform:rotate(90deg)}.pj-badge{width:20px;height:20px;flex:none;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;background:var(--surface-hover);color:var(--fg);font-size:var(--fs-s);font-weight:600;line-height:1;text-transform:uppercase}.pj:hover .pj-badge{background:var(--bg)}.pj-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pj-count{flex:none;padding:0 6px;font-size:var(--fs-s);color:var(--muted)}.pj-act{width:22px;height:22px;flex:none;display:none;align-items:center;justify-content:center;border-radius:var(--r-s);color:var(--muted)}.pj:hover .pj-act,.pj-act:focus-visible{display:inline-flex}.pj:hover .pj-count{display:none}.pj-act:hover{background:var(--bg);color:var(--fg)}.pj-sessions{padding:1px 0 4px 22px}.pj-more,.pj-empty{display:flex;align-items:center;width:100%;height:26px;padding:0 8px 0 21px;font-size:var(--fs-s);color:var(--muted);text-align:left;border-radius:var(--r-s)}.pj-more:hover{background:var(--surface-hover);color:var(--fg)}.t-item{display:flex;align-items:center;gap:8px;width:100%;height:28px;padding:0 4px 0 8px;border-radius:var(--r-s);position:relative;text-align:left;font-size:var(--fs-m);transition:background var(--dur-1)}.t-item:hover,.t-item.on{background:var(--surface-hover)}.t-item.archived{opacity:.55}.t-item.archived .t-title{font-style:italic}.t-item .dot{width:5px;height:5px}.t-pin{width:12px;flex:none;display:inline-flex;color:var(--muted)}.t-pin svg{width:10px;height:10px}.t-title{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.t-time{flex:none;font-size:var(--fs-s);color:var(--muted)}.t-item:hover .t-time{display:none}.t-more{flex:none;width:22px;height:22px;display:none;align-items:center;justify-content:center;border-radius:var(--r-s);color:var(--muted)}.t-item:hover .t-more{display:inline-flex}.t-more:hover{background:var(--bg);color:var(--fg)}.t-rename{flex:1;min-width:0;height:24px;font-size:var(--fs-m);border:1px solid var(--accent);border-radius:var(--r-s);padding:0 6px;background:var(--bg)}.sb-empty{padding:20px 12px;text-align:center;color:var(--muted);font-size:var(--fs-s)}.sb-start{padding:28px 12px;text-align:center}.sb-start-t{font-size:var(--fs-m);font-weight:500}.sb-start-s{margin:4px 0 12px;font-size:var(--fs-s);color:var(--muted)}.rs{width:5px;flex:none;cursor:col-resize;position:relative}.rs:after{content:"";position:absolute;inset:0}.rs:hover:after,.rs.drag:after{background:color-mix(in oklab,var(--accent) 30%,transparent)}.rs-l{margin-left:-3px}.rs-r{margin-right:-3px}body.col-drag{cursor:col-resize;user-select:none}body.col-drag iframe,body.col-drag .xterm{pointer-events:none}.scrim{position:fixed;inset:0;z-index:45;background:#00000052;display:none}.scrim.show{display:block}.main{flex:1;min-width:0;display:flex;flex-direction:column;background:var(--bg);position:relative}.topbar{height:40px;flex:none;display:flex;align-items:center;gap:6px;padding:0 10px;border-bottom:1px solid var(--line)}.tb-menu-btn{display:none}.tb-crumb{flex:none;max-width:26%;font-size:var(--fs-m);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tb-sep{flex:none;color:var(--muted)}.tb-title{font-size:var(--fs-l);font-weight:500;max-width:40%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-radius:var(--r-s);padding:2px 6px;cursor:text}.tb-title:hover{background:var(--surface)}.tb-title.ghost{color:var(--muted);cursor:default}.tb-title.ghost:hover{background:none}.tb-rename{font-size:var(--fs-l);font-weight:500;border:1px solid var(--accent);border-radius:var(--r-s);padding:1px 6px;background:var(--bg);width:300px;max-width:46vw}.tb-pill{flex:none;display:inline-flex;align-items:center;gap:6px;height:22px;padding:0 9px;border-radius:var(--r-full);font-size:var(--fs-s);background:var(--surface);color:var(--muted)}.tb-pill.run{color:var(--accent)}.tb-pill.wait{color:var(--warn)}.tb-pill.err{color:var(--danger)}.tb-right{margin-left:auto;display:flex;align-items:center;gap:2px}.tb-model{font-size:var(--fs-s);color:var(--muted);margin-right:4px;white-space:nowrap;max-width:160px;overflow:hidden;text-overflow:ellipsis}.connbar{flex:none;height:26px;display:flex;align-items:center;justify-content:center;font-size:var(--fs-s);background:color-mix(in oklab,var(--warn) 14%,var(--bg));color:var(--warn)}.scroll{flex:1;overflow-y:auto;overscroll-behavior:contain;position:relative}.tl-wrap{max-width:760px;min-width:0;margin:0 auto;padding:16px 20px 12px}.jump{position:absolute;right:24px;bottom:150px;width:34px;height:34px;border-radius:50%;background:var(--bg);box-shadow:var(--shadow-pop);display:flex;align-items:center;justify-content:center;color:var(--muted);z-index:5}.jump:hover{color:var(--fg)}.jump .jd{position:absolute;top:4px;right:4px;width:7px;height:7px;border-radius:50%;background:var(--accent)}.panel{width:var(--pn-w, 440px);flex:none;display:flex;flex-direction:column;background:var(--bg);border-left:1px solid var(--line)}.pn-head{flex:none;display:flex;align-items:center;gap:2px;height:40px;padding:0 6px;border-bottom:1px solid var(--line)}.pn-tabs{display:flex;gap:2px}.pn-tab{height:28px;padding:0 10px;border-radius:var(--r-s);font-size:var(--fs-m);color:var(--muted);display:inline-flex;align-items:center;gap:6px}.pn-tab:hover{background:var(--surface-hover);color:var(--fg)}.pn-tab.on{background:var(--surface-hover);color:var(--fg);font-weight:500}.pn-body{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.pn-close{margin-left:auto}.pn-empty{padding:20px;color:var(--muted);font-size:var(--fs-m);text-align:center}.pn-empty-sub{margin-top:6px;font-size:var(--fs-s);opacity:.8}.chg-head{flex:none;display:flex;align-items:center;gap:6px;height:34px;padding:0 8px;border-bottom:1px solid var(--line);font-size:var(--fs-m)}.chg-branch{display:inline-flex;align-items:center;gap:5px;color:var(--muted);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chg-head .ibtn{margin-left:auto}.chg-list{flex:none;max-height:40%;overflow-y:auto;border-bottom:1px solid var(--line)}.chg-list:empty{border-bottom:none}.chg-row{display:flex;align-items:center;gap:7px;padding:4px 8px;cursor:pointer;font-size:var(--fs-m);min-height:28px}.chg-row:hover,.chg-row.on{background:var(--surface-hover)}.chg-kind{flex:none;width:18px;height:18px;border-radius:var(--r-s);font-size:10px;display:inline-flex;align-items:center;justify-content:center;background:var(--surface);color:var(--muted)}.chg-kind.k-modified{color:var(--warn, #b45309)}.chg-kind.k-added,.chg-kind.k-untracked{color:var(--ok, #15803d)}.chg-kind.k-deleted{color:var(--err, #b91c1c)}.chg-kind.k-renamed{color:var(--accent)}.chg-path{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;direction:rtl;text-align:left}.chg-stat{flex:none;font-style:normal;font-size:var(--fs-s);font-variant-numeric:tabular-nums}.chg-stat .add{color:var(--ok, #15803d);font-style:normal}.chg-stat .del{color:var(--err, #b91c1c);font-style:normal}.chg-discard{flex:none;opacity:0}.chg-row:hover .chg-discard{opacity:1}.chg-diff{flex:1;min-height:0;overflow:auto}.ft-crumbs{flex:none;display:flex;align-items:center;gap:4px;min-height:34px;padding:0 8px;border-bottom:1px solid var(--line);font-size:var(--fs-m);overflow-x:auto;white-space:nowrap}.ft-crumb{color:var(--muted);font-size:var(--fs-m);padding:2px 4px;border-radius:var(--r-s)}.ft-crumb:hover{background:var(--surface-hover);color:var(--fg)}.ft-crumb-t{color:var(--fg)}.ft-crumb-t.muted{color:var(--muted)}.ft-tree{flex:none;max-height:45%;overflow-y:auto;padding:4px 0}.ft-row{display:flex;align-items:center;gap:5px;padding:3px 8px;cursor:pointer;font-size:var(--fs-m);min-height:24px}.ft-row:hover,.ft-row.on{background:var(--surface-hover)}.ft-row .caret{font-size:11px;color:var(--muted);display:inline-block;transition:transform var(--dur-1) var(--ease)}.ft-row .caret.open{transform:rotate(90deg)}.ft-dot{width:13px;flex:none}.ft-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ft-err{color:var(--danger);font-size:var(--fs-s);padding:2px 0}.ft-view{flex:1;min-height:0;overflow:auto;border-top:1px solid var(--line)}.ft-pre{margin:0;padding:10px 12px;font-family:var(--font-mono);font-size:var(--fs-s);line-height:1.55}.ft-pre code{background:none;padding:0;display:block;white-space:pre}.tm-tabs{flex:none;display:flex;align-items:center;gap:2px;padding:4px 6px;border-bottom:1px solid var(--line);overflow-x:auto}.tm-tab{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 6px 0 10px;border-radius:var(--r-s);font-size:var(--fs-m);color:var(--muted);white-space:nowrap}.tm-tab:hover,.tm-tab.on{background:var(--surface-hover);color:var(--fg)}.tm-x{color:var(--muted);padding:0 3px;border-radius:3px}.tm-x:hover{color:var(--danger);background:var(--surface-hover)}.tm-note{flex:none;padding:4px 10px;font-size:var(--fs-s);color:var(--warn);background:color-mix(in oklab,var(--warn) 10%,var(--bg));border-bottom:1px solid var(--line)}.tm-body{flex:1;min-height:0;position:relative;background:var(--term-bg)}.tm-view{position:absolute;inset:0;padding:6px 8px;background:var(--term-bg)}.tm-view .xterm{height:100%}.tm-view .xterm-viewport{background:var(--term-bg)!important}.tm-view .xterm-screen{padding:2px 0}.imp-list{max-height:320px;overflow-y:auto;display:flex;flex-direction:column;gap:2px}.imp-row{display:flex;align-items:center;gap:8px;padding:5px 6px;border-radius:var(--r-s);font-size:var(--fs-m);cursor:pointer}.imp-row:hover{background:var(--surface-hover)}.imp-row.done{opacity:.55;cursor:default}.imp-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.imp-meta{flex:none;color:var(--muted);font-size:var(--fs-s)}.info-grid{display:flex;flex-direction:column;gap:2px}.info-row{display:flex;align-items:center;gap:10px;padding:3px 2px;font-size:var(--fs-m)}.info-k{flex:none;width:74px;color:var(--muted)}.info-v{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-mono);font-size:var(--fs-s)}@media(min-width:760px){.sidebar.w-hide{display:none}.sidebar.w-hide+.rs-l{display:none}}@media(min-width:900px){.panel.w-hide{display:none}}@media(max-width:899px){.panel{position:fixed;right:0;top:0;bottom:0;z-index:46;width:min(var(--pn-w, 440px),92vw);transform:translate(100%);transition:transform var(--dur-2) var(--ease);box-shadow:var(--shadow-pop)}.panel.open{transform:none}.rs-r{display:none}}@media(max-width:759px){.sidebar{position:fixed;left:0;top:0;bottom:0;z-index:46;width:min(var(--sb-w, 248px),84vw);transform:translate(-100%);transition:transform var(--dur-2) var(--ease);box-shadow:var(--shadow-pop)}.sidebar.open{transform:none}.sidebar~.rs-l{display:none}.tb-menu-btn{display:inline-flex}.tl-wrap{padding:16px 14px 8px}.tb-model{display:none}}.dlg.cpal{width:520px;padding:6px}.cpal-in{width:100%;height:36px;padding:0 12px;border:0;background:none;outline:none;font-size:var(--fs-l);color:var(--fg)}.cpal-list{max-height:46vh;overflow-y:auto;padding:4px}.cpal-item{display:flex;align-items:center;gap:8px;width:100%;height:30px;padding:0 10px;border-radius:var(--r-s);text-align:left;font-size:var(--fs-m)}.cpal-item.on{background:var(--surface-hover)}.cpal-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cpal-hint{flex:none;font-size:var(--fs-s);color:var(--muted)}.um-row{display:flex;flex-direction:column;align-items:flex-end;margin:10px 0}.um{position:relative;max-width:78%;min-width:min(100%,12em);background:var(--accent-soft);border-radius:var(--r-l) var(--r-l) 4px var(--r-l);padding:7px 12px;white-space:pre-wrap;word-break:normal;overflow-wrap:break-word}.um:hover .um-act{display:flex}.um-act{position:absolute;left:-62px;top:4px;display:none;gap:2px}.um-act .ibtn{width:24px;height:24px}.um-img{display:block;max-width:200px;max-height:140px;border-radius:var(--r-s);margin-bottom:6px;cursor:zoom-in}.um-tag{font-size:var(--fs-s);color:var(--muted);margin-top:2px}.as{margin:10px 0}.md.streaming:after{content:"\258d";color:var(--accent);animation:breathe 1s infinite}.md{font-size:var(--fs-m);word-break:normal;overflow-wrap:break-word}.md p{margin:6px 0}.md pre{position:relative;background:var(--code-bg);border-radius:var(--r-s);padding:10px 12px;overflow-x:auto;font-family:var(--font-mono);font-size:var(--fs-s);line-height:1.55}.md code{font-family:var(--font-mono);font-size:.92em;background:var(--code-bg);padding:1px 5px;border-radius:4px}.md pre code{background:none;padding:0}.md ul,.md ol{margin:6px 0;padding-left:22px}.md h1,.md h2,.md h3,.md h4{margin:12px 0 6px;font-size:var(--fs-l);font-weight:600}.md blockquote{margin:6px 0;padding-left:12px;border-left:3px solid var(--line);color:var(--muted)}.md table{border-collapse:collapse;margin:8px 0}.md th,.md td{border:1px solid var(--line);padding:4px 10px;font-size:var(--fs-s)}.cb-copy{position:absolute;top:6px;right:6px;font-size:var(--fs-s);color:var(--muted);padding:2px 8px;border-radius:var(--r-s);background:var(--bg);opacity:0;transition:opacity var(--dur-1)}.md pre:hover .cb-copy{opacity:1}.cb-copy:hover{color:var(--fg)}.rs-item{margin:8px 0}.rs-head{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 8px;border-radius:var(--r-s);color:var(--muted);font-size:var(--fs-s)}.rs-head:hover{background:var(--surface-hover);color:var(--fg)}.rs-head .caret{display:inline-block;transition:transform var(--dur-1)}.rs-head .caret.open{transform:rotate(90deg)}.rs-body{margin:4px 0 4px 14px;padding-left:12px;border-left:2px solid var(--line);color:var(--muted);font-size:var(--fs-s);white-space:pre-wrap}.todo-list{margin:4px 0 4px 14px;padding-left:12px;border-left:2px solid var(--line);list-style:none;font-size:var(--fs-s)}.todo{color:var(--muted)}.todo.in_progress{color:var(--fg)}.tg{margin:8px 0}.tg-head{display:flex;align-items:center;gap:6px;width:100%;height:26px;padding:0 8px;border-radius:var(--r-s);color:var(--muted);font-size:var(--fs-s);text-align:left}.tg-head:hover{background:var(--surface-hover);color:var(--fg)}.tg-head .caret{transition:transform var(--dur-1)}.tg-head .caret.open{transform:rotate(90deg)}.tg-live{margin-left:auto;color:var(--accent)}.tg-list{margin:2px 0 4px 14px;border-left:2px solid var(--line);padding-left:4px}.ts-head{display:flex;align-items:center;gap:8px;width:100%;height:26px;padding:0 8px;border-radius:var(--r-s);font-size:var(--fs-s);text-align:left;color:var(--fg)}.ts-head:hover{background:var(--surface-hover)}.ts-ic{width:14px;flex:none;text-align:center}.ts-ic.ok{color:var(--ok)}.ts-ic.fail{color:var(--danger)}.ts-ic.run{color:var(--accent);animation:breathe 1.2s infinite}.ts-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ts-meta{flex:none;color:var(--muted)}.ts-meta.err{color:var(--danger)}.ts-out{margin:2px 8px 6px 30px}.ts-out pre{background:var(--code-bg);border-radius:var(--r-s);padding:8px 10px;font-family:var(--font-mono);font-size:var(--fs-s);line-height:1.5;overflow-x:auto;white-space:pre-wrap;word-break:break-all;margin:0}.ts-more{font-size:var(--fs-s);color:var(--accent);margin-bottom:4px}.diff{margin:4px 8px 8px 30px;border:1px solid var(--line);border-radius:var(--r-s);overflow:hidden}.diff-path{padding:4px 10px;font-size:var(--fs-s);color:var(--muted);background:var(--surface);border-bottom:1px solid var(--line);font-family:var(--font-mono)}.diff-pre{margin:0;padding:6px 0;font-family:var(--font-mono);font-size:var(--fs-s);line-height:1.5;overflow-x:auto}.dh-head{display:block;color:var(--muted);padding:0 10px}.dl{display:block;padding:0 10px;white-space:pre-wrap;word-break:break-all}.dl.add{background:color-mix(in oklab,var(--ok) 14%,transparent)}.dl.del{background:color-mix(in oklab,var(--danger) 12%,transparent)}.card{margin:10px 0;border-radius:var(--r-m);padding:10px 12px}.card.plan{border:1px solid var(--line)}.card.perm{border:1px solid color-mix(in oklab,var(--warn) 45%,var(--bg))}.card.perm.done{border-color:var(--line);opacity:.75}.card.ask{border:1px solid color-mix(in oklab,var(--info) 45%,var(--bg))}.card.ask.done{border-color:var(--line);opacity:.75}.err-card{border:1px solid color-mix(in oklab,var(--danger) 45%,var(--bg))}.err-line{color:var(--danger);font-size:var(--fs-m)}.perm-title{font-size:var(--fs-m);font-weight:500}.perm-cmd{margin:8px 0 0;background:var(--code-bg);border-radius:var(--r-s);padding:8px 10px;font-family:var(--font-mono);font-size:var(--fs-s);white-space:pre-wrap;word-break:break-all}.perm-path{margin-top:6px;font-family:var(--font-mono);font-size:var(--fs-s);color:var(--muted)}.perm-btns{display:flex;gap:8px;margin-top:10px}.perm-done{margin-top:8px;font-size:var(--fs-s);color:var(--muted)}.ask-q{margin-bottom:10px}.ask-t{font-size:var(--fs-m);font-weight:500;margin-bottom:6px}.ask-opts{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}.ask-free,.ask .btn.primary{margin-top:4px}.nt{margin:8px 0;text-align:center;font-size:var(--fs-s);color:var(--muted)}.nt.warn{color:var(--warn)}.nt.error{color:var(--danger)}.te{display:flex;justify-content:flex-end;align-items:center;gap:8px;margin:10px 0 4px;font-size:var(--fs-s);color:var(--muted)}.te-files{color:var(--accent);font-size:var(--fs-s)}.te-files:hover{text-decoration:underline}.empty-wrap{min-height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;padding-bottom:14vh}.cp-hero-t{font-size:var(--fs-hero);font-weight:600}.cp-hero-s{margin-top:6px;font-size:var(--fs-s);color:var(--muted)}.empty-actions{margin-top:16px}.workspace-empty{padding-bottom:10vh}.q-list{max-width:680px;margin:0 auto 6px;display:flex;flex-direction:column;gap:4px}.q-item{display:flex;align-items:center;gap:8px;padding:4px 10px;background:var(--surface);border-radius:var(--r-s);font-size:var(--fs-s)}.q-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.q-item .ibtn{width:20px;height:20px}.composer{flex:none;padding:0 16px 12px}.cp-card{width:100%;max-width:680px;margin:0 auto;background:var(--surface);border-radius:var(--r-l);border:1px solid transparent;padding:8px 10px 6px;transition:border-color var(--dur-2) var(--ease)}.cp-card:focus-within{border-color:color-mix(in oklab,var(--fg) 18%,var(--bg))}.cp-card.waiting{box-shadow:inset 2px 0 0 var(--warn)}.cp-card.danger{box-shadow:inset 2px 0 0 var(--danger)}.cp-danger{max-width:680px;margin:0 auto 6px;display:flex;align-items:center;gap:6px;color:var(--danger);font-size:var(--fs-s);padding:0 2px}.cp-ta-wrap{position:relative}.slash-pop{position:absolute;left:0;right:0;bottom:100%;margin-bottom:6px;max-height:min(240px,40vh);overflow:hidden;display:flex;flex-direction:column;z-index:20}.slash-pop .ap-scroll{max-height:inherit}.slash-pop .slash-h{padding:4px 10px 2px;font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;letter-spacing:.04em}.slash-pop .mi.slash-item{height:auto;min-height:28px;padding:4px 10px;flex-direction:column;align-items:flex-start;gap:0}.slash-pop .slash-name{font-weight:500}.slash-pop .slash-desc{font-size:var(--fs-xs);color:var(--muted);line-height:1.3}.slash-pop .slash-hint{font-size:var(--fs-xs);color:var(--muted);font-family:var(--mono, monospace)}.slash-pop .mi.slash-item.sel{background:var(--surface-hover)}.cp-ta{width:100%;border:0;resize:none;background:none;outline:none;font-size:var(--fs-m);line-height:1.55;max-height:17em;overflow-y:auto;display:block}.cp-ta::placeholder{color:var(--muted)}.cp-bar{display:flex;align-items:center;flex-wrap:wrap;gap:4px 6px;margin-top:4px;min-width:0}.pill-wrap{position:relative;flex:0 1 auto;min-width:0;max-width:100%}.pill{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 10px;border-radius:var(--r-full);background:var(--surface-hover);color:var(--fg);font-size:var(--fs-s);max-width:min(220px,100%);min-width:0}.pill .p-a{font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.pill .p-t{color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:1 1 auto}.pill.sel{appearance:none;border:0;padding:0 22px 0 10px;cursor:pointer;max-width:min(140px,100%);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;background:var(--surface-hover) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%23737373' d='M1 1l4 4 4-4'/%3E%3C/svg%3E") no-repeat right 8px center}.pill.sel.danger{color:var(--danger)}.pill.cursor-shield{padding:0 8px;color:var(--muted);border:0;cursor:pointer}.pill.cursor-shield.on{color:var(--ok, #3ecf8e);background:color-mix(in oklab,var(--ok, #3ecf8e) 14%,var(--surface-hover))}.pill.sel.cursor-fast{max-width:min(72px,100%)}.cp-bar>.ibtn{flex:none}.cp-card{container-type:inline-size;container-name:composer}@container composer (max-width: 520px){.pill .p-t{display:none}.pill{max-width:min(160px,100%)}.pill.sel{max-width:min(104px,100%);font-size:11px;padding-right:20px}}@container composer (max-width: 380px){.pill{max-width:min(120px,100%);padding:0 8px}.pill.sel{max-width:min(80px,100%);padding-left:7px;padding-right:18px}}.pop{position:absolute;bottom:32px;left:0;z-index:55;width:260px;background:var(--bg);border-radius:var(--r-m);box-shadow:var(--shadow-pop);padding:4px;max-height:60vh;overflow-y:auto}.agent-picker{width:min(300px,calc(100vw - 32px));padding:0;overflow:hidden;display:flex;flex-direction:column;max-height:min(420px,60vh)}.agent-picker .ap-search{display:flex;align-items:center;gap:8px;padding:8px 10px;border-bottom:1px solid var(--line);flex:none;color:var(--muted)}.agent-picker .ap-search-in{flex:1;min-width:0;border:0;background:none;outline:none;font-size:var(--fs-s);color:var(--fg)}.agent-picker .ap-search-in::placeholder{color:var(--muted)}.agent-picker .ap-scroll{overflow-y:auto;padding:4px;flex:1;min-height:0}.agent-picker .ap-empty{padding:12px 10px;font-size:var(--fs-s);color:var(--muted);text-align:center}.agent-picker .ap-back{font-weight:600;margin-bottom:2px}.agent-picker .ap-back-icon{display:inline-flex;transform:rotate(180deg);margin-right:2px}.agent-picker .mi-row{display:flex;align-items:center;gap:6px;width:100%;text-align:left}.agent-picker .provider-pick-row{justify-content:space-between;gap:8px}.agent-picker .provider-pick-name{flex:0 1 auto;min-width:5.5rem;max-width:58%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500;color:inherit}.agent-picker .provider-pick-meta{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:flex-end;gap:6px;min-width:0;margin-left:auto}.agent-picker .provider-pick-meta .dlg-hint{flex:none;font-size:10px;max-width:5rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.agent-picker .mi-row .mi-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.agent-picker .mi-row .mi-sub{flex:none;max-width:38%;font-size:11px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.agent-picker .model-pick-row{min-width:0}.agent-picker .model-pick-name{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.agent-picker .model-pick-default{flex:0 1 58%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.agent-picker .mi-meta{flex:none;font-size:11px;color:var(--muted);white-space:nowrap}.agent-picker .mi-tag{flex:none;font-size:10px;color:var(--accent)}.agent-picker .mi-caret{flex:none;color:var(--muted);display:inline-flex}.agent-picker .mi-check{flex:none;margin-left:auto;color:var(--accent)}.ap-sec{padding:4px 0}.ap-sec .tinput{height:26px;margin-bottom:4px}.ap-sec .mi{height:26px;font-size:var(--fs-s)}.att-bar{display:flex;flex-wrap:wrap;gap:6px;padding:0 0 6px}.att{display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 8px;background:var(--surface-hover);border-radius:var(--r-s);font-size:var(--fs-s)}.att img{width:22px;height:22px;object-fit:cover;border-radius:4px}.att.up{opacity:.6}.att-name{max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.att-x{color:var(--muted);font-size:14px;line-height:1}.att-x:hover{color:var(--fg)}.cp-right{margin-left:auto;flex:none;display:flex;align-items:center;gap:8px}.cp-clock{font-size:var(--fs-s);color:var(--muted)}.cp-send,.cp-stop{width:28px;height:28px;border-radius:50%;flex:none;display:inline-flex;align-items:center;justify-content:center;transition:filter var(--dur-1),opacity var(--dur-1)}.cp-send{background:var(--accent);color:var(--accent-fg)}.cp-send:hover{filter:brightness(1.08)}.cp-send[disabled]{opacity:.3;cursor:default;filter:none}.cp-stop{background:var(--fg);color:var(--bg)}.cp-stop:hover{filter:brightness(1.15)}@media(max-width:560px){.composer{padding:0 10px 10px}}.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;inset:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;inset:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}
2
+ /*! Bundled license information:
3
+
4
+ @xterm/xterm/css/xterm.css:
5
+ (**
6
+ * Copyright (c) 2014 The xterm.js authors. All rights reserved.
7
+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
8
+ * https://github.com/chjj/term.js
9
+ * @license MIT
10
+ *
11
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ * of this software and associated documentation files (the "Software"), to deal
13
+ * in the Software without restriction, including without limitation the rights
14
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ * copies of the Software, and to permit persons to whom the Software is
16
+ * furnished to do so, subject to the following conditions:
17
+ *
18
+ * The above copyright notice and this permission notice shall be included in
19
+ * all copies or substantial portions of the Software.
20
+ *
21
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
+ * THE SOFTWARE.
28
+ *
29
+ * Originally forked from (with the author's permission):
30
+ * Fabrice Bellard's javascript vt100 for jslinux:
31
+ * http://bellard.org/jslinux/
32
+ * Copyright (c) 2011 Fabrice Bellard
33
+ * The original design remains. The terminal itself
34
+ * has been extended to include xterm CSI codes, among
35
+ * other features.
36
+ *)
37
+ */